1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-21 15:04:11 -05:00

feat(ext/crypto): implement deriveKey (#12117)

This commit is contained in:
Divy Srivastava 2021-10-12 16:09:46 +05:30 committed by GitHub
parent f332d72f16
commit 58f04d8e46
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 250 additions and 10917 deletions

View file

@ -513,6 +513,42 @@ unitTest(async function testHkdfDeriveBits() {
assertEquals(result.byteLength, 128 / 8);
});
unitTest(async function testDeriveKey() {
// Test deriveKey
const rawKey = await crypto.getRandomValues(new Uint8Array(16));
const key = await crypto.subtle.importKey(
"raw",
rawKey,
"PBKDF2",
false,
["deriveKey", "deriveBits"],
);
const salt = await crypto.getRandomValues(new Uint8Array(16));
const derivedKey = await crypto.subtle.deriveKey(
{
name: "PBKDF2",
salt,
iterations: 1000,
hash: "SHA-256",
},
key,
{ name: "HMAC", hash: "SHA-256" },
true,
["sign"],
);
assert(derivedKey instanceof CryptoKey);
assertEquals(derivedKey.type, "secret");
assertEquals(derivedKey.extractable, true);
assertEquals(derivedKey.usages, ["sign"]);
const algorithm = derivedKey.algorithm as HmacKeyAlgorithm;
assertEquals(algorithm.name, "HMAC");
assertEquals(algorithm.hash.name, "SHA-256");
assertEquals(algorithm.length, 256);
});
unitTest(async function testAesCbcEncryptDecrypt() {
const key = await crypto.subtle.generateKey(
{ name: "AES-CBC", length: 128 },

View file

@ -27,6 +27,7 @@
StringFromCharCode,
Symbol,
SymbolFor,
SyntaxError,
WeakMap,
WeakMapPrototypeGet,
WeakMapPrototypeSet,
@ -123,6 +124,14 @@
"RSA-OAEP": "RsaOaepParams",
"AES-CBC": "AesCbcParams",
},
"get key length": {
"AES-CBC": "AesDerivedKeyParams",
"AES-GCM": "AesDerivedKeyParams",
"AES-KW": "AesDerivedKeyParams",
"HMAC": "HmacImportParams",
"HKDF": null,
"PBKDF2": null,
},
"wrapKey": {
// TODO(@littledivy): Enable this once implemented.
// "AES-KW": "AesKeyWrapParams",
@ -322,6 +331,67 @@
/** @type {WeakMap<object, object>} */
const KEY_STORE = new WeakMap();
function getKeyLength(algorithm) {
switch (algorithm.name) {
case "AES-CBC":
case "AES-GCM":
case "AES-KW": {
// 1.
if (!ArrayPrototypeIncludes([128, 192, 256], algorithm.length)) {
throw new DOMException(
"length must be 128, 192, or 256",
"OperationError",
);
}
// 2.
return algorithm.length;
}
case "HMAC": {
// 1.
let length;
if (algorithm.length === undefined) {
switch (algorithm.hash.name) {
case "SHA-1":
length = 160;
break;
case "SHA-256":
length = 256;
break;
case "SHA-384":
length = 384;
break;
case "SHA-512":
length = 512;
break;
default:
throw new DOMException(
"Unrecognized hash algorithm",
"NotSupportedError",
);
}
} else if (algorithm.length !== 0) {
length = algorithm.length;
} else {
throw new TypeError("Invalid length.");
}
// 2.
return length;
}
case "HKDF": {
// 1.
return null;
}
case "PBKDF2": {
// 1.
return null;
}
default:
throw new TypeError("unreachable");
}
}
class SubtleCrypto {
constructor() {
webidl.illegalConstructor();
@ -1574,19 +1644,119 @@
const result = await deriveBits(normalizedAlgorithm, baseKey, length);
// 7.
if (normalizedAlgorithm.name !== baseKey[_algorithm].name) {
throw new DOMException("InvalidAccessError", "Invalid algorithm name");
throw new DOMException("Invalid algorithm name", "InvalidAccessError");
}
// 8.
if (!ArrayPrototypeIncludes(baseKey[_usages], "deriveBits")) {
throw new DOMException(
"InvalidAccessError",
"baseKey usages does not contain `deriveBits`",
"InvalidAccessError",
);
}
// 9-10.
return result;
}
/**
* @param {AlgorithmIdentifier} algorithm
* @param {CryptoKey} baseKey
* @param {number} length
* @returns {Promise<ArrayBuffer>}
*/
async deriveKey(
algorithm,
baseKey,
derivedKeyType,
extractable,
keyUsages,
) {
webidl.assertBranded(this, SubtleCrypto);
const prefix = "Failed to execute 'deriveKey' on 'SubtleCrypto'";
webidl.requiredArguments(arguments.length, 5, { prefix });
algorithm = webidl.converters.AlgorithmIdentifier(algorithm, {
prefix,
context: "Argument 1",
});
baseKey = webidl.converters.CryptoKey(baseKey, {
prefix,
context: "Argument 2",
});
derivedKeyType = webidl.converters.AlgorithmIdentifier(derivedKeyType, {
prefix,
context: "Argument 3",
});
extractable = webidl.converters["boolean"](extractable, {
prefix,
context: "Argument 4",
});
keyUsages = webidl.converters["sequence<KeyUsage>"](keyUsages, {
prefix,
context: "Argument 5",
});
// 2-3.
const normalizedAlgorithm = normalizeAlgorithm(algorithm, "deriveBits");
// 4-5.
const normalizedDerivedKeyAlgorithmImport = normalizeAlgorithm(
derivedKeyType,
"importKey",
);
// 6-7.
const normalizedDerivedKeyAlgorithmLength = normalizeAlgorithm(
derivedKeyType,
"get key length",
);
// 8-10.
// 11.
if (normalizedAlgorithm.name !== baseKey[_algorithm].name) {
throw new DOMException(
"Invalid algorithm name",
"InvalidAccessError",
);
}
// 12.
if (!ArrayPrototypeIncludes(baseKey[_usages], "deriveKey")) {
throw new DOMException(
"baseKey usages does not contain `deriveKey`",
"InvalidAccessError",
);
}
// 13.
const length = getKeyLength(normalizedDerivedKeyAlgorithmLength);
// 14.
const secret = await this.deriveBits(
normalizedAlgorithm,
baseKey,
length,
);
// 15.
const result = await this.importKey(
"raw",
secret,
normalizedDerivedKeyAlgorithmImport,
extractable,
keyUsages,
);
// 16.
if (
ArrayPrototypeIncludes(["private", "secret"], result[_type]) &&
keyUsages.length == 0
) {
throw new SyntaxError("Invalid key usages");
}
// 17.
return result;
}
/**
* @param {string} algorithm
* @param {CryptoKey} key

View file

@ -367,6 +367,16 @@
webidl.converters.Pbkdf2Params = webidl
.createDictionaryConverter("Pbkdf2Params", dictPbkdf2Params);
const dictAesDerivedKeyParams = [
...dictAlgorithm,
{
key: "length",
converter: (V, opts) =>
webidl.converters["unsigned long"](V, { ...opts, enforceRange: true }),
required: true,
},
];
const dictAesCbcParams = [
...dictAlgorithm,
{
@ -376,6 +386,9 @@
},
];
webidl.converters.AesDerivedKeyParams = webidl
.createDictionaryConverter("AesDerivedKeyParams", dictAesDerivedKeyParams);
webidl.converters.AesCbcParams = webidl
.createDictionaryConverter("AesCbcParams", dictAesCbcParams);

View file

@ -129,6 +129,10 @@ interface Pbkdf2Params extends Algorithm {
salt: BufferSource;
}
interface AesDerivedKeyParams extends Algorithm {
length: number;
}
interface EcdhKeyDeriveParams extends Algorithm {
public: CryptoKey;
}
@ -235,6 +239,18 @@ interface SubtleCrypto {
baseKey: CryptoKey,
length: number,
): Promise<ArrayBuffer>;
deriveKey(
algorithm: AlgorithmIdentifier | HkdfParams | Pbkdf2Params,
baseKey: CryptoKey,
derivedKeyType:
| AlgorithmIdentifier
| AesDerivedKeyParams
| HmacImportParams
| HkdfParams
| Pbkdf2Params,
extractable: boolean,
keyUsages: KeyUsage[],
): Promise<CryptoKey>;
wrapKey(
format: KeyFormat,
key: CryptoKey,

File diff suppressed because it is too large Load diff