mirror of
https://github.com/denoland/deno.git
synced 2024-12-25 08:39:09 -05:00
fix(ext/node): implement crypto.createVerify (#18703)
This commit is contained in:
parent
b0b0594767
commit
5f44396a9e
6 changed files with 188 additions and 49 deletions
|
@ -236,6 +236,7 @@
|
||||||
"test-crypto-hmac.js",
|
"test-crypto-hmac.js",
|
||||||
"test-crypto-prime.js",
|
"test-crypto-prime.js",
|
||||||
"test-crypto-secret-keygen.js",
|
"test-crypto-secret-keygen.js",
|
||||||
|
"test-crypto-update-encoding.js",
|
||||||
"test-crypto-x509.js",
|
"test-crypto-x509.js",
|
||||||
"test-dgram-close-during-bind.js",
|
"test-dgram-close-during-bind.js",
|
||||||
"test-dgram-close-signal.js",
|
"test-dgram-close-signal.js",
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
// deno-fmt-ignore-file
|
||||||
|
// deno-lint-ignore-file
|
||||||
|
|
||||||
|
// Copyright Joyent and Node contributors. All rights reserved. MIT license.
|
||||||
|
// Taken from Node 18.12.1
|
||||||
|
// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
const common = require('../common');
|
||||||
|
|
||||||
|
if (!common.hasCrypto)
|
||||||
|
common.skip('missing crypto');
|
||||||
|
|
||||||
|
const crypto = require('crypto');
|
||||||
|
|
||||||
|
const zeros = Buffer.alloc;
|
||||||
|
const key = zeros(16);
|
||||||
|
const iv = zeros(16);
|
||||||
|
|
||||||
|
const cipher = () => crypto.createCipheriv('aes-128-cbc', key, iv);
|
||||||
|
const decipher = () => crypto.createDecipheriv('aes-128-cbc', key, iv);
|
||||||
|
const hash = () => crypto.createSign('sha256');
|
||||||
|
const hmac = () => crypto.createHmac('sha256', key);
|
||||||
|
const sign = () => crypto.createSign('sha256');
|
||||||
|
const verify = () => crypto.createVerify('sha256');
|
||||||
|
|
||||||
|
for (const f of [cipher, decipher, hash, hmac, sign, verify])
|
||||||
|
for (const n of [15, 16])
|
||||||
|
f().update(zeros(n), 'hex'); // Should ignore inputEncoding.
|
|
@ -1,7 +1,10 @@
|
||||||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
import { assertEquals } from "../../../test_util/std/testing/asserts.ts";
|
import {
|
||||||
import { createSign } from "node:crypto";
|
assert,
|
||||||
|
assertEquals,
|
||||||
|
} from "../../../test_util/std/testing/asserts.ts";
|
||||||
|
import { createSign, createVerify } from "node:crypto";
|
||||||
import { Buffer } from "node:buffer";
|
import { Buffer } from "node:buffer";
|
||||||
|
|
||||||
const rsaPrivatePem = Buffer.from(
|
const rsaPrivatePem = Buffer.from(
|
||||||
|
@ -9,38 +12,61 @@ const rsaPrivatePem = Buffer.from(
|
||||||
new URL("./testdata/rsa_private.pem", import.meta.url),
|
new URL("./testdata/rsa_private.pem", import.meta.url),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
const rsaPublicPem = Buffer.from(
|
||||||
|
await Deno.readFile(
|
||||||
|
new URL("./testdata/rsa_public.pem", import.meta.url),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
const table = [
|
||||||
|
{
|
||||||
|
algorithms: ["sha224", "RSA-SHA224"],
|
||||||
|
signature:
|
||||||
|
"7ad162b288bd7f4ba9b8a31295ad4136d143a5fd11eb99a72379dc9b53e3e8b5c1b7c9dd8a3864a1f626d921e550c48056982bd8fe7e75333885311b5515de1ecbbfcc6a1dd930f422dff87bfceb7eb38882ac6b4fd9dea9efd462776775976e81b1d677f8db41f5ac8686abfa9838069125be939c59e404aa50550872d84befb8b5f6ce2dd051c62a8ba268f876b6f17a27af43b79938222e4ab8b90c4f5540d0f8b02508ef3e68279d685746956b924f00c92438b7981a3cfcb1e2a97305402d381ea62aeaa803f8707961bc3e10a258352e210772e9846ca4024e3dc0a956a50d6db1c03d2943826cc98c6f36d7bafacf1c94b6c438c7664c300a3be172b1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
algorithms: ["sha256", "RSA-SHA256"],
|
||||||
|
signature:
|
||||||
|
"080313284d7398e1e0e27f6e44f198ceecedddc801e81af63a867d9245ad744e29018099c9ac3c27061c33cabfe27af1db38f44bac09cdcd2c4ab3b00a2a3020f68368f2239db5f911a2dbb7ea2dee322ca7d26d0c88d197482ca4aa1c29ac87b9e6c20075dc974ae71d2d76d2a5b2a15bd541033519465c3aea815cc73b0f1c3ffeedcfb93d6788416623789f86786870d23e86b982ab0df157d7a596097bd3cca3e752f3f47eff4b83754296868b52bc8ff741492dc8a401fe6dc035569e45d1fa1a71c8988d3aadce68fb1bf5c3e756c586af20c8e75c037436ff4c8389e6ce9d943ef7e2566977b84577272181fcec403077cc29e7db1166fff900b36a1d",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
algorithms: ["sha384", "RSA-SHA384"],
|
||||||
|
signature:
|
||||||
|
"2f77a5b7ac0168efd652c30ecb082075f3de30629e9c1f51b7e7e671f24b5c3a2606bb72159a217438220fc7aaba887d4b817e3f43fe0cc8f840747368df8cd65ec760c21a3f9296d01caedc80a335030e31d31ac451277fc4bcc1679c168b2c3185dfee21286514113c080af5238a61a677b03777344f476f25053108588aa6bdc02a6138c6b59a20de4d11e3d668482f17e748e75747f83c0512206283acfc64ed0ad963dddc9ec24589cfd459ee806b8e0e67b93cea16651e967762a5deef890f438ffb9db39247469289db06e2ed7fe262aa1df4ab9607e5b5219a17ddc9694283a61bf8643f58fd702f2c5d3b2d53dc7f36bb5e96461174d376950d6d19",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
algorithms: ["sha512", "RSA-SHA512"],
|
||||||
|
signature:
|
||||||
|
"072e20a433f255ab2f7e5e9ce69255d5c6d7c15a36af75c8389b9672c41abc6a9532fbd057d9d64270bb2483d3c9923f8f419fba4b59b838dcda82a1322009d245c06e2802a74febaea9cebc0b7f46f8761331c5f52ffb650245b5aefefcc604f209b44f6560fe45370cb239d236622e5f72fbb45377f08a0c733e16a8f15830897679ad4349d2e2e5e50a99796820302f4f47881ed444aede56a6d3330b71acaefc4218ae2e4a3bdfbb0c9432ffc5e5bac8c168278b2205d68a5d6905ccbb91282d519c11eccca52d42c86787de492b2a89679dce98cd14c37b0c183af8427e7a1ec86b1ed3f9b5bebf83f1ef81eb18748e69c716a0f263a8598fe627158647",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
Deno.test({
|
Deno.test({
|
||||||
name: "crypto.Sign - RSA PEM with SHA224, SHA256, SHA384, SHA512 digests",
|
name: "crypto.Sign - RSA PEM with SHA224, SHA256, SHA384, SHA512 digests",
|
||||||
fn() {
|
fn() {
|
||||||
const table = [
|
|
||||||
{
|
|
||||||
algorithms: ["sha224", "RSA-SHA224"],
|
|
||||||
expected:
|
|
||||||
"7ad162b288bd7f4ba9b8a31295ad4136d143a5fd11eb99a72379dc9b53e3e8b5c1b7c9dd8a3864a1f626d921e550c48056982bd8fe7e75333885311b5515de1ecbbfcc6a1dd930f422dff87bfceb7eb38882ac6b4fd9dea9efd462776775976e81b1d677f8db41f5ac8686abfa9838069125be939c59e404aa50550872d84befb8b5f6ce2dd051c62a8ba268f876b6f17a27af43b79938222e4ab8b90c4f5540d0f8b02508ef3e68279d685746956b924f00c92438b7981a3cfcb1e2a97305402d381ea62aeaa803f8707961bc3e10a258352e210772e9846ca4024e3dc0a956a50d6db1c03d2943826cc98c6f36d7bafacf1c94b6c438c7664c300a3be172b1",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
algorithms: ["sha256", "RSA-SHA256"],
|
|
||||||
expected:
|
|
||||||
"080313284d7398e1e0e27f6e44f198ceecedddc801e81af63a867d9245ad744e29018099c9ac3c27061c33cabfe27af1db38f44bac09cdcd2c4ab3b00a2a3020f68368f2239db5f911a2dbb7ea2dee322ca7d26d0c88d197482ca4aa1c29ac87b9e6c20075dc974ae71d2d76d2a5b2a15bd541033519465c3aea815cc73b0f1c3ffeedcfb93d6788416623789f86786870d23e86b982ab0df157d7a596097bd3cca3e752f3f47eff4b83754296868b52bc8ff741492dc8a401fe6dc035569e45d1fa1a71c8988d3aadce68fb1bf5c3e756c586af20c8e75c037436ff4c8389e6ce9d943ef7e2566977b84577272181fcec403077cc29e7db1166fff900b36a1d",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
algorithms: ["sha384", "RSA-SHA384"],
|
|
||||||
expected:
|
|
||||||
"2f77a5b7ac0168efd652c30ecb082075f3de30629e9c1f51b7e7e671f24b5c3a2606bb72159a217438220fc7aaba887d4b817e3f43fe0cc8f840747368df8cd65ec760c21a3f9296d01caedc80a335030e31d31ac451277fc4bcc1679c168b2c3185dfee21286514113c080af5238a61a677b03777344f476f25053108588aa6bdc02a6138c6b59a20de4d11e3d668482f17e748e75747f83c0512206283acfc64ed0ad963dddc9ec24589cfd459ee806b8e0e67b93cea16651e967762a5deef890f438ffb9db39247469289db06e2ed7fe262aa1df4ab9607e5b5219a17ddc9694283a61bf8643f58fd702f2c5d3b2d53dc7f36bb5e96461174d376950d6d19",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
algorithms: ["sha512", "RSA-SHA512"],
|
|
||||||
expected:
|
|
||||||
"072e20a433f255ab2f7e5e9ce69255d5c6d7c15a36af75c8389b9672c41abc6a9532fbd057d9d64270bb2483d3c9923f8f419fba4b59b838dcda82a1322009d245c06e2802a74febaea9cebc0b7f46f8761331c5f52ffb650245b5aefefcc604f209b44f6560fe45370cb239d236622e5f72fbb45377f08a0c733e16a8f15830897679ad4349d2e2e5e50a99796820302f4f47881ed444aede56a6d3330b71acaefc4218ae2e4a3bdfbb0c9432ffc5e5bac8c168278b2205d68a5d6905ccbb91282d519c11eccca52d42c86787de492b2a89679dce98cd14c37b0c183af8427e7a1ec86b1ed3f9b5bebf83f1ef81eb18748e69c716a0f263a8598fe627158647",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const testCase of table) {
|
for (const testCase of table) {
|
||||||
for (const algorithm of testCase.algorithms) {
|
for (const algorithm of testCase.algorithms) {
|
||||||
const signature = createSign(algorithm).update("some data to sign")
|
const signature = createSign(algorithm)
|
||||||
|
.update("some data to sign")
|
||||||
.sign(rsaPrivatePem, "hex");
|
.sign(rsaPrivatePem, "hex");
|
||||||
assertEquals(signature, testCase.expected);
|
assertEquals(signature, testCase.signature);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.test({
|
||||||
|
name: "crypto.Verify - RSA PEM with SHA224, SHA256, SHA384, SHA512 digests",
|
||||||
|
fn() {
|
||||||
|
for (const testCase of table) {
|
||||||
|
for (const algorithm of testCase.algorithms) {
|
||||||
|
assert(
|
||||||
|
createVerify(algorithm).update("some data to sign").verify(
|
||||||
|
rsaPublicPem,
|
||||||
|
testCase.signature,
|
||||||
|
"hex",
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -309,7 +309,7 @@ pub fn op_node_sign(
|
||||||
use signature::hazmat::PrehashSigner;
|
use signature::hazmat::PrehashSigner;
|
||||||
let key = match key_format {
|
let key = match key_format {
|
||||||
"pem" => RsaPrivateKey::from_pkcs8_pem((&key).try_into()?)
|
"pem" => RsaPrivateKey::from_pkcs8_pem((&key).try_into()?)
|
||||||
.map_err(|_| type_error("Invalid RSA key"))?,
|
.map_err(|_| type_error("Invalid RSA private key"))?,
|
||||||
// TODO(kt3k): Support der and jwk formats
|
// TODO(kt3k): Support der and jwk formats
|
||||||
_ => {
|
_ => {
|
||||||
return Err(type_error(format!(
|
return Err(type_error(format!(
|
||||||
|
@ -353,6 +353,58 @@ pub fn op_node_sign(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[op]
|
||||||
|
fn op_node_verify(
|
||||||
|
digest: &[u8],
|
||||||
|
digest_type: &str,
|
||||||
|
key: StringOrBuffer,
|
||||||
|
key_type: &str,
|
||||||
|
key_format: &str,
|
||||||
|
signature: &[u8],
|
||||||
|
) -> Result<bool, AnyError> {
|
||||||
|
match key_type {
|
||||||
|
"rsa" => {
|
||||||
|
use rsa::pkcs1v15::VerifyingKey;
|
||||||
|
use signature::hazmat::PrehashVerifier;
|
||||||
|
let key = match key_format {
|
||||||
|
"pem" => RsaPublicKey::from_public_key_pem((&key).try_into()?)
|
||||||
|
.map_err(|_| type_error("Invalid RSA public key"))?,
|
||||||
|
// TODO(kt3k): Support der and jwk formats
|
||||||
|
_ => {
|
||||||
|
return Err(type_error(format!(
|
||||||
|
"Unsupported key format: {}",
|
||||||
|
key_format
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(match digest_type {
|
||||||
|
"sha224" => VerifyingKey::<sha2::Sha224>::new_with_prefix(key)
|
||||||
|
.verify_prehash(digest, &signature.to_vec().try_into()?)
|
||||||
|
.is_ok(),
|
||||||
|
"sha256" => VerifyingKey::<sha2::Sha256>::new_with_prefix(key)
|
||||||
|
.verify_prehash(digest, &signature.to_vec().try_into()?)
|
||||||
|
.is_ok(),
|
||||||
|
"sha384" => VerifyingKey::<sha2::Sha384>::new_with_prefix(key)
|
||||||
|
.verify_prehash(digest, &signature.to_vec().try_into()?)
|
||||||
|
.is_ok(),
|
||||||
|
"sha512" => VerifyingKey::<sha2::Sha512>::new_with_prefix(key)
|
||||||
|
.verify_prehash(digest, &signature.to_vec().try_into()?)
|
||||||
|
.is_ok(),
|
||||||
|
_ => {
|
||||||
|
return Err(type_error(format!(
|
||||||
|
"Unknown digest algorithm: {}",
|
||||||
|
digest_type
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => Err(type_error(format!(
|
||||||
|
"Verifying with {} keys is not supported yet",
|
||||||
|
key_type
|
||||||
|
))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn pbkdf2_sync(
|
fn pbkdf2_sync(
|
||||||
password: &[u8],
|
password: &[u8],
|
||||||
salt: &[u8],
|
salt: &[u8],
|
||||||
|
|
|
@ -194,6 +194,7 @@ deno_core::extension!(deno_node,
|
||||||
crypto::op_node_generate_secret,
|
crypto::op_node_generate_secret,
|
||||||
crypto::op_node_generate_secret_async,
|
crypto::op_node_generate_secret_async,
|
||||||
crypto::op_node_sign,
|
crypto::op_node_sign,
|
||||||
|
crypto::op_node_verify,
|
||||||
crypto::op_node_random_int,
|
crypto::op_node_random_int,
|
||||||
crypto::x509::op_node_x509_parse,
|
crypto::x509::op_node_x509_parse,
|
||||||
crypto::x509::op_node_x509_ca,
|
crypto::x509::op_node_x509_ca,
|
||||||
|
|
|
@ -67,7 +67,7 @@ export class Sign extends Writable {
|
||||||
}
|
}
|
||||||
|
|
||||||
sign(
|
sign(
|
||||||
privateKey: KeyLike | SignKeyObjectInput | SignPrivateKeyInput,
|
privateKey: BinaryLike | SignKeyObjectInput | SignPrivateKeyInput,
|
||||||
encoding?: BinaryToTextEncoding,
|
encoding?: BinaryToTextEncoding,
|
||||||
): Buffer | string {
|
): Buffer | string {
|
||||||
let keyData: Uint8Array;
|
let keyData: Uint8Array;
|
||||||
|
@ -75,7 +75,8 @@ export class Sign extends Writable {
|
||||||
let keyFormat: KeyFormat;
|
let keyFormat: KeyFormat;
|
||||||
if (typeof privateKey === "string" || isArrayBufferView(privateKey)) {
|
if (typeof privateKey === "string" || isArrayBufferView(privateKey)) {
|
||||||
// if the key is BinaryLike, interpret it as a PEM encoded RSA key
|
// if the key is BinaryLike, interpret it as a PEM encoded RSA key
|
||||||
keyData = privateKey;
|
// deno-lint-ignore no-explicit-any
|
||||||
|
keyData = privateKey as any;
|
||||||
keyType = "rsa";
|
keyType = "rsa";
|
||||||
keyFormat = "pem";
|
keyFormat = "pem";
|
||||||
} else {
|
} else {
|
||||||
|
@ -103,35 +104,64 @@ export class Sign extends Writable {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Verify extends Writable {
|
export class Verify extends Writable {
|
||||||
|
hash: Hash;
|
||||||
|
#digestType: string;
|
||||||
|
|
||||||
constructor(algorithm: string, _options?: WritableOptions) {
|
constructor(algorithm: string, _options?: WritableOptions) {
|
||||||
validateString(algorithm, "algorithm");
|
validateString(algorithm, "algorithm");
|
||||||
|
|
||||||
super();
|
super({
|
||||||
|
write(chunk, enc, callback) {
|
||||||
|
this.update(chunk, enc);
|
||||||
|
callback();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
notImplemented("crypto.Verify");
|
algorithm = algorithm.toLowerCase();
|
||||||
|
|
||||||
|
if (algorithm.startsWith("rsa-")) {
|
||||||
|
// Allows RSA-[digest_algorithm] as a valid algorithm
|
||||||
|
algorithm = algorithm.slice(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#digestType = algorithm;
|
||||||
|
this.hash = createHash(this.#digestType);
|
||||||
}
|
}
|
||||||
|
|
||||||
update(data: BinaryLike): this;
|
update(data: BinaryLike, encoding?: string): this {
|
||||||
update(data: string, inputEncoding: Encoding): this;
|
this.hash.update(data, encoding);
|
||||||
update(_data: BinaryLike, _inputEncoding?: string): this {
|
return this;
|
||||||
notImplemented("crypto.Sign.prototype.update");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
verify(
|
verify(
|
||||||
object: KeyLike | VerifyKeyObjectInput | VerifyPublicKeyInput,
|
publicKey: BinaryLike | VerifyKeyObjectInput | VerifyPublicKeyInput,
|
||||||
signature: ArrayBufferView,
|
signature: BinaryLike,
|
||||||
): boolean;
|
encoding?: BinaryToTextEncoding,
|
||||||
verify(
|
|
||||||
object: KeyLike | VerifyKeyObjectInput | VerifyPublicKeyInput,
|
|
||||||
signature: string,
|
|
||||||
signatureEncoding?: BinaryToTextEncoding,
|
|
||||||
): boolean;
|
|
||||||
verify(
|
|
||||||
_object: KeyLike | VerifyKeyObjectInput | VerifyPublicKeyInput,
|
|
||||||
_signature: ArrayBufferView | string,
|
|
||||||
_signatureEncoding?: BinaryToTextEncoding,
|
|
||||||
): boolean {
|
): boolean {
|
||||||
notImplemented("crypto.Sign.prototype.sign");
|
let keyData: BinaryLike;
|
||||||
|
let keyType: KeyType;
|
||||||
|
let keyFormat: KeyFormat;
|
||||||
|
if (typeof publicKey === "string" || isArrayBufferView(publicKey)) {
|
||||||
|
// if the key is BinaryLike, interpret it as a PEM encoded RSA key
|
||||||
|
// deno-lint-ignore no-explicit-any
|
||||||
|
keyData = publicKey as any;
|
||||||
|
keyType = "rsa";
|
||||||
|
keyFormat = "pem";
|
||||||
|
} else {
|
||||||
|
// TODO(kt3k): Add support for the case when publicKey is a KeyObject,
|
||||||
|
// CryptoKey, etc
|
||||||
|
notImplemented(
|
||||||
|
"crypto.Verify.prototype.verify with non BinaryLike input",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return ops.op_node_verify(
|
||||||
|
this.hash.digest(),
|
||||||
|
this.#digestType,
|
||||||
|
keyData!,
|
||||||
|
keyType,
|
||||||
|
keyFormat,
|
||||||
|
Buffer.from(signature, encoding),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue