/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// 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) { // PK11_FindKeyByKeyID returns a non-owning reference, so destroying it // here does not touch the PKCS#11 object backing the preexisting key.
SECKEY_DestroyPrivateKey(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) {
SECKEY_DestroyPrivateKey(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. Since NSS 3.122 (bug // 2017945) PK11_FindKeyByKeyID returns a wrapper that does not own the // underlying PKCS#11 object. We created this session object above, so we own // it and must mark it owned so it is destroyed along with the key.
UniqueSECKEYPrivateKey privKey(
PK11_FindKeyByKeyID(slot.get(), objID.get(), nullptr)); if (privKey) {
SECKEYPRIVATEKEY_SET_OWNED(privKey.get(), PR_TRUE);
} return privKey;
}
// 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();
// Here we have 2 possible inputs, either we've received an uncompressed point // then the length is 1 + flen (x) + flen (y) and the 0th byte is // EC_POINT_FORM_UNCOMPRESSED or we work with the compressed point then the // length is 1 + flen (x) and the 0th byte is either // EC_POINT_FORM_COMPRESSED_Y0 or EC_POINT_FORM_COMPRESSED_Y1
// 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); if (id == CK_INVALID_HANDLE) { returnfalse;
}
// It is possible that the public key was in the decompressed form // Thus we need to read the attribute to retrieve the key if (aPubKey->keyType == ecKey && PublicECKeyEncoded(aPubKey)) {
ScopedAutoSECItem encodedPublicKey; // Independently from whether the key was decompressed or not, // the raw attribute is stored encoded.
SECStatus rv = PK11_ReadRawAttribute(PK11_TypePubKey, aPubKey, CKA_EC_POINT,
&encodedPublicKey); if (NS_WARN_IF(rv != SECSuccess)) { returnfalse;
}
// Updating the public key
rv = SECITEM_CopyItem(aPubKey->arena, &aPubKey->u.ec.publicValue, &decoded); if (NS_WARN_IF(rv != SECSuccess)) { returnfalse;
}
}
return true;
}
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;
}
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 und die Messung sind noch experimentell.