/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// This helper function will release the memory backing a SECKEYPrivateKey and // any resources acquired in its creation. It will leave the backing PKCS#11 // object untouched, however. This should only be called from // PrivateKeyFromPrivateKeyTemplate. staticvoid DestroyPrivateKeyWithoutDestroyingPKCS11Object(
SECKEYPrivateKey* key) {
PK11_FreeSlot(key->pkcs11Slot);
PORT_FreeArena(key->arena, PR_TRUE);
}
// To protect against key ID collisions, PrivateKeyFromPrivateKeyTemplate // generates a random ID for each key. The given template must contain an // attribute slot for a key ID, but it must consist of a null pointer and have a // length of 0.
UniqueSECKEYPrivateKey PrivateKeyFromPrivateKeyTemplate(
CK_ATTRIBUTE* aTemplate, CK_ULONG aTemplateSize) { // Create a generic object with the contents of the key
UniquePK11SlotInfo slot(PK11_GetInternalSlot()); if (!slot) { return nullptr;
}
// Generate a random 160-bit object ID. This ID must be unique.
UniqueSECItem objID(::SECITEM_AllocItem(nullptr, nullptr, 20));
SECStatus rv = PK11_GenerateRandomOnSlot(slot.get(), objID->data, objID->len); if (rv != SECSuccess) { return nullptr;
} // Check if something is already using this ID.
SECKEYPrivateKey* preexistingKey =
PK11_FindKeyByKeyID(slot.get(), objID.get(), nullptr); if (preexistingKey) { // Note that we can't just call SECKEY_DestroyPrivateKey here because that // will destroy the PKCS#11 object that is backing a preexisting key (that // we still have a handle on somewhere else in memory). If that object were // destroyed, cryptographic operations performed by that other key would // fail.
DestroyPrivateKeyWithoutDestroyingPKCS11Object(preexistingKey); // Try again with a new ID (but only once - collisions are very unlikely).
rv = PK11_GenerateRandomOnSlot(slot.get(), objID->data, objID->len); if (rv != SECSuccess) { return nullptr;
}
preexistingKey = PK11_FindKeyByKeyID(slot.get(), objID.get(), nullptr); if (preexistingKey) {
DestroyPrivateKeyWithoutDestroyingPKCS11Object(preexistingKey); return nullptr;
}
}
CK_ATTRIBUTE* idAttributeSlot = nullptr; for (CK_ULONG i = 0; i < aTemplateSize; i++) { if (aTemplate[i].type == CKA_ID) { if (aTemplate[i].pValue != nullptr || aTemplate[i].ulValueLen != 0) { return nullptr;
}
idAttributeSlot = aTemplate + i; break;
}
} if (!idAttributeSlot) { return nullptr;
}
idAttributeSlot->pValue = objID->data;
idAttributeSlot->ulValueLen = objID->len;
UniquePK11GenericObject obj(
PK11_CreateGenericObject(slot.get(), aTemplate, aTemplateSize, PR_FALSE)); // Unset the ID attribute slot's pointer and length so that data that only // lives for the scope of this function doesn't escape.
idAttributeSlot->pValue = nullptr;
idAttributeSlot->ulValueLen = 0; if (!obj) { return nullptr;
}
// Have NSS translate the object to a private key. return UniqueSECKEYPrivateKey(
PK11_FindKeyByKeyID(slot.get(), objID.get(), nullptr));
}
// NSS exports private EC keys without the CKA_EC_POINT attribute, i.e. the // public value. To properly export the private key to JWK or PKCS #8 we need // the public key data though and so we use this method to augment a private // key with data from the given public key.
nsresult CryptoKey::AddPublicKeyData(SECKEYPublicKey* aPublicKey) { // This should be a private key.
MOZ_ASSERT(GetKeyType() == PRIVATE); // There should be a private NSS key with type 'EC', 'EC Montgomery' or 'ED'.
MOZ_ASSERT(mPrivateKey &&
(mPrivateKey->keyType == ecKey || mPrivateKey->keyType == edKey ||
mPrivateKey->keyType == ecMontKey)); // The given public key should have the same key type.
MOZ_ASSERT(aPublicKey->keyType == mPrivateKey->keyType);
// Check for |id-ecDH|. Per old versions of the WebCrypto spec we must // support this OID but NSS does unfortunately not know it. Let's // change the algorithm to |id-ecPublicKey| to make NSS happy. if (isECDHAlgorithm) {
SECOidTag oid = SEC_OID_ANSIX962_EC_PUBLIC_KEY;
SECOidData* oidData = SECOID_FindOIDByTag(oid); if (!oidData) { return nullptr;
}
if (!aRetVal.Assign(spkiItem.get())) { return NS_ERROR_DOM_OPERATION_ERR;
} return NS_OK;
}
SECItem* CreateECPointForCoordinates(const CryptoBuffer& aX, const CryptoBuffer& aY,
PLArenaPool* aArena) { // Check that both points have the same length. if (aX.Length() != aY.Length()) { return nullptr;
}
// Create point.
SECItem* point =
::SECITEM_AllocItem(aArena, nullptr, aX.Length() + aY.Length() + 1); if (!point) { return nullptr;
}
// Set point data.
point->data[0] = EC_POINT_FORM_UNCOMPRESSED;
memcpy(point->data + 1, aX.Elements(), aX.Length());
memcpy(point->data + 1 + aX.Length(), aY.Elements(), aY.Length());
return point;
}
nsresult CheckEDKeyLen(const CryptoBuffer& p) { /* Ed25519 keys are 32 bytes long.
See: https://datatracker.ietf.org/doc/html/rfc8032 Introduction. */
uint32_t lengthEDPrivatePublicKey = 32; if (p.Length() != lengthEDPrivatePublicKey) { /* We do not use this error code, we only check is the function returns
* NS_OK or not. */ return NS_ERROR_DOM_OPERATION_ERR;
}
if (aJwk.mKty.EqualsLiteral(JWK_TYPE_EC)) { // Verify that all of the required parameters are present
CryptoBuffer x, y, d; if (!aJwk.mCrv.WasPassed() || !aJwk.mX.WasPassed() ||
NS_FAILED(x.FromJwkBase64(aJwk.mX.Value())) || !aJwk.mY.WasPassed() ||
NS_FAILED(y.FromJwkBase64(aJwk.mY.Value())) || !aJwk.mD.WasPassed() ||
NS_FAILED(d.FromJwkBase64(aJwk.mD.Value()))) { return nullptr;
}
nsString namedCurve; if (!NormalizeToken(aJwk.mCrv.Value(), namedCurve)) { return nullptr;
}
UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); if (!arena) { return nullptr;
}
/* The function is used to determine a key type from the curve. */
KeyType KeyTypeFromCurveName(const nsAString& aNamedCurve) {
KeyType t = nullKey; if (aNamedCurve.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_P256) ||
aNamedCurve.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_P384) ||
aNamedCurve.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_P521)) {
t = ecKey;
} elseif (aNamedCurve.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_ED25519)) {
t = edKey;
} elseif (aNamedCurve.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_CURVE25519)) {
t = ecMontKey;
} return t;
}
/* The function is used for EC and ED keys. */
UniqueSECKEYPublicKey CreateECPublicKey(const SECItem* aKeyData, const nsAString& aNamedCurve) { if (!EnsureNSSInitializedChromeOrContent()) { return nullptr;
}
UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); if (!arena) { return nullptr;
}
// It's important that this be a UniqueSECKEYPublicKey, as this ensures that // SECKEY_DestroyPublicKey will be called on it. If this doesn't happen, when // CryptoKey::PublicKeyValid is called on it and it gets moved to the internal // PKCS#11 slot, it will leak a reference to the slot.
UniqueSECKEYPublicKey key(PORT_ArenaZNew(arena.get(), SECKEYPublicKey)); if (!key) { return nullptr;
}
// Transfer arena ownership to the key.
key->arena = arena.release();
key->keyType = KeyTypeFromCurveName(aNamedCurve); if (key->keyType != ecKey && key->keyType != edKey &&
key->keyType != ecMontKey) { return nullptr;
}
// Set public point.
SECStatus ret =
SECITEM_CopyItem(key->arena, &key->u.ec.publicValue, aKeyData); if (NS_WARN_IF(ret != SECSuccess)) { return nullptr;
}
// Ensure the given point is on the curve. if (!CryptoKey::PublicKeyValid(key.get())) { return nullptr;
}
return key;
}
UniqueSECKEYPublicKey CryptoKey::PublicKeyFromJwk(const JsonWebKey& aJwk) { if (aJwk.mKty.EqualsLiteral(JWK_TYPE_RSA)) { // Verify that all of the required parameters are present
CryptoBuffer n, e; if (!aJwk.mN.WasPassed() || NS_FAILED(n.FromJwkBase64(aJwk.mN.Value())) ||
!aJwk.mE.WasPassed() || NS_FAILED(e.FromJwkBase64(aJwk.mE.Value()))) { return nullptr;
}
if (aJwk.mKty.EqualsLiteral(JWK_TYPE_EC)) { // Verify that all of the required parameters are present
CryptoBuffer x, y; if (!aJwk.mCrv.WasPassed() || !aJwk.mX.WasPassed() ||
NS_FAILED(x.FromJwkBase64(aJwk.mX.Value())) || !aJwk.mY.WasPassed() ||
NS_FAILED(y.FromJwkBase64(aJwk.mY.Value()))) { return nullptr;
}
UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); if (!arena) { return nullptr;
}
// Create point.
SECItem* point = CreateECPointForCoordinates(x, y, arena.get()); if (!point) { return nullptr;
}
nsString namedCurve; if (!NormalizeToken(aJwk.mCrv.Value(), namedCurve)) { return nullptr;
}
return CreateECPublicKey(point, namedCurve);
}
if (aJwk.mKty.EqualsLiteral(JWK_TYPE_OKP)) { // Verify that all of the required parameters are present
CryptoBuffer x; if (!aJwk.mCrv.WasPassed() || !aJwk.mX.WasPassed() ||
NS_FAILED(x.FromJwkBase64(aJwk.mX.Value()))) { return nullptr;
}
UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); if (!arena) { return nullptr;
}
// Create point.
SECItem* point = CreateEDPointForXCoordinate(x, arena.get()); if (!point) { return nullptr;
}
nsString namedCurve; if (!NormalizeToken(aJwk.mCrv.Value(), namedCurve)) { return nullptr;
}
if (!namedCurve.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_ED25519) &&
!namedCurve.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_CURVE25519)) { return nullptr;
}
return CreateECPublicKey(point, namedCurve);
}
return nullptr;
}
nsresult CryptoKey::PublicKeyToJwk(SECKEYPublicKey* aPubKey,
JsonWebKey& aRetVal) { switch (aPubKey->keyType) { case rsaKey: {
CryptoBuffer n, e;
aRetVal.mN.Construct();
aRetVal.mE.Construct();
// Check length of uncompressed point coordinates. There are 2 field elements // and a leading point form octet (which must EC_POINT_FORM_UNCOMPRESSED). if (rawItem.len != (2 * flen + 1)) { return nullptr;
}
// No support for compressed points. if (rawItem.data[0] != EC_POINT_FORM_UNCOMPRESSED) { return nullptr;
}
// This assumes that NSS checks the validity of a public key when // it is imported into a PKCS#11 module, and returns CK_INVALID_HANDLE // if it is invalid.
CK_OBJECT_HANDLE id = PK11_ImportPublicKey(slot.get(), aPubKey, PR_FALSE); return id != CK_INVALID_HANDLE;
}
bool CryptoKey::WriteStructuredClone(JSContext* aCX,
JSStructuredCloneWriter* aWriter) const { // Write in five pieces // 1. Attributes // 2. Symmetric key as raw (if present) // 3. Private key as pkcs8 (if present) // 4. Public key as spki (if present) // 5. Algorithm in whatever form it chooses
CryptoBuffer priv, pub;
if (mPrivateKey) { if (NS_FAILED(CryptoKey::PrivateKeyToPkcs8(mPrivateKey.get(), priv))) { returnfalse;
}
}
if (mPublicKey) { if (NS_FAILED(CryptoKey::PublicKeyToSpki(mPublicKey.get(), pub))) { returnfalse;
}
}
if (sym.Length() > 0 && !key->mSymKey.Assign(sym)) { return nullptr;
} if (priv.Length() > 0) {
key->mPrivateKey = CryptoKey::PrivateKeyFromPkcs8(priv);
} if (pub.Length() > 0) {
key->mPublicKey = CryptoKey::PublicKeyFromSpki(pub);
}
// Ensure that what we've read is consistent // If the attributes indicate a key type, should have a key of that type if (!((key->GetKeyType() == SECRET && key->mSymKey.Length() > 0) ||
(key->GetKeyType() == PRIVATE && key->mPrivateKey) ||
(key->GetKeyType() == PUBLIC && key->mPublicKey))) { return nullptr;
}
return key.forget();
}
} // namespace mozilla::dom
¤ Dauer der Verarbeitung: 0.10 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.