mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-30 00:01:50 +00:00
Bug 1034855 - Implement SPKI import/export for ECDH r=keeler,rbarnes
This commit is contained in:
parent
454efc0d1e
commit
d3f757e062
@ -322,6 +322,23 @@ CryptoKey::PublicKeyFromSpki(CryptoBuffer& aKeyData,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check for id-ecDH. Per the WebCrypto spec we must support it but NSS
|
||||
// does unfortunately not know about it. Let's change the algorithm to
|
||||
// id-ecPublicKey to make NSS happy.
|
||||
if (SECITEM_ItemsAreEqual(&SEC_OID_DATA_EC_DH, &spki->algorithm.algorithm)) {
|
||||
// Retrieve OID data for id-ecPublicKey (1.2.840.10045.2.1).
|
||||
SECOidData* oidData = SECOID_FindOIDByTag(SEC_OID_ANSIX962_EC_PUBLIC_KEY);
|
||||
if (!oidData) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SECStatus rv = SECITEM_CopyItem(spki->arena, &spki->algorithm.algorithm,
|
||||
&oidData->oid);
|
||||
if (rv != SECSuccess) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return SECKEY_ExtractPublicKey(spki.get());
|
||||
}
|
||||
|
||||
@ -343,11 +360,25 @@ CryptoKey::PublicKeyToSpki(SECKEYPublicKey* aPubKey,
|
||||
CryptoBuffer& aRetVal,
|
||||
const nsNSSShutDownPreventionLock& /*proofOfLock*/)
|
||||
{
|
||||
ScopedSECItem spkiItem(PK11_DEREncodePublicKey(aPubKey));
|
||||
if (!spkiItem.get()) {
|
||||
return NS_ERROR_DOM_INVALID_ACCESS_ERR;
|
||||
ScopedCERTSubjectPublicKeyInfo spki(SECKEY_CreateSubjectPublicKeyInfo(aPubKey));
|
||||
if (!spki) {
|
||||
return NS_ERROR_DOM_OPERATION_ERR;
|
||||
}
|
||||
|
||||
// Per WebCrypto spec we must export ECDH SPKIs with the algorithm OID
|
||||
// id-ecDH (1.3.132.112). NSS doesn't know about that OID and there is
|
||||
// no way to specify the algorithm to use when exporting a public key.
|
||||
if (aPubKey->keyType == ecKey) {
|
||||
SECStatus rv = SECITEM_CopyItem(spki->arena, &spki->algorithm.algorithm,
|
||||
&SEC_OID_DATA_EC_DH);
|
||||
if (rv != SECSuccess) {
|
||||
return NS_ERROR_DOM_OPERATION_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
const SEC_ASN1Template* tpl = SEC_ASN1_GET(CERT_SubjectPublicKeyInfoTemplate);
|
||||
ScopedSECItem spkiItem(SEC_ASN1EncodeItem(nullptr, nullptr, spki, tpl));
|
||||
|
||||
aRetVal.Assign(spkiItem.get());
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -92,6 +92,11 @@
|
||||
// Define an unknown mechanism type
|
||||
#define UNKNOWN_CK_MECHANISM CKM_VENDOR_DEFINED+1
|
||||
|
||||
// python security/pkix/tools/DottedOIDToCode.py id-ecDH 1.3.132.112
|
||||
static const uint8_t id_ecDH[] = { 0x2b, 0x81, 0x04, 0x70 };
|
||||
const SECItem SEC_OID_DATA_EC_DH = { siBuffer, (unsigned char*)id_ecDH,
|
||||
PR_ARRAY_SIZE(id_ecDH) };
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
|
@ -258,6 +258,26 @@ GetKeySizeForAlgorithm(JSContext* aCx, const ObjectOrString& aAlgorithm,
|
||||
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
|
||||
}
|
||||
|
||||
inline bool
|
||||
MapOIDTagToNamedCurve(SECOidTag aOIDTag, nsString& aResult)
|
||||
{
|
||||
switch (aOIDTag) {
|
||||
case SEC_OID_SECG_EC_SECP256R1:
|
||||
aResult.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P256);
|
||||
break;
|
||||
case SEC_OID_SECG_EC_SECP384R1:
|
||||
aResult.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P384);
|
||||
break;
|
||||
case SEC_OID_SECG_EC_SECP521R1:
|
||||
aResult.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P521);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Helper function to clone data from an ArrayBuffer or ArrayBufferView object
|
||||
inline bool
|
||||
CloneData(JSContext* aCx, CryptoBuffer& aDst, JS::Handle<JSObject*> aSrc)
|
||||
@ -1682,16 +1702,12 @@ private:
|
||||
|
||||
virtual nsresult DoCrypto() MOZ_OVERRIDE
|
||||
{
|
||||
if (!mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
|
||||
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
|
||||
}
|
||||
|
||||
// Import the key data itself
|
||||
ScopedSECKEYPublicKey pubKey;
|
||||
ScopedSECKEYPrivateKey privKey;
|
||||
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (mJwk.mD.WasPassed()) {
|
||||
if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) && mJwk.mD.WasPassed()) {
|
||||
// Private key import
|
||||
privKey = CryptoKey::PrivateKeyFromJwk(mJwk, locker);
|
||||
if (!privKey) {
|
||||
@ -1700,19 +1716,47 @@ private:
|
||||
|
||||
mKey->SetPrivateKey(privKey.get());
|
||||
mKey->SetType(CryptoKey::PRIVATE);
|
||||
} else {
|
||||
} else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) ||
|
||||
(mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
|
||||
!mJwk.mD.WasPassed())) {
|
||||
// Public key import
|
||||
pubKey = CryptoKey::PublicKeyFromJwk(mJwk, locker);
|
||||
if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
|
||||
pubKey = CryptoKey::PublicKeyFromSpki(mKeyData, locker);
|
||||
} else {
|
||||
pubKey = CryptoKey::PublicKeyFromJwk(mJwk, locker);
|
||||
}
|
||||
|
||||
if (!pubKey) {
|
||||
return NS_ERROR_DOM_DATA_ERR;
|
||||
}
|
||||
|
||||
if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
|
||||
if (!CheckEncodedECParameters(&pubKey->u.ec.DEREncodedParams)) {
|
||||
return NS_ERROR_DOM_OPERATION_ERR;
|
||||
}
|
||||
|
||||
// Construct the OID tag.
|
||||
SECItem oid = { siBuffer, nullptr, 0 };
|
||||
oid.len = pubKey->u.ec.DEREncodedParams.data[1];
|
||||
oid.data = pubKey->u.ec.DEREncodedParams.data + 2;
|
||||
|
||||
// Find a matching and supported named curve.
|
||||
if (!MapOIDTagToNamedCurve(SECOID_FindOIDTag(&oid), mNamedCurve)) {
|
||||
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
mKey->SetPublicKey(pubKey.get());
|
||||
mKey->SetType(CryptoKey::PUBLIC);
|
||||
} else {
|
||||
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
|
||||
}
|
||||
|
||||
if (!NormalizeNamedCurveValue(mJwk.mCrv.Value(), mNamedCurve)) {
|
||||
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
|
||||
// Extract 'crv' parameter from JWKs.
|
||||
if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
|
||||
if (!NormalizeNamedCurveValue(mJwk.mCrv.Value(), mNamedCurve)) {
|
||||
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -472,6 +472,20 @@ tv = {
|
||||
y: "is9pWAaneK4RdxmdLfsq5IwizDmUS2w8OGS99sKm3ek"
|
||||
},
|
||||
|
||||
// vector with algorithm = id-ecDH
|
||||
spki: util.hex2abv(
|
||||
"3056301006042b81047006082a8648ce3d030107034200045ce7b86e3b326604" +
|
||||
"03e63712ef0998deae1027faec3c1be9f76f934dfeb58e98f4cf075b39405dd1" +
|
||||
"f1adeb090107edcfb2b4963739d87679e3056cb0557d0adf"
|
||||
),
|
||||
|
||||
// vector with algorithm = id-ecPublicKey
|
||||
spki_id_ecpk: util.hex2abv(
|
||||
"3059301306072a8648ce3d020106082a8648ce3d030107034200045ce7b86e3b" +
|
||||
"32660403e63712ef0998deae1027faec3c1be9f76f934dfeb58e98f4cf075b39" +
|
||||
"405dd1f1adeb090107edcfb2b4963739d87679e3056cb0557d0adf"
|
||||
),
|
||||
|
||||
secret: util.hex2abv(
|
||||
"35669cd5c244ba6c1ea89b8802c3d1db815cd769979072e6556eb98548c65f7d"
|
||||
)
|
||||
|
@ -2191,3 +2191,65 @@ TestArray.addTest(
|
||||
.then(complete(that), error(that));
|
||||
}
|
||||
);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
TestArray.addTest(
|
||||
"SPKI import/export of public ECDH keys (P-256)",
|
||||
function () {
|
||||
var that = this;
|
||||
var alg = { name: "ECDH" };
|
||||
var keys = ["spki", "spki_id_ecpk"];
|
||||
|
||||
function doImport(key) {
|
||||
return crypto.subtle.importKey("spki", tv.ecdh_p256[key], alg, true, ["deriveBits"]);
|
||||
}
|
||||
|
||||
function doExport(x) {
|
||||
return crypto.subtle.exportKey("spki", x);
|
||||
}
|
||||
|
||||
function nextKey() {
|
||||
var key = keys.shift();
|
||||
var imported = doImport(key);
|
||||
var derived = imported.then(doExport);
|
||||
|
||||
return derived.then(function (x) {
|
||||
if (!util.memcmp(x, tv.ecdh_p256.spki)) {
|
||||
throw "exported key is invalid";
|
||||
}
|
||||
|
||||
if (keys.length) {
|
||||
return nextKey();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
nextKey().then(complete(that), error(that));
|
||||
}
|
||||
);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
TestArray.addTest(
|
||||
"SPKI/JWK import ECDH keys (P-256) and derive a known secret",
|
||||
function () {
|
||||
var that = this;
|
||||
var alg = { name: "ECDH" };
|
||||
|
||||
var pubKey, privKey;
|
||||
function setPub(x) { pubKey = x; }
|
||||
function setPriv(x) { privKey = x; }
|
||||
|
||||
function doDerive() {
|
||||
var alg = { name: "ECDH", public: pubKey };
|
||||
return crypto.subtle.deriveBits(alg, privKey, tv.ecdh_p256.secret.byteLength * 8);
|
||||
}
|
||||
|
||||
Promise.all([
|
||||
crypto.subtle.importKey("spki", tv.ecdh_p256.spki, alg, false, ["deriveBits"])
|
||||
.then(setPub),
|
||||
crypto.subtle.importKey("jwk", tv.ecdh_p256.jwk_priv, alg, false, ["deriveBits"])
|
||||
.then(setPriv)
|
||||
]).then(doDerive)
|
||||
.then(memcmp_complete(that, tv.ecdh_p256.secret), error(that));
|
||||
}
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user