github.com/khulnasoft-lab/defsec@v1.0.5-0.20230827010352-5e9f46893d95/pkg/scanners/terraform/parser/funcs/crypto.go (about)

     1  // Copied from github.com/hashicorp/terraform/internal/lang/funcs
     2  package funcs
     3  
     4  import (
     5  	"crypto/md5"
     6  	"crypto/rsa"
     7  	"crypto/sha1"
     8  	"crypto/sha256"
     9  	"crypto/sha512"
    10  	"encoding/asn1"
    11  	"encoding/base64"
    12  	"encoding/hex"
    13  	"fmt"
    14  	"hash"
    15  	"io"
    16  	"io/fs"
    17  	"strings"
    18  
    19  	uuidv5 "github.com/google/uuid"
    20  	uuid "github.com/hashicorp/go-uuid"
    21  	"github.com/zclconf/go-cty/cty"
    22  	"github.com/zclconf/go-cty/cty/function"
    23  	"github.com/zclconf/go-cty/cty/gocty"
    24  	"golang.org/x/crypto/bcrypt"
    25  	"golang.org/x/crypto/ssh"
    26  )
    27  
    28  var UUIDFunc = function.New(&function.Spec{
    29  	Params: []function.Parameter{},
    30  	Type:   function.StaticReturnType(cty.String),
    31  	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
    32  		result, err := uuid.GenerateUUID()
    33  		if err != nil {
    34  			return cty.UnknownVal(cty.String), err
    35  		}
    36  		return cty.StringVal(result), nil
    37  	},
    38  })
    39  
    40  var UUIDV5Func = function.New(&function.Spec{
    41  	Params: []function.Parameter{
    42  		{
    43  			Name: "namespace",
    44  			Type: cty.String,
    45  		},
    46  		{
    47  			Name: "name",
    48  			Type: cty.String,
    49  		},
    50  	},
    51  	Type: function.StaticReturnType(cty.String),
    52  	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
    53  		var namespace uuidv5.UUID
    54  		switch {
    55  		case args[0].AsString() == "dns":
    56  			namespace = uuidv5.NameSpaceDNS
    57  		case args[0].AsString() == "url":
    58  			namespace = uuidv5.NameSpaceURL
    59  		case args[0].AsString() == "oid":
    60  			namespace = uuidv5.NameSpaceOID
    61  		case args[0].AsString() == "x500":
    62  			namespace = uuidv5.NameSpaceX500
    63  		default:
    64  			if namespace, err = uuidv5.Parse(args[0].AsString()); err != nil {
    65  				return cty.UnknownVal(cty.String), fmt.Errorf("uuidv5() doesn't support namespace %s (%v)", args[0].AsString(), err)
    66  			}
    67  		}
    68  		val := args[1].AsString()
    69  		return cty.StringVal(uuidv5.NewSHA1(namespace, []byte(val)).String()), nil
    70  	},
    71  })
    72  
    73  // Base64Sha256Func constructs a function that computes the SHA256 hash of a given string
    74  // and encodes it with Base64.
    75  var Base64Sha256Func = makeStringHashFunction(sha256.New, base64.StdEncoding.EncodeToString)
    76  
    77  // MakeFileBase64Sha256Func constructs a function that is like Base64Sha256Func but reads the
    78  // contents of a file rather than hashing a given literal string.
    79  func MakeFileBase64Sha256Func(target fs.FS, baseDir string) function.Function {
    80  	return makeFileHashFunction(target, baseDir, sha256.New, base64.StdEncoding.EncodeToString)
    81  }
    82  
    83  // Base64Sha512Func constructs a function that computes the SHA256 hash of a given string
    84  // and encodes it with Base64.
    85  var Base64Sha512Func = makeStringHashFunction(sha512.New, base64.StdEncoding.EncodeToString)
    86  
    87  // MakeFileBase64Sha512Func constructs a function that is like Base64Sha512Func but reads the
    88  // contents of a file rather than hashing a given literal string.
    89  func MakeFileBase64Sha512Func(target fs.FS, baseDir string) function.Function {
    90  	return makeFileHashFunction(target, baseDir, sha512.New, base64.StdEncoding.EncodeToString)
    91  }
    92  
    93  // BcryptFunc constructs a function that computes a hash of the given string using the Blowfish cipher.
    94  var BcryptFunc = function.New(&function.Spec{
    95  	Params: []function.Parameter{
    96  		{
    97  			Name: "str",
    98  			Type: cty.String,
    99  		},
   100  	},
   101  	VarParam: &function.Parameter{
   102  		Name: "cost",
   103  		Type: cty.Number,
   104  	},
   105  	Type: function.StaticReturnType(cty.String),
   106  	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
   107  		defaultCost := 10
   108  
   109  		if len(args) > 1 {
   110  			var val int
   111  			if err := gocty.FromCtyValue(args[1], &val); err != nil {
   112  				return cty.UnknownVal(cty.String), err
   113  			}
   114  			defaultCost = val
   115  		}
   116  
   117  		if len(args) > 2 {
   118  			return cty.UnknownVal(cty.String), fmt.Errorf("bcrypt() takes no more than two arguments")
   119  		}
   120  
   121  		input := args[0].AsString()
   122  		out, err := bcrypt.GenerateFromPassword([]byte(input), defaultCost)
   123  		if err != nil {
   124  			return cty.UnknownVal(cty.String), fmt.Errorf("error occurred generating password %s", err.Error())
   125  		}
   126  
   127  		return cty.StringVal(string(out)), nil
   128  	},
   129  })
   130  
   131  // Md5Func constructs a function that computes the MD5 hash of a given string and encodes it with hexadecimal digits.
   132  var Md5Func = makeStringHashFunction(md5.New, hex.EncodeToString)
   133  
   134  // MakeFileMd5Func constructs a function that is like Md5Func but reads the
   135  // contents of a file rather than hashing a given literal string.
   136  func MakeFileMd5Func(target fs.FS, baseDir string) function.Function {
   137  	return makeFileHashFunction(target, baseDir, md5.New, hex.EncodeToString)
   138  }
   139  
   140  // RsaDecryptFunc constructs a function that decrypts an RSA-encrypted ciphertext.
   141  var RsaDecryptFunc = function.New(&function.Spec{
   142  	Params: []function.Parameter{
   143  		{
   144  			Name: "ciphertext",
   145  			Type: cty.String,
   146  		},
   147  		{
   148  			Name: "privatekey",
   149  			Type: cty.String,
   150  		},
   151  	},
   152  	Type: function.StaticReturnType(cty.String),
   153  	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
   154  		s := args[0].AsString()
   155  		key := args[1].AsString()
   156  
   157  		b, err := base64.StdEncoding.DecodeString(s)
   158  		if err != nil {
   159  			return cty.UnknownVal(cty.String), function.NewArgErrorf(0, "failed to decode input %q: cipher text must be base64-encoded", s)
   160  		}
   161  
   162  		rawKey, err := ssh.ParseRawPrivateKey([]byte(key))
   163  		if err != nil {
   164  			var errStr string
   165  			switch e := err.(type) {
   166  			case asn1.SyntaxError:
   167  				errStr = strings.ReplaceAll(e.Error(), "asn1: syntax error", "invalid ASN1 data in the given private key")
   168  			case asn1.StructuralError:
   169  				errStr = strings.ReplaceAll(e.Error(), "asn1: structure error", "invalid ASN1 data in the given private key")
   170  			default:
   171  				errStr = fmt.Sprintf("invalid private key: %s", e)
   172  			}
   173  			return cty.UnknownVal(cty.String), function.NewArgErrorf(1, errStr)
   174  		}
   175  		privateKey, ok := rawKey.(*rsa.PrivateKey)
   176  		if !ok {
   177  			return cty.UnknownVal(cty.String), function.NewArgErrorf(1, "invalid private key type %t", rawKey)
   178  		}
   179  
   180  		out, err := rsa.DecryptPKCS1v15(nil, privateKey, b)
   181  		if err != nil {
   182  			return cty.UnknownVal(cty.String), fmt.Errorf("failed to decrypt: %s", err)
   183  		}
   184  
   185  		return cty.StringVal(string(out)), nil
   186  	},
   187  })
   188  
   189  // Sha1Func constructs a function that computes the SHA1 hash of a given string
   190  // and encodes it with hexadecimal digits.
   191  var Sha1Func = makeStringHashFunction(sha1.New, hex.EncodeToString)
   192  
   193  // MakeFileSha1Func constructs a function that is like Sha1Func but reads the
   194  // contents of a file rather than hashing a given literal string.
   195  func MakeFileSha1Func(target fs.FS, baseDir string) function.Function {
   196  	return makeFileHashFunction(target, baseDir, sha1.New, hex.EncodeToString)
   197  }
   198  
   199  // Sha256Func constructs a function that computes the SHA256 hash of a given string
   200  // and encodes it with hexadecimal digits.
   201  var Sha256Func = makeStringHashFunction(sha256.New, hex.EncodeToString)
   202  
   203  // MakeFileSha256Func constructs a function that is like Sha256Func but reads the
   204  // contents of a file rather than hashing a given literal string.
   205  func MakeFileSha256Func(target fs.FS, baseDir string) function.Function {
   206  	return makeFileHashFunction(target, baseDir, sha256.New, hex.EncodeToString)
   207  }
   208  
   209  // Sha512Func constructs a function that computes the SHA512 hash of a given string
   210  // and encodes it with hexadecimal digits.
   211  var Sha512Func = makeStringHashFunction(sha512.New, hex.EncodeToString)
   212  
   213  // MakeFileSha512Func constructs a function that is like Sha512Func but reads the
   214  // contents of a file rather than hashing a given literal string.
   215  func MakeFileSha512Func(target fs.FS, baseDir string) function.Function {
   216  	return makeFileHashFunction(target, baseDir, sha512.New, hex.EncodeToString)
   217  }
   218  
   219  func makeStringHashFunction(hf func() hash.Hash, enc func([]byte) string) function.Function {
   220  	return function.New(&function.Spec{
   221  		Params: []function.Parameter{
   222  			{
   223  				Name: "str",
   224  				Type: cty.String,
   225  			},
   226  		},
   227  		Type: function.StaticReturnType(cty.String),
   228  		Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
   229  			s := args[0].AsString()
   230  			h := hf()
   231  			h.Write([]byte(s))
   232  			rv := enc(h.Sum(nil))
   233  			return cty.StringVal(rv), nil
   234  		},
   235  	})
   236  }
   237  
   238  func makeFileHashFunction(target fs.FS, baseDir string, hf func() hash.Hash, enc func([]byte) string) function.Function {
   239  	return function.New(&function.Spec{
   240  		Params: []function.Parameter{
   241  			{
   242  				Name: "path",
   243  				Type: cty.String,
   244  			},
   245  		},
   246  		Type: function.StaticReturnType(cty.String),
   247  		Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
   248  			path := args[0].AsString()
   249  			f, err := openFile(target, baseDir, path)
   250  			if err != nil {
   251  				return cty.UnknownVal(cty.String), err
   252  			}
   253  
   254  			h := hf()
   255  			_, err = io.Copy(h, f)
   256  			if err != nil {
   257  				return cty.UnknownVal(cty.String), err
   258  			}
   259  			rv := enc(h.Sum(nil))
   260  			return cty.StringVal(rv), nil
   261  		},
   262  	})
   263  }
   264  
   265  // UUID generates and returns a Type-4 UUID in the standard hexadecimal string
   266  // format.
   267  //
   268  // This is not a pure function: it will generate a different result for each
   269  // call. It must therefore be registered as an impure function in the function
   270  // table in the "lang" package.
   271  func UUID() (cty.Value, error) {
   272  	return UUIDFunc.Call(nil)
   273  }
   274  
   275  // UUIDV5 generates and returns a Type-5 UUID in the standard hexadecimal string
   276  // format.
   277  func UUIDV5(namespace cty.Value, name cty.Value) (cty.Value, error) {
   278  	return UUIDV5Func.Call([]cty.Value{namespace, name})
   279  }
   280  
   281  // Base64Sha256 computes the SHA256 hash of a given string and encodes it with
   282  // Base64.
   283  //
   284  // The given string is first encoded as UTF-8 and then the SHA256 algorithm is applied
   285  // as defined in RFC 4634. The raw hash is then encoded with Base64 before returning.
   286  // Terraform uses the "standard" Base64 alphabet as defined in RFC 4648 section 4.
   287  func Base64Sha256(str cty.Value) (cty.Value, error) {
   288  	return Base64Sha256Func.Call([]cty.Value{str})
   289  }
   290  
   291  // Base64Sha512 computes the SHA512 hash of a given string and encodes it with
   292  // Base64.
   293  //
   294  // The given string is first encoded as UTF-8 and then the SHA256 algorithm is applied
   295  // as defined in RFC 4634. The raw hash is then encoded with Base64 before returning.
   296  // Terraform uses the "standard" Base64  alphabet as defined in RFC 4648 section 4
   297  func Base64Sha512(str cty.Value) (cty.Value, error) {
   298  	return Base64Sha512Func.Call([]cty.Value{str})
   299  }
   300  
   301  // Bcrypt computes a hash of the given string using the Blowfish cipher,
   302  // returning a string in the Modular Crypt Format
   303  // usually expected in the shadow password file on many Unix systems.
   304  func Bcrypt(str cty.Value, cost ...cty.Value) (cty.Value, error) {
   305  	args := make([]cty.Value, len(cost)+1)
   306  	args[0] = str
   307  	copy(args[1:], cost)
   308  	return BcryptFunc.Call(args)
   309  }
   310  
   311  // Md5 computes the MD5 hash of a given string and encodes it with hexadecimal digits.
   312  func Md5(str cty.Value) (cty.Value, error) {
   313  	return Md5Func.Call([]cty.Value{str})
   314  }
   315  
   316  // RsaDecrypt decrypts an RSA-encrypted ciphertext, returning the corresponding
   317  // cleartext.
   318  func RsaDecrypt(ciphertext, privatekey cty.Value) (cty.Value, error) {
   319  	return RsaDecryptFunc.Call([]cty.Value{ciphertext, privatekey})
   320  }
   321  
   322  // Sha1 computes the SHA1 hash of a given string and encodes it with hexadecimal digits.
   323  func Sha1(str cty.Value) (cty.Value, error) {
   324  	return Sha1Func.Call([]cty.Value{str})
   325  }
   326  
   327  // Sha256 computes the SHA256 hash of a given string and encodes it with hexadecimal digits.
   328  func Sha256(str cty.Value) (cty.Value, error) {
   329  	return Sha256Func.Call([]cty.Value{str})
   330  }
   331  
   332  // Sha512 computes the SHA512 hash of a given string and encodes it with hexadecimal digits.
   333  func Sha512(str cty.Value) (cty.Value, error) {
   334  	return Sha512Func.Call([]cty.Value{str})
   335  }