cmd/sign_string.go (172 lines of code) (raw):

package cmd import ( "bufio" "crypto" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/rsa" "crypto/sha256" "crypto/x509" "encoding/binary" "encoding/hex" "encoding/json" "fmt" "io/ioutil" "log" "os" "strconv" "strings" helper "github.com/aws/rolesanywhere-credential-helper/aws_signing_helper" "github.com/spf13/cobra" ) var ( format *enum digestArg *enum ) var ( SIGN_STRING_TEST_VERSION uint16 = 1 signFixedString bool = true ) type enum struct { Allowed []string Value string } func newEnum(allowed []string, d string) *enum { return &enum{ Allowed: allowed, Value: d, } } func (e enum) String() string { return e.Value } func (a *enum) Set(p string) error { isIncluded := func(opts []string, val string) bool { for _, opt := range opts { if val == opt { return true } } return false } if !isIncluded(a.Allowed, p) { return fmt.Errorf("%s is not included in %s", p, strings.Join(a.Allowed, ",")) } a.Value = p return nil } func (a *enum) Type() string { return "string" } func init() { rootCmd.AddCommand(signStringCmd) format = newEnum([]string{"json", "text", "bin"}, "json") digestArg = newEnum([]string{"SHA256", "SHA384", "SHA512"}, "SHA256") signStringCmd.PersistentFlags().StringVar(&certificateId, "certificate", "", "PKCS#11 URI to identify the certificate") signStringCmd.PersistentFlags().StringVar(&privateKeyId, "private-key", "", "Path to private key file or PKCS#11 URI to identify the private key") signStringCmd.PersistentFlags().BoolVar(&debug, "debug", false, "To print debug output") signStringCmd.PersistentFlags().StringVar(&certSelector, "cert-selector", "", "JSON structure to identify a certificate from a certificate store. "+ "Can be passed in either as string or a file name (prefixed by \"file://\")") signStringCmd.PersistentFlags().StringVar(&systemStoreName, "system-store-name", "MY", "Name of the system store to search for within the "+ "CERT_SYSTEM_STORE_CURRENT_USER context. Note that this flag is only relevant for Windows certificate stores and will be ignored otherwise") signStringCmd.PersistentFlags().BoolVar(&useLatestExpiringCertificate, "use-latest-expiring-certificate", false, "If multiple certificates match "+ "a given certificate selector, the one that expires the latest will be chosen (if more than one still fits this criteria, an arbitrary "+ "one is chosen from those that meet the criteria)") signStringCmd.PersistentFlags().StringVar(&libPkcs11, "pkcs11-lib", "", "Library for smart card / cryptographic device (default: p11-kit-proxy.{so, dll, dylib})") signStringCmd.PersistentFlags().BoolVar(&reusePin, "reuse-pin", false, "Use the CKU_USER PIN as the CKU_CONTEXT_SPECIFIC PIN for "+ "private key objects, when they are first used to sign. If the CKU_USER PIN doesn't work as the CKU_CONTEXT_SPECIFIC PIN "+ "for a given private key object, fall back to prompting the user") signStringCmd.PersistentFlags().StringVar(&tpmKeyPassword, "tpm-key-password", "", "Password for TPM key, if applicable") signStringCmd.PersistentFlags().BoolVar(&noTpmKeyPassword, "no-tpm-key-password", false, "Required if the TPM key has no password and"+ "a handle is used to refer to the key") signStringCmd.PersistentFlags().Var(format, "format", "Output format. One of json, text, and bin") signStringCmd.PersistentFlags().Var(digestArg, "digest", "One of SHA256, SHA384, and SHA512") signStringCmd.MarkFlagsMutuallyExclusive("certificate", "cert-selector") signStringCmd.MarkFlagsMutuallyExclusive("certificate", "system-store-name") signStringCmd.MarkFlagsMutuallyExclusive("private-key", "cert-selector") signStringCmd.MarkFlagsMutuallyExclusive("private-key", "system-store-name") signStringCmd.MarkFlagsMutuallyExclusive("private-key", "use-latest-expiring-certificate") signStringCmd.MarkFlagsMutuallyExclusive("use-latest-expiring-certificate", "reuse-pin") signStringCmd.MarkFlagsMutuallyExclusive("cert-selector", "reuse-pin") signStringCmd.MarkFlagsMutuallyExclusive("system-store-name", "reuse-pin") signStringCmd.MarkFlagsMutuallyExclusive("tpm-key-password", "cert-selector") signStringCmd.MarkFlagsMutuallyExclusive("tpm-key-password", "reuse-pin") signStringCmd.MarkFlagsMutuallyExclusive("no-tpm-key-password", "cert-selector") signStringCmd.MarkFlagsMutuallyExclusive("no-tpm-key-password", "reuse-pin") signStringCmd.MarkFlagsMutuallyExclusive("no-tpm-key-password", "tpm-key-password") } func getFixedStringToSign(publicKey crypto.PublicKey) string { var digestSuffix []byte ecdsaPublicKey, isEcKey := publicKey.(*ecdsa.PublicKey) if isEcKey { digestSuffixArr := sha256.Sum256(append([]byte("IAM RA"), elliptic.Marshal(ecdsaPublicKey, ecdsaPublicKey.X, ecdsaPublicKey.Y)...)) digestSuffix = digestSuffixArr[:] } rsaPublicKey, isRsaKey := publicKey.(*rsa.PublicKey) if isRsaKey { digestSuffixArr := sha256.Sum256(append([]byte("IAM RA"), x509.MarshalPKCS1PublicKey(rsaPublicKey)...)) digestSuffix = digestSuffixArr[:] } // "AWS Roles Anywhere Credential Helper Signing Test" || SIGN_STRING_TEST_VERSION || // SHA256("IAM RA" || PUBLIC_KEY_BYTE_ARRAY) fixedStringToSign := "AWS Roles Anywhere Credential Helper Signing Test" + strconv.Itoa(int(SIGN_STRING_TEST_VERSION)) + string(digestSuffix) return fixedStringToSign } var signStringCmd = &cobra.Command{ Use: "sign-string [flags]", Short: "Signs a fixed string using the passed-in private key (or reference to private key)", Run: func(cmd *cobra.Command, args []string) { var digest crypto.Hash switch strings.ToUpper(digestArg.String()) { case "SHA256": digest = crypto.SHA256 case "SHA384": digest = crypto.SHA384 case "SHA512": digest = crypto.SHA512 default: digest = crypto.SHA256 } err := PopulateCredentialsOptions() if err != nil { log.Println(err) os.Exit(1) } helper.Debug = credentialsOptions.Debug var signer helper.Signer signer, _, err = helper.GetSigner(&credentialsOptions) if err != nil { log.Println(err) os.Exit(1) } defer signer.Close() var stringToSignBytes []byte if signFixedString { stringToSign := getFixedStringToSign(signer.Public()) stringToSignBytes = []byte(stringToSign) if credentialsOptions.Debug { log.Println("Signing fixed string of the form: \"AWS Roles Anywhere " + "Credential Helper Signing Test\" || SIGN_STRING_TEST_VERSION || SHA256(\"IAM RA\" || PUBLIC_KEY_BYTE_ARRAY)\"") } } else { stringToSignBytes, _ = ioutil.ReadAll(bufio.NewReader(os.Stdin)) } sigBytes, err := signer.Sign(rand.Reader, stringToSignBytes, digest) if err != nil { log.Println("unable to sign the digest:", err) os.Exit(1) } sigStr := hex.EncodeToString(sigBytes) switch strings.ToLower(format.String()) { case "text": fmt.Print(sigStr) case "json": buf, _ := json.Marshal(sigStr) fmt.Print(string(buf[:])) case "bin": binary.Write(os.Stdout, binary.BigEndian, sigBytes[:]) default: fmt.Print(sigStr) } }, }