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  }