Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(ext/node): exporting rsa public keys #23596

Merged
merged 7 commits into from
Apr 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ext/node/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,8 @@ deno_core::extension!(deno_node,
ops::crypto::op_node_ecdh_compute_secret,
ops::crypto::op_node_ecdh_compute_public_key,
ops::crypto::op_node_ecdh_encode_pubkey,
ops::crypto::op_node_export_rsa_public_pem,
ops::crypto::op_node_export_rsa_spki_der,
ops::crypto::x509::op_node_x509_parse,
ops::crypto::x509::op_node_x509_ca,
ops::crypto::x509::op_node_x509_check_email,
Expand Down
22 changes: 21 additions & 1 deletion ext/node/ops/crypto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ use rsa::Oaep;
use rsa::Pkcs1v15Encrypt;
use rsa::RsaPrivateKey;
use rsa::RsaPublicKey;
use spki::EncodePublicKey;

mod cipher;
mod dh;
Expand Down Expand Up @@ -681,13 +682,32 @@ pub async fn op_node_generate_rsa_async(
spawn_blocking(move || generate_rsa(modulus_length, public_exponent)).await?
}

#[op2]
#[string]
pub fn op_node_export_rsa_public_pem(
#[buffer] pkcs1_der: &[u8],
) -> Result<String, AnyError> {
let public_key = RsaPublicKey::from_pkcs1_der(pkcs1_der)?;
let export = public_key.to_public_key_pem(Default::default())?;
Ok(export)
}

#[op2]
#[serde]
pub fn op_node_export_rsa_spki_der(
#[buffer] pkcs1_der: &[u8],
) -> Result<ToJsBuffer, AnyError> {
let public_key = RsaPublicKey::from_pkcs1_der(pkcs1_der)?;
let export = public_key.to_public_key_der()?.to_vec();
Ok(export.into())
}

fn dsa_generate(
modulus_length: usize,
divisor_length: usize,
) -> Result<(ToJsBuffer, ToJsBuffer), AnyError> {
let mut rng = rand::thread_rng();
use dsa::pkcs8::EncodePrivateKey;
use dsa::pkcs8::EncodePublicKey;
use dsa::Components;
use dsa::KeySize;
use dsa::SigningKey;
Expand Down
10 changes: 6 additions & 4 deletions ext/node/polyfills/internal/crypto/keygen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import { KeyObject } from "ext:deno_node/internal/crypto/keys.ts";
import { kAesKeyLengths } from "ext:deno_node/internal/crypto/util.ts";
import {
PrivateKeyObject,
PublicKeyObject,
SecretKeyObject,
setOwnedKey,
} from "ext:deno_node/internal/crypto/keys.ts";
Expand Down Expand Up @@ -564,8 +566,8 @@ export function generateKeyPair(
) => void,
) {
createJob(kAsync, type, options).then(([privateKey, publicKey]) => {
privateKey = new KeyObject("private", setOwnedKey(privateKey));
publicKey = new KeyObject("public", setOwnedKey(publicKey));
privateKey = new PrivateKeyObject(setOwnedKey(privateKey), { type });
publicKey = new PublicKeyObject(setOwnedKey(publicKey), { type });

if (typeof options === "object" && options !== null) {
const { publicKeyEncoding, privateKeyEncoding } = options as any;
Expand Down Expand Up @@ -766,8 +768,8 @@ export function generateKeyPairSync(
| KeyPairSyncResult<string | Buffer, string | Buffer> {
let [privateKey, publicKey] = createJob(kSync, type, options);

privateKey = new KeyObject("private", setOwnedKey(privateKey));
publicKey = new KeyObject("public", setOwnedKey(publicKey));
privateKey = new PrivateKeyObject(setOwnedKey(privateKey), { type });
publicKey = new PublicKeyObject(setOwnedKey(publicKey), { type });

if (typeof options === "object" && options !== null) {
const { publicKeyEncoding, privateKeyEncoding } = options as any;
Expand Down
34 changes: 30 additions & 4 deletions ext/node/polyfills/internal/crypto/keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import {
op_node_create_private_key,
op_node_create_public_key,
op_node_export_rsa_public_pem,
op_node_export_rsa_spki_der,
} from "ext:core/ops";

import {
Expand Down Expand Up @@ -360,7 +362,7 @@ class AsymmetricKeyObject extends KeyObject {
}
}

class PrivateKeyObject extends AsymmetricKeyObject {
export class PrivateKeyObject extends AsymmetricKeyObject {
constructor(handle: unknown, details: unknown) {
super("private", handle, details);
}
Expand All @@ -370,13 +372,35 @@ class PrivateKeyObject extends AsymmetricKeyObject {
}
}

class PublicKeyObject extends AsymmetricKeyObject {
export class PublicKeyObject extends AsymmetricKeyObject {
constructor(handle: unknown, details: unknown) {
super("public", handle, details);
}

export(_options: unknown) {
notImplemented("crypto.PublicKeyObject.prototype.export");
export(options: unknown) {
const key = KEY_STORE.get(this[kHandle]);
switch (this.asymmetricKeyType) {
case "rsa":
case "rsa-pss": {
switch (options.format) {
case "pem":
return op_node_export_rsa_public_pem(key);
case "der": {
if (options.type == "pkcs1") {
return key;
} else {
return op_node_export_rsa_spki_der(key);
}
}
default:
throw new TypeError(`exporting ${options.type} is not implemented`);
}
}
default:
throw new TypeError(
`exporting ${this.asymmetricKeyType} is not implemented`,
);
}
}
}

Expand Down Expand Up @@ -414,4 +438,6 @@ export default {
prepareSecretKey,
setOwnedKey,
SecretKeyObject,
PrivateKeyObject,
PublicKeyObject,
};
15 changes: 14 additions & 1 deletion tests/unit_node/crypto/crypto_key_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
} from "node:crypto";
import { promisify } from "node:util";
import { Buffer } from "node:buffer";
import { assertEquals, assertThrows } from "@std/assert/mod.ts";
import { assert, assertEquals, assertThrows } from "@std/assert/mod.ts";

const RUN_SLOW_TESTS = Deno.env.get("SLOW_TESTS") === "1";

Expand Down Expand Up @@ -402,3 +402,16 @@ SogaIHQjE81ZkmNtU5gM5Q==
`jEwckJ/d5GkF/8TTm+wllq2JNghG/m2JYJIW7vS8Vms53zCTTNSSegTSoIVoxWymwTPw2dTtZi41Lg0O271/WvEmQhiWD2dnjz6D/0F4eyn+QUhcmGCadDFyfp7+8x1XOppSw2YB8vL5WCL0QDdp3TAa/rWI0Hn4OftHMa6HPvatkGs+8XlQOGCCfd3TLg+t1UROgpgmetjoAM67mlwxXMGGu/Tr/EbXnnINKeB0iuSmD1FCxlrgFuYWDKxd79n2jZ74FrS/zto+bqWSI5uUa4Ar7yvXtek1Cu1OFM6vgdN9Y6Po2UD9+IT04EhU03LUDY5paYOO8yohz7p7kqHvpA==`,
);
});

Deno.test("generate rsa export public key", async function () {
const { publicKey } = await generateKeyPairAsync("rsa", {
modulusLength: 2048,
});

const spkiPem = publicKey.export({ format: "pem", type: "spki" });
assert(typeof spkiPem === "string");
assert(spkiPem.startsWith("-----BEGIN PUBLIC KEY-----"));

const der = publicKey.export({ format: "der", type: "spki" });
assert(der instanceof Uint8Array);
});