func getPKCS11Key()

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
}