mirror of
https://github.com/denoland/deno.git
synced 2024-12-23 07:44:48 -05:00
4fa8869f24
This completely rewrites how we handle key material in ext/node. Changes in this PR: - **Signing** - RSA - RSA-PSS 🆕 - DSA 🆕 - EC - ED25519 🆕 - **Verifying** - RSA - RSA-PSS 🆕 - DSA 🆕 - EC 🆕 - ED25519 🆕 - **Private key import** - Passphrase encrypted private keys 🆕 - RSA - PEM - DER (PKCS#1) 🆕 - DER (PKCS#8) 🆕 - RSA-PSS - PEM - DER (PKCS#1) 🆕 - DER (PKCS#8) 🆕 - DSA 🆕 - EC - PEM - DER (SEC1) 🆕 - DER (PKCS#8) 🆕 - X25519 🆕 - ED25519 🆕 - DH - **Public key import** - RSA - PEM - DER (PKCS#1) 🆕 - DER (PKCS#8) 🆕 - RSA-PSS 🆕 - DSA 🆕 - EC 🆕 - X25519 🆕 - ED25519 🆕 - DH 🆕 - **Private key export** - RSA 🆕 - DSA 🆕 - EC 🆕 - X25519 🆕 - ED25519 🆕 - DH 🆕 - **Public key export** - RSA - DSA 🆕 - EC 🆕 - X25519 🆕 - ED25519 🆕 - DH 🆕 - **Key pair generation** - Overhauled, but supported APIs unchanged This PR adds a lot of new individual functionality. But most importantly because of the new key material representation, it is now trivial to add new algorithms (as shown by this PR). Now, when adding a new algorithm, it is also widely supported - for example previously we supported ED25519 key pair generation, but we could not import, export, sign or verify with ED25519. We can now do all of those things.
149 lines
3.2 KiB
TypeScript
149 lines
3.2 KiB
TypeScript
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
|
// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license.
|
|
|
|
// TODO(petamoriken): enable prefer-primordials for node polyfills
|
|
// deno-lint-ignore-file prefer-primordials
|
|
|
|
import { op_node_hkdf, op_node_hkdf_async } from "ext:core/ops";
|
|
|
|
import {
|
|
validateFunction,
|
|
validateInteger,
|
|
validateString,
|
|
} from "ext:deno_node/internal/validators.mjs";
|
|
import {
|
|
ERR_CRYPTO_INVALID_DIGEST,
|
|
ERR_INVALID_ARG_TYPE,
|
|
ERR_OUT_OF_RANGE,
|
|
hideStackFrames,
|
|
} from "ext:deno_node/internal/errors.ts";
|
|
import {
|
|
kHandle,
|
|
toBuf,
|
|
validateByteSource,
|
|
} from "ext:deno_node/internal/crypto/util.ts";
|
|
import {
|
|
createSecretKey,
|
|
KeyObject,
|
|
} from "ext:deno_node/internal/crypto/keys.ts";
|
|
import type { BinaryLike } from "ext:deno_node/internal/crypto/types.ts";
|
|
import { kMaxLength } from "ext:deno_node/internal/buffer.mjs";
|
|
import {
|
|
isAnyArrayBuffer,
|
|
isArrayBufferView,
|
|
} from "ext:deno_node/internal/util/types.ts";
|
|
import { isKeyObject } from "ext:deno_node/internal/crypto/_keys.ts";
|
|
|
|
const validateParameters = hideStackFrames((hash, key, salt, info, length) => {
|
|
validateString(hash, "digest");
|
|
key = prepareKey(key);
|
|
validateByteSource(salt, "salt");
|
|
validateByteSource(info, "info");
|
|
|
|
salt = new Uint8Array(toBuf(salt));
|
|
info = new Uint8Array(toBuf(info));
|
|
|
|
validateInteger(length, "length", 0, kMaxLength);
|
|
|
|
if (info.byteLength > 1024) {
|
|
throw new ERR_OUT_OF_RANGE(
|
|
"info",
|
|
"must not contain more than 1024 bytes",
|
|
info.byteLength,
|
|
);
|
|
}
|
|
|
|
return {
|
|
hash,
|
|
key,
|
|
salt,
|
|
info,
|
|
length,
|
|
};
|
|
});
|
|
|
|
function prepareKey(key: BinaryLike | KeyObject) {
|
|
if (isKeyObject(key)) {
|
|
return key;
|
|
}
|
|
|
|
if (isAnyArrayBuffer(key)) {
|
|
return createSecretKey(new Uint8Array(key as unknown as ArrayBufferLike));
|
|
}
|
|
|
|
key = toBuf(key as string);
|
|
|
|
if (!isArrayBufferView(key)) {
|
|
throw new ERR_INVALID_ARG_TYPE(
|
|
"ikm",
|
|
[
|
|
"string",
|
|
"SecretKeyObject",
|
|
"ArrayBuffer",
|
|
"TypedArray",
|
|
"DataView",
|
|
"Buffer",
|
|
],
|
|
key,
|
|
);
|
|
}
|
|
|
|
return createSecretKey(key);
|
|
}
|
|
|
|
export function hkdf(
|
|
hash: string,
|
|
key: BinaryLike | KeyObject,
|
|
salt: BinaryLike,
|
|
info: BinaryLike,
|
|
length: number,
|
|
callback: (err: Error | null, derivedKey: ArrayBuffer | undefined) => void,
|
|
) {
|
|
({ hash, key, salt, info, length } = validateParameters(
|
|
hash,
|
|
key,
|
|
salt,
|
|
info,
|
|
length,
|
|
));
|
|
|
|
validateFunction(callback, "callback");
|
|
|
|
hash = hash.toLowerCase();
|
|
|
|
op_node_hkdf_async(hash, key[kHandle], salt, info, length)
|
|
.then((okm) => callback(null, okm.buffer))
|
|
.catch((err) => callback(new ERR_CRYPTO_INVALID_DIGEST(err), undefined));
|
|
}
|
|
|
|
export function hkdfSync(
|
|
hash: string,
|
|
key: BinaryLike | KeyObject,
|
|
salt: BinaryLike,
|
|
info: BinaryLike,
|
|
length: number,
|
|
) {
|
|
({ hash, key, salt, info, length } = validateParameters(
|
|
hash,
|
|
key,
|
|
salt,
|
|
info,
|
|
length,
|
|
));
|
|
|
|
hash = hash.toLowerCase();
|
|
|
|
const okm = new Uint8Array(length);
|
|
try {
|
|
op_node_hkdf(hash, key[kHandle], salt, info, okm);
|
|
} catch (e) {
|
|
throw new ERR_CRYPTO_INVALID_DIGEST(e);
|
|
}
|
|
|
|
return okm.buffer;
|
|
}
|
|
|
|
export default {
|
|
hkdf,
|
|
hkdfSync,
|
|
};
|