Bug 1034856 - Implement generateKey() for DH. r=rbarnes, a=lsblakk
--- a/dom/crypto/CryptoBuffer.cpp
+++ b/dom/crypto/CryptoBuffer.cpp
@@ -156,16 +156,30 @@ CryptoBuffer::ToSECItem() const
item->data = data;
item->len = Length();
memcpy(item->data, Elements(), Length());
return item;
}
+bool
+CryptoBuffer::ToSECItem(PLArenaPool *aArena, SECItem* aItem) const
+{
+ aItem->type = siBuffer;
+ aItem->data = nullptr;
+
+ if (!::SECITEM_AllocItem(aArena, aItem, Length())) {
+ return false;
+ }
+
+ memcpy(aItem->data, Elements(), Length());
+ return true;
+}
+
JSObject*
CryptoBuffer::ToUint8Array(JSContext* aCx) const
{
return Uint8Array::Create(aCx, Length(), Elements());
}
// "BigInt" comes from the WebCrypto spec
--- a/dom/crypto/CryptoBuffer.h
+++ b/dom/crypto/CryptoBuffer.h
@@ -35,16 +35,17 @@ public:
{
aArray.ComputeLengthAndData();
return Assign(aArray.Data(), aArray.Length());
}
nsresult FromJwkBase64(const nsString& aBase64);
nsresult ToJwkBase64(nsString& aBase64);
SECItem* ToSECItem() const;
+ bool ToSECItem(PLArenaPool* aArena, SECItem* aItem) const;
JSObject* ToUint8Array(JSContext* aCx) const;
bool GetBigIntValue(unsigned long& aRetVal);
};
} // namespace dom
} // namespace mozilla
--- a/dom/crypto/WebCryptoTask.cpp
+++ b/dom/crypto/WebCryptoTask.cpp
@@ -2003,16 +2003,22 @@ public:
const Sequence<nsString>& aKeyUsages)
{
nsIGlobalObject* global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
if (!global) {
mEarlyRv = NS_ERROR_DOM_UNKNOWN_ERR;
return;
}
+ mArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (!mArena) {
+ mEarlyRv = NS_ERROR_DOM_UNKNOWN_ERR;
+ return;
+ }
+
// Create an empty key and set easy attributes
mKeyPair.mPrivateKey = new CryptoKey(global);
mKeyPair.mPublicKey = new CryptoKey(global);
// Extract algorithm name
nsString algName;
mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
if (NS_FAILED(mEarlyRv)) {
@@ -2073,30 +2079,56 @@ public:
mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
return;
}
// Create algorithm.
mKeyPair.mPublicKey.get()->Algorithm().MakeEc(algName, mNamedCurve);
mKeyPair.mPrivateKey.get()->Algorithm().MakeEc(algName, mNamedCurve);
mMechanism = CKM_EC_KEY_PAIR_GEN;
+ } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_DH)) {
+ RootedDictionary<DhKeyGenParams> params(aCx);
+ mEarlyRv = Coerce(aCx, params, aAlgorithm);
+ if (NS_FAILED(mEarlyRv)) {
+ mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
+ return;
+ }
+
+ CryptoBuffer prime;
+ ATTEMPT_BUFFER_INIT(prime, params.mPrime);
+
+ CryptoBuffer generator;
+ ATTEMPT_BUFFER_INIT(generator, params.mGenerator);
+
+ // Set up params.
+ if (!prime.ToSECItem(mArena, &mDhParams.prime) ||
+ !generator.ToSECItem(mArena, &mDhParams.base)) {
+ mEarlyRv = NS_ERROR_DOM_UNKNOWN_ERR;
+ return;
+ }
+
+ // Create algorithm.
+ mKeyPair.mPublicKey.get()->Algorithm().MakeDh(algName, prime, generator);
+ mKeyPair.mPrivateKey.get()->Algorithm().MakeDh(algName, prime, generator);
+ mMechanism = CKM_DH_PKCS_KEY_PAIR_GEN;
} else {
mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
return;
}
// Set key usages.
if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
privateAllowedUsages = CryptoKey::SIGN;
publicAllowedUsages = CryptoKey::VERIFY;
} else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
privateAllowedUsages = CryptoKey::DECRYPT | CryptoKey::UNWRAPKEY;
publicAllowedUsages = CryptoKey::ENCRYPT | CryptoKey::WRAPKEY;
- } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
+ } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
+ algName.EqualsLiteral(WEBCRYPTO_ALG_DH)) {
privateAllowedUsages = CryptoKey::DERIVEKEY | CryptoKey::DERIVEBITS;
publicAllowedUsages = 0;
}
mKeyPair.mPrivateKey.get()->SetExtractable(aExtractable);
mKeyPair.mPrivateKey.get()->SetType(CryptoKey::PRIVATE);
mKeyPair.mPublicKey.get()->SetExtractable(true);
@@ -2122,48 +2154,46 @@ public:
if (!mKeyPair.mPublicKey.get()->HasAnyUsage() &&
!mKeyPair.mPrivateKey.get()->HasAnyUsage()) {
mEarlyRv = NS_ERROR_DOM_DATA_ERR;
return;
}
}
private:
+ ScopedPLArenaPool mArena;
CryptoKeyPair mKeyPair;
CK_MECHANISM_TYPE mMechanism;
PK11RSAGenParams mRsaParams;
+ SECKEYDHParams mDhParams;
ScopedSECKEYPublicKey mPublicKey;
ScopedSECKEYPrivateKey mPrivateKey;
nsString mNamedCurve;
virtual void ReleaseNSSResources() MOZ_OVERRIDE
{
mPublicKey.dispose();
mPrivateKey.dispose();
}
virtual nsresult DoCrypto() MOZ_OVERRIDE
{
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
MOZ_ASSERT(slot.get());
void* param;
- ScopedPLArenaPool arena;
-
switch (mMechanism) {
case CKM_RSA_PKCS_KEY_PAIR_GEN:
param = &mRsaParams;
break;
+ case CKM_DH_PKCS_KEY_PAIR_GEN:
+ param = &mDhParams;
+ break;
case CKM_EC_KEY_PAIR_GEN: {
- arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
- if (!arena) {
- return NS_ERROR_DOM_UNKNOWN_ERR;
- }
-
- param = CreateECParamsForCurve(mNamedCurve, arena.get());
+ param = CreateECParamsForCurve(mNamedCurve, mArena);
if (!param) {
return NS_ERROR_DOM_UNKNOWN_ERR;
}
break;
}
default:
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
}
@@ -2763,17 +2793,18 @@ WebCryptoTask::CreateGenerateKeyTask(JSC
algName.EqualsASCII(WEBCRYPTO_ALG_AES_CTR) ||
algName.EqualsASCII(WEBCRYPTO_ALG_AES_GCM) ||
algName.EqualsASCII(WEBCRYPTO_ALG_AES_KW) ||
algName.EqualsASCII(WEBCRYPTO_ALG_HMAC)) {
return new GenerateSymmetricKeyTask(aCx, aAlgorithm, aExtractable, aKeyUsages);
} else if (algName.EqualsASCII(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
algName.EqualsASCII(WEBCRYPTO_ALG_RSA_OAEP) ||
algName.EqualsASCII(WEBCRYPTO_ALG_ECDH) ||
- algName.EqualsASCII(WEBCRYPTO_ALG_ECDSA)) {
+ algName.EqualsASCII(WEBCRYPTO_ALG_ECDSA) ||
+ algName.EqualsASCII(WEBCRYPTO_ALG_DH)) {
return new GenerateAsymmetricKeyTask(aCx, aAlgorithm, aExtractable, aKeyUsages);
} else {
return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
}
}
WebCryptoTask*
WebCryptoTask::CreateDeriveKeyTask(JSContext* aCx,
--- a/dom/crypto/test/mochitest.ini
+++ b/dom/crypto/test/mochitest.ini
@@ -3,12 +3,13 @@
skip-if = (buildapp == 'b2g')
support-files =
test-array.js
test-vectors.js
test_WebCrypto.css
util.js
[test_WebCrypto.html]
+[test_WebCrypto_DH.html]
[test_WebCrypto_ECDH.html]
[test_WebCrypto_JWK.html]
[test_WebCrypto_PBKDF2.html]
[test_WebCrypto_RSA_OAEP.html]
--- a/dom/crypto/test/test-vectors.js
+++ b/dom/crypto/test/test-vectors.js
@@ -652,10 +652,19 @@ tv = {
pub_jwk: {
"kty": "EC",
"crv": "P-521",
"x": "BhOH_WuVkU6IX5Eu37tfsnRlUCfyFsQJHKg-GTNnQP2B" +
"rt_gR_UbQr32gWESEBPg1VsRehTkMD-SbI3rt3p_2q0B",
"y": "AUNouOdGgHsraPNhXNeNdhpGTd15GPyN9R0iWWL98ePc" +
"JD4mUQD/DsEzNZ4zLkTdSa/Y5fOP6GEzVzQy0zwC+goD"
}
- }
+ },
+
+ // RFC 2409 <http://tools.ietf.org/html/rfc2409#section-6.1>
+ dh: {
+ prime: util.hex2abv(
+ "ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74" +
+ "020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f1437" +
+ "4fe1356d6d51c245e485b576625e7ec6f44c42e9a63a3620ffffffffffffffff"
+ )
+ },
}
new file mode 100644
--- /dev/null
+++ b/dom/crypto/test/test_WebCrypto_DH.html
@@ -0,0 +1,92 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+<title>WebCrypto Test Suite</title>
+<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
+<link rel="stylesheet" href="./test_WebCrypto.css"/>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+
+<!-- Utilities for manipulating ABVs -->
+<script src="util.js"></script>
+
+<!-- A simple wrapper around IndexedDB -->
+<script src="simpledb.js"></script>
+
+<!-- Test vectors drawn from the literature -->
+<script src="./test-vectors.js"></script>
+
+<!-- General testing framework -->
+<script src="./test-array.js"></script>
+
+<script>/*<![CDATA[*/
+"use strict";
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+ "Generate a DH key",
+ function() {
+ var that = this;
+ var alg = {
+ name: "DH",
+ prime: tv.dh.prime,
+ generator: new Uint8Array([0x02])
+ };
+ crypto.subtle.generateKey(alg, false, ["deriveKey", "deriveBits"]).then(
+ complete(that, function(x) {
+ return exists(x.publicKey) &&
+ (x.publicKey.algorithm.name == alg.name) &&
+ util.memcmp(x.publicKey.algorithm.prime, alg.prime) &&
+ util.memcmp(x.publicKey.algorithm.generator, alg.generator) &&
+ (x.publicKey.type == "public") &&
+ x.publicKey.extractable &&
+ (x.publicKey.usages.length == 0) &&
+ exists(x.privateKey) &&
+ (x.privateKey.algorithm.name == alg.name) &&
+ util.memcmp(x.privateKey.algorithm.prime, alg.prime) &&
+ util.memcmp(x.privateKey.algorithm.generator, alg.generator) &&
+ (x.privateKey.type == "private") &&
+ !x.privateKey.extractable &&
+ (x.privateKey.usages.length == 2) &&
+ (x.privateKey.usages[0] == "deriveKey") &&
+ (x.privateKey.usages[1] == "deriveBits");
+ }),
+ error(that)
+ );
+ }
+);
+/*]]>*/</script>
+</head>
+
+<body>
+
+<div id="content">
+ <div id="head">
+ <b>Web</b>Crypto<br>
+ </div>
+
+ <div id="start" onclick="start();">RUN ALL</div>
+
+ <div id="resultDiv" class="content">
+ Summary:
+ <span class="pass"><span id="passN">0</span> passed, </span>
+ <span class="fail"><span id="failN">0</span> failed, </span>
+ <span class="pending"><span id="pendingN">0</span> pending.</span>
+ <br/>
+ <br/>
+
+ <table id="results">
+ <tr>
+ <th>Test</th>
+ <th>Result</th>
+ <th>Time</th>
+ </tr>
+ </table>
+
+ </div>
+
+ <div id="foot"></div>
+</div>
+
+</body>
+</html>