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