Skip to content

Commit

Permalink
Change "force-prompt" option to "reuse-pin"
Browse files Browse the repository at this point in the history
 * Make the "try user PIN as the context-specific PIN" behavior opt-in,
   reducing the risk of lockout.

 * Modify unit tests to set the reuse PIN option, so that prompting
   doesn't occur.
  • Loading branch information
13ajay committed Sep 8, 2023
1 parent be61bf7 commit 3f52117
Show file tree
Hide file tree
Showing 6 changed files with 33 additions and 26 deletions.
2 changes: 1 addition & 1 deletion aws_signing_helper/credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ type CredentialsOpts struct {
Debug bool
Version string
LibPkcs11 string
ForcePrompt bool
ReusePin bool
}

// Function to create session and generate credentials
Expand Down
30 changes: 15 additions & 15 deletions aws_signing_helper/pkcs11_signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ type PKCS11Signer struct {
contextSpecificPin string
certUri *pkcs11uri.Pkcs11URI
keyUri *pkcs11uri.Pkcs11URI
forcePrompt bool
reusePin bool
}

// Initialize a PKCS#11 module.
Expand Down Expand Up @@ -600,7 +600,7 @@ func GetPassword(ttyReadFile *os.File, ttyWriteFile *os.File, prompt string, par
}

// Helper function to sign a digest using a PKCS#11 private key handle.
func signHelper(module *pkcs11.Ctx, session pkcs11.SessionHandle, privateKeyObj KeyObjInfo, slot SlotIdInfo, userPin string, alwaysAuth uint, contextSpecificPin string, forcePrompt bool, keyType uint, digest []byte, hashFunc crypto.Hash) (_contextSpecificPin string, signature []byte, err error) {
func signHelper(module *pkcs11.Ctx, session pkcs11.SessionHandle, privateKeyObj KeyObjInfo, slot SlotIdInfo, userPin string, alwaysAuth uint, contextSpecificPin string, reusePin bool, keyType uint, digest []byte, hashFunc crypto.Hash) (_contextSpecificPin string, signature []byte, err error) {
// XXX: If you use this outside the context of IAM RA, be aware that
// you'll want to use something other than SHA256 in many cases.
// For TLSv1.3 the hash needs to precisely match the bit size of the
Expand Down Expand Up @@ -649,9 +649,9 @@ func signHelper(module *pkcs11.Ctx, session pkcs11.SessionHandle, privateKeyObj
if alwaysAuth != 0 {
// Set the value for the context-specific PIN used to do the signing
// operation with this key. If the context-specific PIN wasn't specified
// in the input, and the "force prompt" option wasn't set, try to use the
// in the input, and the "reuse PIN" option was set, try to use the
// user PIN as the context-specific PIN.
if contextSpecificPin == "" && userPin != "" && !forcePrompt {
if contextSpecificPin == "" && userPin != "" && reusePin {
contextSpecificPin = userPin
}
if contextSpecificPin != "" {
Expand Down Expand Up @@ -714,7 +714,7 @@ afterContextSpecificLogin:

// Gets a handle to the private key object (along with some other information
// that may need to be saved).
func getPKCS11Key(module *pkcs11.Ctx, session pkcs11.SessionHandle, loggedIn bool, certUri *pkcs11uri.Pkcs11URI, keyUri *pkcs11uri.Pkcs11URI, noKeyUri bool, certSlotNr uint, certObj CertObjInfo, userPin string, contextSpecificPin string, forcePrompt bool, slots []SlotIdInfo) (_session pkcs11.SessionHandle, _userPin string, _keyUri *pkcs11uri.Pkcs11URI, keyType uint, privateKeyObj KeyObjInfo, slot SlotIdInfo, alwaysAuth uint, _contextSpecificPin string, err error) {
func getPKCS11Key(module *pkcs11.Ctx, session pkcs11.SessionHandle, loggedIn bool, certUri *pkcs11uri.Pkcs11URI, keyUri *pkcs11uri.Pkcs11URI, noKeyUri bool, certSlotNr uint, certObj CertObjInfo, userPin string, contextSpecificPin string, reusePin bool, slots []SlotIdInfo) (_session pkcs11.SessionHandle, _userPin string, _keyUri *pkcs11uri.Pkcs11URI, keyType uint, privateKeyObj KeyObjInfo, slot SlotIdInfo, alwaysAuth uint, _contextSpecificPin string, err error) {
var (
keySlot SlotIdInfo
manufacturerId string
Expand Down Expand Up @@ -872,7 +872,7 @@ retry_search:
curContextSpecificPin = userPin
}
privateKeyMatchesCert := false
curContextSpecificPin, privateKeyMatchesCert = checkPrivateKeyMatchesCert(module, session, keyType, userPin, alwaysAuth, "", forcePrompt, curPrivateKeyObj, keySlot, certObj.cert, manufacturerId)
curContextSpecificPin, privateKeyMatchesCert = checkPrivateKeyMatchesCert(module, session, keyType, userPin, alwaysAuth, "", reusePin, curPrivateKeyObj, keySlot, certObj.cert, manufacturerId)
if privateKeyMatchesCert {
privateKeyObj = curPrivateKeyObj
contextSpecificPin = curContextSpecificPin
Expand Down Expand Up @@ -961,7 +961,7 @@ func (pkcs11Signer *PKCS11Signer) Sign(rand io.Reader, digest []byte, opts crypt
certObj CertObjInfo
slots []SlotIdInfo
loggedIn bool
forcePrompt bool
reusePin bool
alwaysAuth uint
certSlot SlotIdInfo
)
Expand All @@ -974,7 +974,7 @@ func (pkcs11Signer *PKCS11Signer) Sign(rand io.Reader, digest []byte, opts crypt
contextSpecificPin = pkcs11Signer.contextSpecificPin
certUri = pkcs11Signer.certUri
keyUri = pkcs11Signer.keyUri
forcePrompt = pkcs11Signer.forcePrompt
reusePin = pkcs11Signer.reusePin

// If a PKCS#11 URI was provided for the certificate, use it.
if certUri != nil {
Expand All @@ -993,12 +993,12 @@ func (pkcs11Signer *PKCS11Signer) Sign(rand io.Reader, digest []byte, opts crypt
}
}

session, userPin, keyUri, keyType, privateKeyObj, keySlot, alwaysAuth, contextSpecificPin, err = getPKCS11Key(module, session, loggedIn, certUri, keyUri, false, certSlotNr, certObj, userPin, contextSpecificPin, forcePrompt, slots)
session, userPin, keyUri, keyType, privateKeyObj, keySlot, alwaysAuth, contextSpecificPin, err = getPKCS11Key(module, session, loggedIn, certUri, keyUri, false, certSlotNr, certObj, userPin, contextSpecificPin, reusePin, slots)
if err != nil {
goto cleanUp
}

contextSpecificPin, signature, err = signHelper(module, session, privateKeyObj, keySlot, userPin, alwaysAuth, contextSpecificPin, forcePrompt, keyType, digest, hashFunc)
contextSpecificPin, signature, err = signHelper(module, session, privateKeyObj, keySlot, userPin, alwaysAuth, contextSpecificPin, reusePin, keyType, digest, hashFunc)
if err != nil {
goto cleanUp
} else {
Expand Down Expand Up @@ -1098,7 +1098,7 @@ func (pkcs11Signer *PKCS11Signer) CertificateChain() (certChain []*x509.Certific
}

// Checks whether the private key and certificate are associated with each other.
func checkPrivateKeyMatchesCert(module *pkcs11.Ctx, session pkcs11.SessionHandle, keyType uint, userPin string, alwaysAuth uint, contextSpecificPin string, forcePrompt bool, privateKeyObj KeyObjInfo, keySlot SlotIdInfo, certificate *x509.Certificate, manufacturerId string) (string, bool) {
func checkPrivateKeyMatchesCert(module *pkcs11.Ctx, session pkcs11.SessionHandle, keyType uint, userPin string, alwaysAuth uint, contextSpecificPin string, reusePin bool, privateKeyObj KeyObjInfo, keySlot SlotIdInfo, certificate *x509.Certificate, manufacturerId string) (string, bool) {
var digestSuffix []byte
publicKey := certificate.PublicKey
ecdsaPublicKey, isEcKey := publicKey.(*ecdsa.PublicKey)
Expand All @@ -1125,7 +1125,7 @@ func checkPrivateKeyMatchesCert(module *pkcs11.Ctx, session pkcs11.SessionHandle
digestBytes := []byte(digest)
hash := sha256.Sum256(digestBytes)

contextSpecificPin, signature, err := signHelper(module, session, privateKeyObj, keySlot, userPin, alwaysAuth, "", forcePrompt, keyType, digestBytes, crypto.SHA256)
contextSpecificPin, signature, err := signHelper(module, session, privateKeyObj, keySlot, userPin, alwaysAuth, "", reusePin, keyType, digestBytes, crypto.SHA256)
if err != nil {
return "", false
}
Expand Down Expand Up @@ -1193,7 +1193,7 @@ func escapeAll(s []byte) string {
// already found in a file) or as a PKCS#11 URI, and an optional private key
// PKCS#11 URI, return a PKCS11Signer that can be used to sign a payload
// through a PKCS#11-compatible cryptographic device.
func GetPKCS11Signer(libPkcs11 string, cert *x509.Certificate, certChain []*x509.Certificate, privateKeyId string, certificateId string, forcePrompt bool) (signer Signer, signingAlgorithm string, err error) {
func GetPKCS11Signer(libPkcs11 string, cert *x509.Certificate, certChain []*x509.Certificate, privateKeyId string, certificateId string, reusePin bool) (signer Signer, signingAlgorithm string, err error) {
var (
module *pkcs11.Ctx
certObj CertObjInfo
Expand Down Expand Up @@ -1285,7 +1285,7 @@ func GetPKCS11Signer(libPkcs11 string, cert *x509.Certificate, certChain []*x509
}
}

session, userPin, keyUri, keyType, _, _, alwaysAuth, contextSpecificPin, err = getPKCS11Key(module, session, loggedIn, certUri, keyUri, noKeyUri, certSlotNr, certObj, userPin, "", forcePrompt, slots)
session, userPin, keyUri, keyType, _, _, alwaysAuth, contextSpecificPin, err = getPKCS11Key(module, session, loggedIn, certUri, keyUri, noKeyUri, certSlotNr, certObj, userPin, "", reusePin, slots)
if err != nil {
goto fail
}
Expand All @@ -1306,7 +1306,7 @@ func GetPKCS11Signer(libPkcs11 string, cert *x509.Certificate, certChain []*x509
module.CloseSession(session)
}

return &PKCS11Signer{cert, certChain, module, userPin, alwaysAuth, contextSpecificPin, certUri, keyUri, forcePrompt}, signingAlgorithm, nil
return &PKCS11Signer{cert, certChain, module, userPin, alwaysAuth, contextSpecificPin, certUri, keyUri, reusePin}, signingAlgorithm, nil

fail:
if module != nil {
Expand Down
2 changes: 1 addition & 1 deletion aws_signing_helper/signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ func GetSigner(opts *CredentialsOpts) (signer Signer, signatureAlgorithm string,
if certificate != nil {
opts.CertificateId = ""
}
return GetPKCS11Signer(opts.LibPkcs11, certificate, certificateChain, opts.PrivateKeyId, opts.CertificateId, opts.ForcePrompt)
return GetPKCS11Signer(opts.LibPkcs11, certificate, certificateChain, opts.PrivateKeyId, opts.CertificateId, opts.ReusePin)
} else {
privateKey, err = ReadPrivateKeyData(privateKeyId)
if err != nil {
Expand Down
7 changes: 7 additions & 0 deletions aws_signing_helper/signer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,10 +263,12 @@ func TestSign(t *testing.T) {
testTable = append(testTable, CredentialsOpts{
CertificateId: basic_pkcs11_uri,
PrivateKeyId: always_auth_pkcs11_uri,
ReusePin: true,
})
testTable = append(testTable, CredentialsOpts{
CertificateId: cert_file,
PrivateKeyId: always_auth_pkcs11_uri,
ReusePin: true,
})
// Note that for the below test case, there are two matching keys.
// Both keys will validate with the certificate, and one will be chosen
Expand All @@ -275,6 +277,7 @@ func TestSign(t *testing.T) {
testTable = append(testTable, CredentialsOpts{
CertificateId: cert_file,
PrivateKeyId: base_pkcs11_uri,
ReusePin: true,
})
}

Expand Down Expand Up @@ -496,18 +499,22 @@ func TestPKCS11SignerCreationFails(t *testing.T) {
testTable = append(testTable, CredentialsOpts{
CertificateId: rsa_generic_uri,
PrivateKeyId: always_auth_ec_uri,
ReusePin: true,
})
testTable = append(testTable, CredentialsOpts{
CertificateId: ec_generic_uri,
PrivateKeyId: always_auth_rsa_uri,
ReusePin: true,
})
testTable = append(testTable, CredentialsOpts{
CertificateId: "../tst/certs/ec-prime256v1-sha256-cert.pem",
PrivateKeyId: always_auth_rsa_uri,
ReusePin: true,
})
testTable = append(testTable, CredentialsOpts{
CertificateId: "../tst/certs/rsa-2048-sha256-cert.pem",
PrivateKeyId: always_auth_ec_uri,
ReusePin: true,
})

for _, credOpts := range testTable {
Expand Down
12 changes: 6 additions & 6 deletions cmd/credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ var (
noVerifySSL bool
withProxy bool
debug bool
forcePrompt bool
reusePin bool

certificateId string
privateKeyId string
Expand Down Expand Up @@ -66,13 +66,13 @@ func initCredentialsSubCommand(subCmd *cobra.Command) {
subCmd.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://\")")
subCmd.PersistentFlags().StringVar(&libPkcs11, "pkcs11-lib", "", "Library for smart card / cryptographic device (OpenSC or vendor specific)")
subCmd.PersistentFlags().BoolVar(&forcePrompt, "force-prompt", false, "Force prompting for a context-specific PIN if the "+
"CKA_ALWAYS_AUTHENTICATE attribute is set on the desired private key. This option is only relevant for the PKCS#11 integration and will "+
"be ignored in all other cases")
subCmd.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")

subCmd.MarkFlagsMutuallyExclusive("private-key", "cert-selector")
subCmd.MarkFlagsMutuallyExclusive("cert-selector", "intermediates")
subCmd.MarkFlagsMutuallyExclusive("cert-selector", "force-prompt")
subCmd.MarkFlagsMutuallyExclusive("cert-selector", "reuse-pin")
}

// Parses a cert selector string to a map
Expand Down Expand Up @@ -220,7 +220,7 @@ func PopulateCredentialsOptions() error {
Debug: debug,
Version: Version,
LibPkcs11: libPkcs11,
ForcePrompt: forcePrompt,
ReusePin: reusePin,
}

return nil
Expand Down
6 changes: 3 additions & 3 deletions cmd/sign_string.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,9 @@ func init() {
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(&libPkcs11, "pkcs11-lib", "", "Library for smart card / cryptographic device (default: p11-kit-proxy.{so, dll, dylib})")
signStringCmd.PersistentFlags().BoolVar(&forcePrompt, "force-prompt", false, "Force prompting for a context-specific PIN if the "+
"CKA_ALWAYS_AUTHENTICATE attribute is set on the desired private key. This option is only relevant for the PKCS#11 integration and will "+
"be ignored in all other cases")
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().Var(format, "format", "Output format. One of json, text, and bin")
signStringCmd.PersistentFlags().Var(digestArg, "digest", "One of SHA256, SHA384, and SHA512")
}
Expand Down

0 comments on commit 3f52117

Please sign in to comment.