mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1034855 - Implement SPKI import/export for ECDH r=keeler,rbarnes
This commit is contained in:
parent
e22e6a6640
commit
6930a08cf6
@ -322,6 +322,23 @@ CryptoKey::PublicKeyFromSpki(CryptoBuffer& aKeyData,
|
|||||||
return nullptr;
|
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());
|
return SECKEY_ExtractPublicKey(spki.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -343,11 +360,25 @@ CryptoKey::PublicKeyToSpki(SECKEYPublicKey* aPubKey,
|
|||||||
CryptoBuffer& aRetVal,
|
CryptoBuffer& aRetVal,
|
||||||
const nsNSSShutDownPreventionLock& /*proofOfLock*/)
|
const nsNSSShutDownPreventionLock& /*proofOfLock*/)
|
||||||
{
|
{
|
||||||
ScopedSECItem spkiItem(PK11_DEREncodePublicKey(aPubKey));
|
ScopedCERTSubjectPublicKeyInfo spki(SECKEY_CreateSubjectPublicKeyInfo(aPubKey));
|
||||||
if (!spkiItem.get()) {
|
if (!spki) {
|
||||||
return NS_ERROR_DOM_INVALID_ACCESS_ERR;
|
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());
|
aRetVal.Assign(spkiItem.get());
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
@ -92,6 +92,11 @@
|
|||||||
// Define an unknown mechanism type
|
// Define an unknown mechanism type
|
||||||
#define UNKNOWN_CK_MECHANISM CKM_VENDOR_DEFINED+1
|
#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 mozilla {
|
||||||
namespace dom {
|
namespace dom {
|
||||||
|
|
||||||
|
@ -258,6 +258,26 @@ GetKeySizeForAlgorithm(JSContext* aCx, const ObjectOrString& aAlgorithm,
|
|||||||
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
|
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
|
// Helper function to clone data from an ArrayBuffer or ArrayBufferView object
|
||||||
inline bool
|
inline bool
|
||||||
CloneData(JSContext* aCx, CryptoBuffer& aDst, JS::Handle<JSObject*> aSrc)
|
CloneData(JSContext* aCx, CryptoBuffer& aDst, JS::Handle<JSObject*> aSrc)
|
||||||
@ -1682,16 +1702,12 @@ private:
|
|||||||
|
|
||||||
virtual nsresult DoCrypto() MOZ_OVERRIDE
|
virtual nsresult DoCrypto() MOZ_OVERRIDE
|
||||||
{
|
{
|
||||||
if (!mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
|
|
||||||
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Import the key data itself
|
// Import the key data itself
|
||||||
ScopedSECKEYPublicKey pubKey;
|
ScopedSECKEYPublicKey pubKey;
|
||||||
ScopedSECKEYPrivateKey privKey;
|
ScopedSECKEYPrivateKey privKey;
|
||||||
|
|
||||||
nsNSSShutDownPreventionLock locker;
|
nsNSSShutDownPreventionLock locker;
|
||||||
if (mJwk.mD.WasPassed()) {
|
if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) && mJwk.mD.WasPassed()) {
|
||||||
// Private key import
|
// Private key import
|
||||||
privKey = CryptoKey::PrivateKeyFromJwk(mJwk, locker);
|
privKey = CryptoKey::PrivateKeyFromJwk(mJwk, locker);
|
||||||
if (!privKey) {
|
if (!privKey) {
|
||||||
@ -1700,19 +1716,47 @@ private:
|
|||||||
|
|
||||||
mKey->SetPrivateKey(privKey.get());
|
mKey->SetPrivateKey(privKey.get());
|
||||||
mKey->SetType(CryptoKey::PRIVATE);
|
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
|
// 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) {
|
if (!pubKey) {
|
||||||
return NS_ERROR_DOM_DATA_ERR;
|
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->SetPublicKey(pubKey.get());
|
||||||
mKey->SetType(CryptoKey::PUBLIC);
|
mKey->SetType(CryptoKey::PUBLIC);
|
||||||
|
} else {
|
||||||
|
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!NormalizeNamedCurveValue(mJwk.mCrv.Value(), mNamedCurve)) {
|
// Extract 'crv' parameter from JWKs.
|
||||||
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
|
if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
|
||||||
|
if (!NormalizeNamedCurveValue(mJwk.mCrv.Value(), mNamedCurve)) {
|
||||||
|
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
@ -472,6 +472,20 @@ tv = {
|
|||||||
y: "is9pWAaneK4RdxmdLfsq5IwizDmUS2w8OGS99sKm3ek"
|
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(
|
secret: util.hex2abv(
|
||||||
"35669cd5c244ba6c1ea89b8802c3d1db815cd769979072e6556eb98548c65f7d"
|
"35669cd5c244ba6c1ea89b8802c3d1db815cd769979072e6556eb98548c65f7d"
|
||||||
)
|
)
|
||||||
|
@ -2191,3 +2191,65 @@ TestArray.addTest(
|
|||||||
.then(complete(that), error(that));
|
.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