mirror of
https://github.com/denoland/deno.git
synced 2025-02-01 20:25:12 -05:00
dda0f1c343
`ZeroCopyBuf` was convenient to use, but sometimes it did hide details that some copies were necessary in certain cases. Also it made it way to easy for the caller to pass around and convert into different values. This commit splits `ZeroCopyBuf` into `JsBuffer` (an array buffer coming from V8) and `ToJsBuffer` (a Rust buffer that will be converted into a V8 array buffer). As a result some magical conversions were removed (they were never used) limiting the API surface and preparing for changes in #19534.
402 lines
11 KiB
Rust
402 lines
11 KiB
Rust
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
|
|
|
use const_oid::AssociatedOid;
|
|
use const_oid::ObjectIdentifier;
|
|
use deno_core::error::custom_error;
|
|
use deno_core::error::AnyError;
|
|
use deno_core::op;
|
|
use deno_core::ToJsBuffer;
|
|
use elliptic_curve::sec1::ToEncodedPoint;
|
|
use p256::pkcs8::DecodePrivateKey;
|
|
use rsa::pkcs1::UIntRef;
|
|
use serde::Deserialize;
|
|
use serde::Serialize;
|
|
use spki::der::asn1;
|
|
use spki::der::Decode;
|
|
use spki::der::Encode;
|
|
use spki::AlgorithmIdentifier;
|
|
|
|
use crate::shared::*;
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ExportKeyOptions {
|
|
format: ExportKeyFormat,
|
|
#[serde(flatten)]
|
|
algorithm: ExportKeyAlgorithm,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(rename_all = "lowercase")]
|
|
pub enum ExportKeyFormat {
|
|
Raw,
|
|
Pkcs8,
|
|
Spki,
|
|
JwkPublic,
|
|
JwkPrivate,
|
|
JwkSecret,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(rename_all = "camelCase", tag = "algorithm")]
|
|
pub enum ExportKeyAlgorithm {
|
|
#[serde(rename = "RSASSA-PKCS1-v1_5")]
|
|
RsassaPkcs1v15 {},
|
|
#[serde(rename = "RSA-PSS")]
|
|
RsaPss {},
|
|
#[serde(rename = "RSA-OAEP")]
|
|
RsaOaep {},
|
|
#[serde(rename = "ECDSA", rename_all = "camelCase")]
|
|
Ecdsa { named_curve: EcNamedCurve },
|
|
#[serde(rename = "ECDH", rename_all = "camelCase")]
|
|
Ecdh { named_curve: EcNamedCurve },
|
|
#[serde(rename = "AES")]
|
|
Aes {},
|
|
#[serde(rename = "HMAC")]
|
|
Hmac {},
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
#[serde(untagged)]
|
|
pub enum ExportKeyResult {
|
|
Raw(ToJsBuffer),
|
|
Pkcs8(ToJsBuffer),
|
|
Spki(ToJsBuffer),
|
|
JwkSecret {
|
|
k: String,
|
|
},
|
|
JwkPublicRsa {
|
|
n: String,
|
|
e: String,
|
|
},
|
|
JwkPrivateRsa {
|
|
n: String,
|
|
e: String,
|
|
d: String,
|
|
p: String,
|
|
q: String,
|
|
dp: String,
|
|
dq: String,
|
|
qi: String,
|
|
},
|
|
JwkPublicEc {
|
|
x: String,
|
|
y: String,
|
|
},
|
|
JwkPrivateEc {
|
|
x: String,
|
|
y: String,
|
|
d: String,
|
|
},
|
|
}
|
|
|
|
#[op]
|
|
pub fn op_crypto_export_key(
|
|
opts: ExportKeyOptions,
|
|
key_data: V8RawKeyData,
|
|
) -> Result<ExportKeyResult, AnyError> {
|
|
match opts.algorithm {
|
|
ExportKeyAlgorithm::RsassaPkcs1v15 {}
|
|
| ExportKeyAlgorithm::RsaPss {}
|
|
| ExportKeyAlgorithm::RsaOaep {} => export_key_rsa(opts.format, key_data),
|
|
ExportKeyAlgorithm::Ecdh { named_curve }
|
|
| ExportKeyAlgorithm::Ecdsa { named_curve } => {
|
|
export_key_ec(opts.format, key_data, opts.algorithm, named_curve)
|
|
}
|
|
ExportKeyAlgorithm::Aes {} | ExportKeyAlgorithm::Hmac {} => {
|
|
export_key_symmetric(opts.format, key_data)
|
|
}
|
|
}
|
|
}
|
|
|
|
fn uint_to_b64(bytes: UIntRef) -> String {
|
|
base64::encode_config(bytes.as_bytes(), base64::URL_SAFE_NO_PAD)
|
|
}
|
|
|
|
fn bytes_to_b64(bytes: &[u8]) -> String {
|
|
base64::encode_config(bytes, base64::URL_SAFE_NO_PAD)
|
|
}
|
|
|
|
fn export_key_rsa(
|
|
format: ExportKeyFormat,
|
|
key_data: V8RawKeyData,
|
|
) -> Result<ExportKeyResult, deno_core::anyhow::Error> {
|
|
match format {
|
|
ExportKeyFormat::Spki => {
|
|
let subject_public_key = &key_data.as_rsa_public_key()?;
|
|
|
|
// the SPKI structure
|
|
let key_info = spki::SubjectPublicKeyInfo {
|
|
algorithm: spki::AlgorithmIdentifier {
|
|
// rsaEncryption(1)
|
|
oid: const_oid::ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.1"),
|
|
// parameters field should not be ommited (None).
|
|
// It MUST have ASN.1 type NULL.
|
|
parameters: Some(asn1::AnyRef::from(asn1::Null)),
|
|
},
|
|
subject_public_key,
|
|
};
|
|
|
|
// Infallible because we know the public key is valid.
|
|
let spki_der = key_info.to_vec().unwrap();
|
|
Ok(ExportKeyResult::Spki(spki_der.into()))
|
|
}
|
|
ExportKeyFormat::Pkcs8 => {
|
|
let private_key = key_data.as_rsa_private_key()?;
|
|
|
|
// the PKCS#8 v1 structure
|
|
// PrivateKeyInfo ::= SEQUENCE {
|
|
// version Version,
|
|
// privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
|
|
// privateKey PrivateKey,
|
|
// attributes [0] IMPLICIT Attributes OPTIONAL }
|
|
|
|
// version is 0 when publickey is None
|
|
|
|
let pk_info = rsa::pkcs8::PrivateKeyInfo {
|
|
public_key: None,
|
|
algorithm: rsa::pkcs8::AlgorithmIdentifier {
|
|
// rsaEncryption(1)
|
|
oid: rsa::pkcs8::ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.1"),
|
|
// parameters field should not be ommited (None).
|
|
// It MUST have ASN.1 type NULL as per defined in RFC 3279 Section 2.3.1
|
|
parameters: Some(asn1::AnyRef::from(asn1::Null)),
|
|
},
|
|
private_key,
|
|
};
|
|
|
|
// Infallible because we know the private key is valid.
|
|
let pkcs8_der = pk_info.to_vec().unwrap();
|
|
|
|
Ok(ExportKeyResult::Pkcs8(pkcs8_der.into()))
|
|
}
|
|
ExportKeyFormat::JwkPublic => {
|
|
let public_key = key_data.as_rsa_public_key()?;
|
|
let public_key = rsa::pkcs1::RsaPublicKey::from_der(&public_key)
|
|
.map_err(|_| {
|
|
custom_error(
|
|
"DOMExceptionOperationError",
|
|
"failed to decode public key",
|
|
)
|
|
})?;
|
|
|
|
Ok(ExportKeyResult::JwkPublicRsa {
|
|
n: uint_to_b64(public_key.modulus),
|
|
e: uint_to_b64(public_key.public_exponent),
|
|
})
|
|
}
|
|
ExportKeyFormat::JwkPrivate => {
|
|
let private_key = key_data.as_rsa_private_key()?;
|
|
let private_key = rsa::pkcs1::RsaPrivateKey::from_der(private_key)
|
|
.map_err(|_| {
|
|
custom_error(
|
|
"DOMExceptionOperationError",
|
|
"failed to decode private key",
|
|
)
|
|
})?;
|
|
|
|
Ok(ExportKeyResult::JwkPrivateRsa {
|
|
n: uint_to_b64(private_key.modulus),
|
|
e: uint_to_b64(private_key.public_exponent),
|
|
d: uint_to_b64(private_key.private_exponent),
|
|
p: uint_to_b64(private_key.prime1),
|
|
q: uint_to_b64(private_key.prime2),
|
|
dp: uint_to_b64(private_key.exponent1),
|
|
dq: uint_to_b64(private_key.exponent2),
|
|
qi: uint_to_b64(private_key.coefficient),
|
|
})
|
|
}
|
|
_ => Err(unsupported_format()),
|
|
}
|
|
}
|
|
|
|
fn export_key_symmetric(
|
|
format: ExportKeyFormat,
|
|
key_data: V8RawKeyData,
|
|
) -> Result<ExportKeyResult, deno_core::anyhow::Error> {
|
|
match format {
|
|
ExportKeyFormat::JwkSecret => {
|
|
let bytes = key_data.as_secret_key()?;
|
|
|
|
Ok(ExportKeyResult::JwkSecret {
|
|
k: bytes_to_b64(bytes),
|
|
})
|
|
}
|
|
_ => Err(unsupported_format()),
|
|
}
|
|
}
|
|
|
|
fn export_key_ec(
|
|
format: ExportKeyFormat,
|
|
key_data: V8RawKeyData,
|
|
algorithm: ExportKeyAlgorithm,
|
|
named_curve: EcNamedCurve,
|
|
) -> Result<ExportKeyResult, deno_core::anyhow::Error> {
|
|
match format {
|
|
ExportKeyFormat::Raw => {
|
|
let subject_public_key = match named_curve {
|
|
EcNamedCurve::P256 => {
|
|
let point = key_data.as_ec_public_key_p256()?;
|
|
|
|
point.as_ref().to_vec()
|
|
}
|
|
EcNamedCurve::P384 => {
|
|
let point = key_data.as_ec_public_key_p384()?;
|
|
|
|
point.as_ref().to_vec()
|
|
}
|
|
EcNamedCurve::P521 => {
|
|
return Err(data_error("Unsupported named curve"))
|
|
}
|
|
};
|
|
Ok(ExportKeyResult::Raw(subject_public_key.into()))
|
|
}
|
|
ExportKeyFormat::Spki => {
|
|
let subject_public_key = match named_curve {
|
|
EcNamedCurve::P256 => {
|
|
let point = key_data.as_ec_public_key_p256()?;
|
|
|
|
point.as_ref().to_vec()
|
|
}
|
|
EcNamedCurve::P384 => {
|
|
let point = key_data.as_ec_public_key_p384()?;
|
|
|
|
point.as_ref().to_vec()
|
|
}
|
|
EcNamedCurve::P521 => {
|
|
return Err(data_error("Unsupported named curve"))
|
|
}
|
|
};
|
|
|
|
let alg_id = match named_curve {
|
|
EcNamedCurve::P256 => AlgorithmIdentifier {
|
|
oid: elliptic_curve::ALGORITHM_OID,
|
|
parameters: Some((&p256::NistP256::OID).into()),
|
|
},
|
|
EcNamedCurve::P384 => AlgorithmIdentifier {
|
|
oid: elliptic_curve::ALGORITHM_OID,
|
|
parameters: Some((&p384::NistP384::OID).into()),
|
|
},
|
|
EcNamedCurve::P521 => {
|
|
return Err(data_error("Unsupported named curve"))
|
|
}
|
|
};
|
|
|
|
let alg_id = match algorithm {
|
|
ExportKeyAlgorithm::Ecdh { .. } => AlgorithmIdentifier {
|
|
oid: ObjectIdentifier::new_unwrap("1.2.840.10045.2.1"),
|
|
parameters: alg_id.parameters,
|
|
},
|
|
_ => alg_id,
|
|
};
|
|
|
|
// the SPKI structure
|
|
let key_info = spki::SubjectPublicKeyInfo {
|
|
algorithm: alg_id,
|
|
subject_public_key: &subject_public_key,
|
|
};
|
|
|
|
let spki_der = key_info.to_vec().unwrap();
|
|
|
|
Ok(ExportKeyResult::Spki(spki_der.into()))
|
|
}
|
|
ExportKeyFormat::Pkcs8 => {
|
|
// private_key is a PKCS#8 DER-encoded private key
|
|
let private_key = key_data.as_ec_private_key()?;
|
|
|
|
Ok(ExportKeyResult::Pkcs8(private_key.to_vec().into()))
|
|
}
|
|
ExportKeyFormat::JwkPublic => match named_curve {
|
|
EcNamedCurve::P256 => {
|
|
let point = key_data.as_ec_public_key_p256()?;
|
|
let coords = point.coordinates();
|
|
|
|
if let p256::elliptic_curve::sec1::Coordinates::Uncompressed { x, y } =
|
|
coords
|
|
{
|
|
Ok(ExportKeyResult::JwkPublicEc {
|
|
x: bytes_to_b64(x),
|
|
y: bytes_to_b64(y),
|
|
})
|
|
} else {
|
|
Err(custom_error(
|
|
"DOMExceptionOperationError",
|
|
"failed to decode public key",
|
|
))
|
|
}
|
|
}
|
|
EcNamedCurve::P384 => {
|
|
let point = key_data.as_ec_public_key_p384()?;
|
|
let coords = point.coordinates();
|
|
|
|
if let p384::elliptic_curve::sec1::Coordinates::Uncompressed { x, y } =
|
|
coords
|
|
{
|
|
Ok(ExportKeyResult::JwkPublicEc {
|
|
x: bytes_to_b64(x),
|
|
y: bytes_to_b64(y),
|
|
})
|
|
} else {
|
|
Err(custom_error(
|
|
"DOMExceptionOperationError",
|
|
"failed to decode public key",
|
|
))
|
|
}
|
|
}
|
|
EcNamedCurve::P521 => Err(data_error("Unsupported named curve")),
|
|
},
|
|
ExportKeyFormat::JwkPrivate => {
|
|
let private_key = key_data.as_ec_private_key()?;
|
|
|
|
match named_curve {
|
|
EcNamedCurve::P256 => {
|
|
let ec_key =
|
|
p256::SecretKey::from_pkcs8_der(private_key).map_err(|_| {
|
|
custom_error(
|
|
"DOMExceptionOperationError",
|
|
"failed to decode private key",
|
|
)
|
|
})?;
|
|
|
|
let point = ec_key.public_key().to_encoded_point(false);
|
|
if let elliptic_curve::sec1::Coordinates::Uncompressed { x, y } =
|
|
point.coordinates()
|
|
{
|
|
Ok(ExportKeyResult::JwkPrivateEc {
|
|
x: bytes_to_b64(x),
|
|
y: bytes_to_b64(y),
|
|
d: bytes_to_b64(&ec_key.to_be_bytes()),
|
|
})
|
|
} else {
|
|
Err(data_error("expected valid public EC key"))
|
|
}
|
|
}
|
|
|
|
EcNamedCurve::P384 => {
|
|
let ec_key =
|
|
p384::SecretKey::from_pkcs8_der(private_key).map_err(|_| {
|
|
custom_error(
|
|
"DOMExceptionOperationError",
|
|
"failed to decode private key",
|
|
)
|
|
})?;
|
|
|
|
let point = ec_key.public_key().to_encoded_point(false);
|
|
if let elliptic_curve::sec1::Coordinates::Uncompressed { x, y } =
|
|
point.coordinates()
|
|
{
|
|
Ok(ExportKeyResult::JwkPrivateEc {
|
|
x: bytes_to_b64(x),
|
|
y: bytes_to_b64(y),
|
|
d: bytes_to_b64(&ec_key.to_be_bytes()),
|
|
})
|
|
} else {
|
|
Err(data_error("expected valid public EC key"))
|
|
}
|
|
}
|
|
_ => Err(not_supported_error("Unsupported namedCurve")),
|
|
}
|
|
}
|
|
ExportKeyFormat::JwkSecret => Err(unsupported_format()),
|
|
}
|
|
}
|