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 }