in aws_signing_helper/pkcs11_signer.go [713:926]
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
templatePrivateKey []*pkcs11.Attribute
privateKeyObjects []pkcs11.ObjectHandle
keyAttributes []*pkcs11.Attribute
)
if keyUri == nil {
keyUri = certUri
noKeyUri = true
}
if userPin == "" {
userPin, _ = keyUri.GetQueryAttribute("pin-value", false)
}
// This time we're looking for a *single* slot, as we (presumably)
// will have to log in to access the key.
slots = matchSlots(slots, keyUri)
if len(slots) == 1 {
if certSlotNr != slots[0].id {
keySlot = slots[0]
manufacturerId = slots[0].info.ManufacturerID
if session != 0 {
if loggedIn {
module.Logout(session)
module.CloseSession(session)
}
}
loggedIn = false
session = 0
}
} else {
if Debug {
log.Printf("Found %d matching slots for the PKCS#11 key\n", len(slots))
}
// If the URI matched multiple slots *but* one of them is the
// one (certSlotNr) that the certificate was found in, then use
// that.
for _, slot := range slots {
if certSlotNr == slot.id {
keySlot = slot
manufacturerId = slot.info.ManufacturerID
goto got_slot
}
}
err = errors.New("Could not identify unique slot for PKCS#11 key")
goto fail
}
got_slot:
if session == 0 {
session, err = module.OpenSession(keySlot.id, pkcs11.CKF_SERIAL_SESSION|pkcs11.CKS_RO_PUBLIC_SESSION)
}
if err != nil {
goto fail
}
// And *now* we fall back to prompting the user for a PIN if necessary.
if !loggedIn {
if userPin == "" {
passwordName := "user PIN"
finalAuthErrMsg := "user authentication failed (%s)"
userPin, err = pkcs11PasswordPrompt(module, session, pkcs11.CKU_USER, passwordName, finalAuthErrMsg)
if err != nil {
goto fail
}
} else {
err = module.Login(session, pkcs11.CKU_USER, userPin)
if err != nil {
goto fail
}
}
}
retry_search:
templatePrivateKey = getFindTemplate(keyUri, pkcs11.CKO_PRIVATE_KEY)
if err = module.FindObjectsInit(session, templatePrivateKey); err != nil {
goto fail
}
for true {
sessionPrivateKeyObjects, _, err := module.FindObjects(session, MAX_OBJECT_LIMIT)
if err != nil {
goto fail
}
if len(sessionPrivateKeyObjects) == 0 {
break
}
privateKeyObjects = append(privateKeyObjects, sessionPrivateKeyObjects...)
if len(sessionPrivateKeyObjects) < MAX_OBJECT_LIMIT {
break
}
}
if err = module.FindObjectsFinal(session); err != nil {
goto fail
}
// If we found multiple keys, try them until we find the one
// that actually matches the cert. More realistically, there
// will be only one. Sanity check that it matches the cert.
for _, curPrivateKeyHandle := range privateKeyObjects {
keyAttributes = []*pkcs11.Attribute{
pkcs11.NewAttribute(pkcs11.CKA_KEY_TYPE, 0),
}
if keyAttributes, err = module.GetAttributeValue(session, curPrivateKeyHandle, keyAttributes); err != nil {
continue
}
keyType, err = bytesToUint(keyAttributes[0].Value)
if err != nil {
goto fail
}
keyAttributes = []*pkcs11.Attribute{
pkcs11.NewAttribute(pkcs11.CKA_ALWAYS_AUTHENTICATE, 0),
}
keyAttributes, err = module.GetAttributeValue(session, curPrivateKeyHandle, keyAttributes)
if err == nil {
alwaysAuth, err = bytesToUint(keyAttributes[0].Value)
if err != nil {
goto fail
}
} else {
alwaysAuth = 0
}
var curPrivateKeyObj KeyObjInfo
curPrivateKeyObj.keyObject = curPrivateKeyHandle
// Fetch the CKA_ID and CKA_LABEL of the current private key object, so
// that more specific attributes can be used to identify the private key
// when prompting for a context-specifc PIN (assuming the CKA_ALWAYS_AUTHENTICATE
// attribute is set on the private key object).
keyAttributes = []*pkcs11.Attribute{
pkcs11.NewAttribute(pkcs11.CKA_ID, 0),
}
keyAttributes, err = module.GetAttributeValue(session, curPrivateKeyHandle, keyAttributes)
if err == nil {
curPrivateKeyObj.id = keyAttributes[0].Value
}
keyAttributes = []*pkcs11.Attribute{
pkcs11.NewAttribute(pkcs11.CKA_LABEL, 0),
}
keyAttributes, err = module.GetAttributeValue(session, curPrivateKeyHandle, keyAttributes)
if err == nil {
curPrivateKeyObj.label = keyAttributes[0].Value
}
if certObj.cert == nil {
if len(privateKeyObjects) == 1 {
privateKeyObj = curPrivateKeyObj
break
} else {
err = errors.New("multiple matching private keys, but" +
" no certificate provided to match with")
goto fail
}
}
var curContextSpecificPin string
privateKeyMatchesCert := false
curContextSpecificPin, privateKeyMatchesCert = checkPrivateKeyMatchesCert(module, session, keyType, userPin, alwaysAuth, "", reusePin, curPrivateKeyObj, keySlot, certObj.cert, manufacturerId)
if privateKeyMatchesCert {
privateKeyObj = curPrivateKeyObj
contextSpecificPin = curContextSpecificPin
break
}
}
if privateKeyObj.keyObject == 0 {
/* "If the key is not found and the original search was by
* CKA_LABEL of the certificate, then repeat the search using
* the CKA_ID of the certificate that was actually found, but
* not requiring a CKA_LABEL match."
*
* http://david.woodhou.se/draft-woodhouse-cert-best-practice.html#rfc.section.8.2
*/
if certObj.cert != nil {
if noKeyUri {
_, keyHadLabel := keyUri.GetPathAttribute("object", false)
if keyHadLabel {
if Debug {
log.Println("unable to find private key with CKA_LABEL;" +
" repeating the search using CKA_ID of the certificate" +
" without requiring a CKA_LABEL match")
}
keyUri.RemovePathAttribute("object")
keyUri.SetPathAttribute("id", escapeAll(certObj.id))
goto retry_search
}
}
}
err = errors.New("unable to find matching private key")
goto fail
}
// So that hunting for the key can be more efficient in the future,
// return a key URI that has CKA_ID and CKA_LABEL appropriately set.
if privateKeyObj.id != nil && len(privateKeyObj.id) != 0 {
keyUri.SetPathAttribute("id", escapeAll(privateKeyObj.id))
}
if privateKeyObj.label != nil && len(privateKeyObj.label) != 0 {
keyUri.SetPathAttribute("object", escapeAll(privateKeyObj.label))
}
return session, userPin, keyUri, keyType, privateKeyObj, keySlot, alwaysAuth, contextSpecificPin, nil
fail:
return 0, "", nil, 0, KeyObjInfo{}, SlotIdInfo{}, 0, "", err
}