Bug 1034856 - Implement deriveBits() for DH r=rbarnes,smaug
--- a/dom/crypto/WebCryptoTask.cpp
+++ b/dom/crypto/WebCryptoTask.cpp
@@ -2446,19 +2446,120 @@ public:
private:
size_t mLength;
ScopedSECKEYPrivateKey mPrivKey;
ScopedSECKEYPublicKey mPubKey;
virtual nsresult DoCrypto() MOZ_OVERRIDE
{
+ // CKM_SHA512_HMAC and CKA_SIGN are key type and usage attributes of the
+ // derived symmetric key and don't matter because we ignore them anyway.
ScopedPK11SymKey symKey(PK11_PubDeriveWithKDF(
mPrivKey, mPubKey, PR_FALSE, nullptr, nullptr, CKM_ECDH1_DERIVE,
- CKM_CONCATENATE_DATA_AND_BASE, CKA_DERIVE, 0, CKD_NULL, nullptr, nullptr));
+ CKM_SHA512_HMAC, CKA_SIGN, 0, CKD_NULL, nullptr, nullptr));
+
+ if (!symKey.get()) {
+ return NS_ERROR_DOM_OPERATION_ERR;
+ }
+
+ nsresult rv = MapSECStatus(PK11_ExtractKeyValue(symKey));
+ if (NS_FAILED(rv)) {
+ return NS_ERROR_DOM_OPERATION_ERR;
+ }
+
+ // This doesn't leak, because the SECItem* returned by PK11_GetKeyData
+ // just refers to a buffer managed by symKey. The assignment copies the
+ // data, so mResult manages one copy, while symKey manages another.
+ ATTEMPT_BUFFER_ASSIGN(mResult, PK11_GetKeyData(symKey));
+
+ if (mLength > mResult.Length()) {
+ return NS_ERROR_DOM_DATA_ERR;
+ }
+
+ if (!mResult.SetLength(mLength)) {
+ return NS_ERROR_DOM_UNKNOWN_ERR;
+ }
+
+ return NS_OK;
+ }
+};
+
+class DeriveDhBitsTask : public ReturnArrayBufferViewTask
+{
+public:
+ DeriveDhBitsTask(JSContext* aCx,
+ const ObjectOrString& aAlgorithm, CryptoKey& aKey, uint32_t aLength)
+ : mLength(aLength),
+ mPrivKey(aKey.GetPrivateKey())
+ {
+ Init(aCx, aAlgorithm, aKey);
+ }
+
+ DeriveDhBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
+ CryptoKey& aKey, const ObjectOrString& aTargetAlgorithm)
+ : mPrivKey(aKey.GetPrivateKey())
+ {
+ mEarlyRv = GetKeyLengthForAlgorithm(aCx, aTargetAlgorithm, mLength);
+ if (NS_SUCCEEDED(mEarlyRv)) {
+ Init(aCx, aAlgorithm, aKey);
+ }
+ }
+
+ void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey)
+ {
+ CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_DH);
+
+ // Check that we have a private key.
+ if (!mPrivKey) {
+ mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
+ return;
+ }
+
+ mLength = mLength >> 3; // bits to bytes
+
+ // Retrieve the peer's public key.
+ RootedDictionary<DhKeyDeriveParams> params(aCx);
+ mEarlyRv = Coerce(aCx, params, aAlgorithm);
+ if (NS_FAILED(mEarlyRv)) {
+ mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
+ return;
+ }
+
+ CryptoKey* publicKey = params.mPublic;
+ mPubKey = publicKey->GetPublicKey();
+ if (!mPubKey) {
+ mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
+ return;
+ }
+
+ KeyAlgorithmProxy alg1 = publicKey->Algorithm();
+ CHECK_KEY_ALGORITHM(alg1, WEBCRYPTO_ALG_DH);
+
+ // Both keys must use the same prime and generator.
+ KeyAlgorithmProxy alg2 = aKey.Algorithm();
+ if (alg1.mDh.mPrime != alg2.mDh.mPrime ||
+ alg1.mDh.mGenerator != alg2.mDh.mGenerator) {
+ mEarlyRv = NS_ERROR_DOM_DATA_ERR;
+ return;
+ }
+ }
+
+private:
+ size_t mLength;
+ ScopedSECKEYPrivateKey mPrivKey;
+ ScopedSECKEYPublicKey mPubKey;
+
+ virtual nsresult DoCrypto() MOZ_OVERRIDE
+ {
+ // CKM_SHA512_HMAC and CKA_SIGN are key type and usage attributes of the
+ // derived symmetric key and don't matter because we ignore them anyway.
+ ScopedPK11SymKey symKey(PK11_PubDeriveWithKDF(
+ mPrivKey, mPubKey, PR_FALSE, nullptr, nullptr, CKM_DH_PKCS_DERIVE,
+ CKM_SHA512_HMAC, CKA_SIGN, 0, CKD_NULL, nullptr, nullptr));
if (!symKey.get()) {
return NS_ERROR_DOM_OPERATION_ERR;
}
nsresult rv = MapSECStatus(PK11_ExtractKeyValue(symKey));
if (NS_FAILED(rv)) {
return NS_ERROR_DOM_OPERATION_ERR;
@@ -2869,16 +2970,20 @@ WebCryptoTask::CreateDeriveBitsTask(JSCo
if (algName.EqualsASCII(WEBCRYPTO_ALG_PBKDF2)) {
return new DerivePbkdfBitsTask(aCx, aAlgorithm, aKey, aLength);
}
if (algName.EqualsASCII(WEBCRYPTO_ALG_ECDH)) {
return new DeriveEcdhBitsTask(aCx, aAlgorithm, aKey, aLength);
}
+ if (algName.EqualsASCII(WEBCRYPTO_ALG_DH)) {
+ return new DeriveDhBitsTask(aCx, aAlgorithm, aKey, aLength);
+ }
+
return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
}
WebCryptoTask*
WebCryptoTask::CreateWrapKeyTask(JSContext* aCx,
const nsAString& aFormat,
CryptoKey& aKey,
CryptoKey& aWrappingKey,
--- a/dom/crypto/test/test-vectors.js
+++ b/dom/crypto/test/test-vectors.js
@@ -660,11 +660,18 @@ tv = {
},
// RFC 2409 <http://tools.ietf.org/html/rfc2409#section-6.1>
dh: {
prime: util.hex2abv(
"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74" +
"020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f1437" +
"4fe1356d6d51c245e485b576625e7ec6f44c42e9a63a3620ffffffffffffffff"
+ ),
+
+ prime2: util.hex2abv(
+ "ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74" +
+ "020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f1437" +
+ "4fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7ed" +
+ "ee386bfb5a899fa5ae9f24117c4b1fe649286651ece65381ffffffffffffffff"
)
},
}
--- a/dom/crypto/test/test_WebCrypto_DH.html
+++ b/dom/crypto/test/test_WebCrypto_DH.html
@@ -50,16 +50,138 @@ TestArray.addTest(
(x.privateKey.usages.length == 2) &&
(x.privateKey.usages[0] == "deriveKey") &&
(x.privateKey.usages[1] == "deriveBits");
}),
error(that)
);
}
);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+ "Derive bits from a DH key",
+ function() {
+ var that = this;
+ var alg = {
+ name: "DH",
+ prime: tv.dh.prime,
+ generator: new Uint8Array([0x02])
+ };
+
+ function doDerive(x) {
+ var alg = {
+ name: "DH",
+ public: x.publicKey
+ };
+ return crypto.subtle.deriveBits(alg, x.privateKey, 128);
+ }
+
+ crypto.subtle.generateKey(alg, false, ["deriveBits"])
+ .then(doDerive, error(that))
+ .then(complete(that, function (x) {
+ return x.byteLength == 16;
+ }), error(that));
+ }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+ "Test that DH deriveBits() fails when the public key is not a DH key",
+ function() {
+ var that = this;
+ var pubKey, privKey;
+ function setPub(x) { pubKey = x.publicKey; }
+ function setPriv(x) { privKey = x.privateKey; }
+
+ function doGenerateDH() {
+ var alg = {
+ name: "DH",
+ prime: tv.dh.prime,
+ generator: new Uint8Array([0x02])
+ };
+ return crypto.subtle.generateKey(alg, false, ["deriveBits"]);
+ }
+
+ function doGenerateRSA() {
+ var alg = {
+ name: "RSA-OAEP",
+ hash: "SHA-256",
+ modulusLength: 2048,
+ publicExponent: new Uint8Array([0x01, 0x00, 0x01])
+ };
+ return crypto.subtle.generateKey(alg, false, ["encrypt"])
+ }
+
+ function doDerive() {
+ var alg = {name: "DH", public: pubKey};
+ return crypto.subtle.deriveBits(alg, privKey, 128);
+ }
+
+ doGenerateDH()
+ .then(setPriv, error(that))
+ .then(doGenerateRSA, error(that))
+ .then(setPub, error(that))
+ .then(doDerive, error(that))
+ .then(error(that), complete(that));
+ }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+ "Test that DH deriveBits() fails when the given keys' primes or bases don't match",
+ function() {
+ var that = this;
+ var pubKey, privKey;
+ function setPub(x) { pubKey = x.publicKey; }
+ function setPriv(x) { privKey = x.privateKey; }
+
+ function doGenerateDH() {
+ var alg = {
+ name: "DH",
+ prime: tv.dh.prime,
+ generator: new Uint8Array([0x02])
+ };
+ return crypto.subtle.generateKey(alg, false, ["deriveBits"]);
+ }
+
+ function doGenerateDH2() {
+ var alg = {
+ name: "DH",
+ prime: tv.dh.prime2,
+ generator: new Uint8Array([0x02])
+ };
+ return crypto.subtle.generateKey(alg, false, ["deriveBits"]);
+ }
+
+ function doGenerateDH3() {
+ var alg = {
+ name: "DH",
+ prime: tv.dh.prime,
+ generator: new Uint8Array([0x03])
+ };
+ return crypto.subtle.generateKey(alg, false, ["deriveBits"]);
+ }
+
+ function doDerive() {
+ var alg = {name: "DH", public: pubKey};
+ return crypto.subtle.deriveBits(alg, privKey, 128);
+ }
+
+ doGenerateDH()
+ .then(setPriv, error(that))
+ .then(doGenerateDH2, error(that))
+ .then(setPub, error(that))
+ .then(doDerive, error(that))
+ .then(error(that), doGenerateDH3)
+ .then(setPub, error(that))
+ .then(doDerive, error(that))
+ .then(error(that), complete(that));
+ }
+);
/*]]>*/</script>
</head>
<body>
<div id="content">
<div id="head">
<b>Web</b>Crypto<br>
--- a/dom/webidl/SubtleCrypto.webidl
+++ b/dom/webidl/SubtleCrypto.webidl
@@ -82,16 +82,20 @@ dictionary AesDerivedKeyParams : Algorit
dictionary HmacDerivedKeyParams : HmacImportParams {
[EnforceRange] unsigned long length;
};
dictionary EcdhKeyDeriveParams : Algorithm {
required CryptoKey public;
};
+dictionary DhKeyDeriveParams : Algorithm {
+ required CryptoKey public;
+};
+
dictionary EcdsaParams : Algorithm {
required AlgorithmIdentifier hash;
};
/***** JWK *****/
dictionary RsaOtherPrimesInfo {
// The following fields are defined in Section 6.3.2.7 of JSON Web Algorithms