Bug 1034855 - Implement deriveBits() for ECDH r=rbarnes,smaug

This commit is contained in:
Tim Taubert 2014-07-29 11:11:26 +02:00
parent 29966dbb81
commit 0dfe8e631b
3 changed files with 221 additions and 0 deletions

View File

@ -2283,6 +2283,116 @@ private:
}
};
class DeriveEcdhBitsTask : public ReturnArrayBufferViewTask
{
public:
DeriveEcdhBitsTask(JSContext* aCx,
const ObjectOrString& aAlgorithm, CryptoKey& aKey, uint32_t aLength)
: mLength(aLength),
mPrivKey(aKey.GetPrivateKey())
{
Init(aCx, aAlgorithm, aKey);
}
DeriveEcdhBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
CryptoKey& aKey, const ObjectOrString& aTargetAlgorithm)
: mPrivKey(aKey.GetPrivateKey())
{
mEarlyRv = GetKeySizeForAlgorithm(aCx, aTargetAlgorithm, mLength);
if (NS_SUCCEEDED(mEarlyRv)) {
Init(aCx, aAlgorithm, aKey);
}
}
void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey)
{
// Check that we have a private key.
if (!mPrivKey) {
mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
return;
}
// Length must be a multiple of 8 bigger than zero.
if (mLength == 0 || mLength % 8) {
mEarlyRv = NS_ERROR_DOM_DATA_ERR;
return;
}
mLength = mLength >> 3; // bits to bytes
// Retrieve the peer's public key.
RootedDictionary<EcdhKeyDeriveParams> params(aCx);
mEarlyRv = Coerce(aCx, params, aAlgorithm);
if (NS_FAILED(mEarlyRv) || !params.mPublic.WasPassed()) {
mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
return;
}
CryptoKey* publicKey = params.mPublic.Value();
mPubKey = publicKey->GetPublicKey();
if (!mPubKey) {
mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
return;
}
nsRefPtr<KeyAlgorithm> publicAlgorithm = publicKey->Algorithm();
// Given public key must be an ECDH key.
nsString algName;
publicAlgorithm->GetName(algName);
if (!algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
return;
}
// Both keys must use the same named curve.
nsString curve1, curve2;
static_cast<EcKeyAlgorithm*>(aKey.Algorithm())->GetNamedCurve(curve1);
static_cast<EcKeyAlgorithm*>(publicAlgorithm.get())->GetNamedCurve(curve2);
if (!curve1.Equals(curve2)) {
mEarlyRv = NS_ERROR_DOM_DATA_ERR;
return;
}
}
private:
size_t mLength;
ScopedSECKEYPrivateKey mPrivKey;
ScopedSECKEYPublicKey mPubKey;
virtual nsresult DoCrypto() MOZ_OVERRIDE
{
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));
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;
}
};
template<class KeyEncryptTask>
class WrapKeyTask : public ExportKeyTask
{
@ -2566,6 +2676,10 @@ WebCryptoTask::CreateDeriveBitsTask(JSContext* aCx,
return new DerivePbkdfBitsTask(aCx, aAlgorithm, aKey, aLength);
}
if (algName.EqualsASCII(WEBCRYPTO_ALG_ECDH)) {
return new DeriveEcdhBitsTask(aCx, aAlgorithm, aKey, aLength);
}
return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
}

View File

@ -1895,3 +1895,106 @@ TestArray.addTest(
);
}
);
// -----------------------------------------------------------------------------
TestArray.addTest(
"Generate an ECDH key and derive some bits",
function() {
var that = this;
var alg = { name: "ECDH", namedCurve: "P-256" };
var pair;
function setKeyPair(x) { pair = x; }
function doDerive(n) {
return function (x) {
var alg = { name: "ECDH", public: pair.publicKey };
return crypto.subtle.deriveBits(alg, pair.privateKey, n * 8);
}
}
crypto.subtle.generateKey(alg, false, ["deriveBits"])
.then(setKeyPair, error(that))
.then(doDerive(2), error(that))
.then(function (x) {
// Deriving less bytes works.
if (x.byteLength != 2) {
throw "should have derived two bytes";
}
})
// Deriving more than the curve yields doesn't.
.then(doDerive(33), error(that))
.then(error(that), complete(that));
}
);
// -----------------------------------------------------------------------------
TestArray.addTest(
"Test that ECDH deriveBits() fails when the public key is not an ECDH key",
function() {
var that = this;
var pubKey, privKey;
function setPub(x) { pubKey = x.publicKey; }
function setPriv(x) { privKey = x.privateKey; }
function doGenerateP256() {
var alg = { name: "ECDH", namedCurve: "P-256" };
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: "ECDH", public: pubKey };
return crypto.subtle.deriveBits(alg, privKey, 16);
}
doGenerateP256()
.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 ECDH deriveBits() fails when the given keys' curves don't match",
function() {
var that = this;
var pubKey, privKey;
function setPub(x) { pubKey = x.publicKey; }
function setPriv(x) { privKey = x.privateKey; }
function doGenerateP256() {
var alg = { name: "ECDH", namedCurve: "P-256" };
return crypto.subtle.generateKey(alg, false, ["deriveBits"]);
}
function doGenerateP384() {
var alg = { name: "ECDH", namedCurve: "P-384" };
return crypto.subtle.generateKey(alg, false, ["deriveBits"]);
}
function doDerive() {
var alg = { name: "ECDH", public: pubKey };
return crypto.subtle.deriveBits(alg, privKey, 16);
}
doGenerateP256()
.then(setPriv, error(that))
.then(doGenerateP384, error(that))
.then(setPub, error(that))
.then(doDerive, error(that))
.then(error(that), complete(that));
}
);

View File

@ -114,6 +114,10 @@ dictionary EcKeyGenParams : Algorithm {
NamedCurve namedCurve;
};
dictionary EcdhKeyDeriveParams : Algorithm {
CryptoKey public;
};
/***** JWK *****/