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

feat(ext/crypto): export spki for RSA (#12114)

This commit is contained in:
Divy Srivastava 2021-10-06 14:48:12 +05:30 committed by GitHub
parent d5b38a9929
commit 3aa8591595
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 162 additions and 10 deletions

5
Cargo.lock generated
View file

@ -733,6 +733,7 @@ dependencies = [
"serde",
"sha-1",
"sha2",
"spki",
"tokio",
"uuid",
]
@ -3302,9 +3303,9 @@ dependencies = [
[[package]]
name = "spki"
version = "0.4.0"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "987637c5ae6b3121aba9d513f869bd2bff11c4cc086c22473befd6649c0bd521"
checksum = "5c01a0c15da1b0b0e1494112e7af814a678fec9bd157881b49beac661e9b6f32"
dependencies = [
"der",
]

View file

@ -441,7 +441,7 @@ const asn1AlgorithmIdentifier = new Uint8Array([
0x05, 0x00, // NULL
]);
unitTest(async function rsaExportPkcs8() {
unitTest(async function rsaExport() {
for (const algorithm of ["RSASSA-PKCS1-v1_5", "RSA-PSS", "RSA-OAEP"]) {
const keyPair = await crypto.subtle.generateKey(
{
@ -458,21 +458,34 @@ unitTest(async function rsaExportPkcs8() {
assert(keyPair.publicKey);
assertEquals(keyPair.privateKey.extractable, true);
const exportedKey = await crypto.subtle.exportKey(
const exportedPrivateKey = await crypto.subtle.exportKey(
"pkcs8",
keyPair.privateKey,
);
assert(exportedKey);
assert(exportedKey instanceof ArrayBuffer);
assert(exportedPrivateKey);
assert(exportedPrivateKey instanceof ArrayBuffer);
const pkcs8 = new Uint8Array(exportedKey);
const pkcs8 = new Uint8Array(exportedPrivateKey);
assert(pkcs8.length > 0);
assertEquals(
pkcs8.slice(4, asn1AlgorithmIdentifier.byteLength + 4),
asn1AlgorithmIdentifier,
);
const exportedPublicKey = await crypto.subtle.exportKey(
"spki",
keyPair.publicKey,
);
const spki = new Uint8Array(exportedPublicKey);
assert(spki.length > 0);
assertEquals(
spki.slice(4, asn1AlgorithmIdentifier.byteLength + 1),
asn1AlgorithmIdentifier.slice(3),
);
}
});

View file

@ -1278,6 +1278,28 @@
// 3.
return data.buffer;
}
case "spki": {
// 1.
if (key[_type] !== "public") {
throw new DOMException(
"Key is not a public key",
"InvalidAccessError",
);
}
// 2.
const data = await core.opAsync(
"op_crypto_export_key",
{
key: innerKey,
format: "spki",
algorithm: "RSASSA-PKCS1-v1_5",
},
);
// 3.
return data.buffer;
}
default:
throw new DOMException("Not implemented", "NotSupportedError");
}
@ -1307,6 +1329,29 @@
// 3.
return data.buffer;
}
case "spki": {
// 1.
if (key[_type] !== "public") {
throw new DOMException(
"Key is not a public key",
"InvalidAccessError",
);
}
// 2.
const data = await core.opAsync(
"op_crypto_export_key",
{
key: innerKey,
format: "spki",
algorithm: "RSA-PSS",
hash: key[_algorithm].hash.name,
},
);
// 3.
return data.buffer;
}
default:
throw new DOMException("Not implemented", "NotSupportedError");
}
@ -1336,6 +1381,29 @@
// 3.
return data.buffer;
}
case "spki": {
// 1.
if (key[_type] !== "public") {
throw new DOMException(
"Key is not a public key",
"InvalidAccessError",
);
}
// 2.
const data = await core.opAsync(
"op_crypto_export_key",
{
key: innerKey,
format: "spki",
algorithm: "RSA-OAEP",
hash: key[_algorithm].hash.name,
},
);
// 3.
return data.buffer;
}
default:
throw new DOMException("Not implemented", "NotSupportedError");
}

View file

@ -24,5 +24,6 @@ rsa = { version = "0.5.0", default-features = false, features = ["std"] }
serde = { version = "1.0.129", features = ["derive"] }
sha-1 = "0.9.7"
sha2 = "0.9.5"
spki = "0.4.1"
tokio = { version = "1.10.1", features = ["full"] }
uuid = { version = "0.8.2", features = ["v4"] }

View file

@ -271,6 +271,7 @@ pub async fn op_crypto_generate_key(
pub enum KeyFormat {
Raw,
Pkcs8,
Spki,
}
#[derive(Deserialize)]
@ -631,7 +632,27 @@ pub async fn op_crypto_export_key(
Ok(pk_info.to_der().as_ref().to_vec().into())
}
// TODO(@littledivy): spki
KeyFormat::Spki => {
// public_key is a PKCS#1 DER-encoded public key
let subject_public_key = &args.key.data;
// the SPKI structure
let key_info = spki::SubjectPublicKeyInfo {
algorithm: spki::AlgorithmIdentifier {
// rsaEncryption(1)
oid: spki::ObjectIdentifier::new("1.2.840.113549.1.1.1"),
// parameters field should not be ommited (None).
// It MUST have ASN.1 type NULL.
parameters: Some(asn1::Any::from(asn1::Null)),
},
subject_public_key,
};
// Infallible based on spec because of the way we import and generate keys.
let spki_der = key_info.to_vec().unwrap();
Ok(spki_der.into())
}
// TODO(@littledivy): jwk
_ => unreachable!(),
}
@ -668,7 +689,31 @@ pub async fn op_crypto_export_key(
Ok(pk_info.to_der().as_ref().to_vec().into())
}
// TODO(@littledivy): spki
KeyFormat::Spki => {
// Intentionally unused but required. Not encoded into SPKI (see below).
let _hash = args
.hash
.ok_or_else(|| type_error("Missing argument hash".to_string()))?;
// public_key is a PKCS#1 DER-encoded public key
let subject_public_key = &args.key.data;
// the SPKI structure
let key_info = spki::SubjectPublicKeyInfo {
algorithm: spki::AlgorithmIdentifier {
// rsaEncryption(1)
oid: spki::ObjectIdentifier::new("1.2.840.113549.1.1.1"),
// parameters field should not be ommited (None).
// It MUST have ASN.1 type NULL.
parameters: Some(asn1::Any::from(asn1::Null)),
},
subject_public_key,
};
// Infallible based on spec because of the way we import and generate keys.
let spki_der = key_info.to_vec().unwrap();
Ok(spki_der.into())
}
// TODO(@littledivy): jwk
_ => unreachable!(),
}
@ -705,7 +750,31 @@ pub async fn op_crypto_export_key(
Ok(pk_info.to_der().as_ref().to_vec().into())
}
// TODO(@littledivy): spki
KeyFormat::Spki => {
// Intentionally unused but required. Not encoded into SPKI (see below).
let _hash = args
.hash
.ok_or_else(|| type_error("Missing argument hash".to_string()))?;
// public_key is a PKCS#1 DER-encoded public key
let subject_public_key = &args.key.data;
// the SPKI structure
let key_info = spki::SubjectPublicKeyInfo {
algorithm: spki::AlgorithmIdentifier {
// rsaEncryption(1)
oid: spki::ObjectIdentifier::new("1.2.840.113549.1.1.1"),
// parameters field should not be ommited (None).
// It MUST have ASN.1 type NULL.
parameters: Some(asn1::Any::from(asn1::Null)),
},
subject_public_key,
};
// Infallible based on spec because of the way we import and generate keys.
let spki_der = key_info.to_vec().unwrap();
Ok(spki_der.into())
}
// TODO(@littledivy): jwk
_ => unreachable!(),
}