use std::borrow::Cow;
use std::cell::RefCell;
use base64::Engine;
use deno_core::FromV8;
use deno_core::GarbageCollected;
use deno_core::ToV8;
use deno_core::convert::Uint8Array;
use deno_core::op2;
use deno_core::unsync::spawn_blocking;
use deno_error::JsErrorBox;
use digest::Digest;
use digest::FixedOutputReset;
use ed25519_dalek::pkcs8::BitStringRef;
use elliptic_curve::JwkEcKey;
use hmac::Hmac;
use hmac::Mac;
use num_bigint::BigInt;
use num_traits::FromPrimitive as _;
use p12::AlgorithmIdentifier as Pkcs12AlgorithmIdentifier;
use p12::CertBag as Pkcs12CertBag;
use p12::ContentInfo as Pkcs12ContentInfo;
use p12::PFX as Pkcs12;
use p12::SafeBag as Pkcs12SafeBag;
use p12::SafeBagKind as Pkcs12SafeBagKind;
use pkcs8::DecodePrivateKey as _;
use pkcs8::Document;
use pkcs8::EncodePrivateKey as _;
use pkcs8::EncryptedPrivateKeyInfo;
use pkcs8::PrivateKeyInfo;
use pkcs8::SecretDocument;
use rand::RngCore as _;
use rand::thread_rng;
use rsa::RsaPrivateKey;
use rsa::RsaPublicKey;
use rsa::pkcs1::DecodeRsaPrivateKey as _;
use rsa::pkcs1::DecodeRsaPublicKey;
use rsa::pkcs1::EncodeRsaPrivateKey as _;
use rsa::pkcs1::EncodeRsaPublicKey;
use rsa::traits::PrivateKeyParts;
use rsa::traits::PublicKeyParts;
use sec1::DecodeEcPrivateKey as _;
use sec1::LineEnding;
use sec1::der::Tag;
use sec1::der::Writer as _;
use sec1::pem::PemLabel as _;
use spki::DecodePublicKey as _;
use spki::EncodePublicKey as _;
use spki::SubjectPublicKeyInfoRef;
use spki::der::AnyRef;
use spki::der::Decode as _;
use spki::der::Encode as _;
use spki::der::PemWriter;
use spki::der::Reader as _;
use spki::der::asn1;
use spki::der::asn1::OctetStringRef;
use x509_parser::error::X509Error;
use x509_parser::x509;
use super::dh;
use super::dh::DiffieHellmanGroup;
use super::digest::match_fixed_digest_with_oid;
use super::pkcs3;
use super::pkcs3::DhParameter;
use super::primes::Prime;
#[derive(Clone)]
pub enum KeyObjectHandle {
AsymmetricPrivate(AsymmetricPrivateKey),
AsymmetricPublic(AsymmetricPublicKey),
Secret(Box<[u8]>),
}
unsafe impl GarbageCollected for KeyObjectHandle {
fn trace(&self, _visitor: &mut deno_core::v8::cppgc::Visitor) {}
fn get_name(&self) -> &'static std::ffi::CStr {
c"KeyObjectHandle"
}
}
#[derive(Clone)]
pub enum AsymmetricPrivateKey {
Rsa(RsaPrivateKey),
RsaPss(RsaPssPrivateKey),
Dsa(dsa::SigningKey),
Ec(EcPrivateKey),
X25519(x25519_dalek::StaticSecret),
Ed25519(ed25519_dalek::SigningKey),
X448([u8; 56]),
Ed448(ed448_goldilocks::SigningKey),
Dh(DhPrivateKey),
}
#[derive(Clone)]
pub struct RsaPssPrivateKey {
pub key: RsaPrivateKey,
pub details: Option<RsaPssDetails>,
}
#[derive(Clone, Copy, PartialEq)]
pub struct RsaPssDetails {
pub hash_algorithm: RsaPssHashAlgorithm,
pub mf1_hash_algorithm: RsaPssHashAlgorithm,
pub salt_length: u32,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum RsaPssHashAlgorithm {
Sha1,
Sha224,
Sha256,
Sha384,
Sha512,
Sha512_224,
Sha512_256,
}
impl RsaPssHashAlgorithm {
pub fn as_str(&self) -> &'static str {
match self {
RsaPssHashAlgorithm::Sha1 => "sha1",
RsaPssHashAlgorithm::Sha224 => "sha224",
RsaPssHashAlgorithm::Sha256 => "sha256",
RsaPssHashAlgorithm::Sha384 => "sha384",
RsaPssHashAlgorithm::Sha512 => "sha512",
RsaPssHashAlgorithm::Sha512_224 => "sha512-224",
RsaPssHashAlgorithm::Sha512_256 => "sha512-256",
}
}
pub fn salt_length(&self) -> u32 {
match self {
RsaPssHashAlgorithm::Sha1 => 20,
RsaPssHashAlgorithm::Sha224 | RsaPssHashAlgorithm::Sha512_224 => 28,
RsaPssHashAlgorithm::Sha256 | RsaPssHashAlgorithm::Sha512_256 => 32,
RsaPssHashAlgorithm::Sha384 => 48,
RsaPssHashAlgorithm::Sha512 => 64,
}
}
}
#[derive(Clone)]
pub enum EcPrivateKey {
P224(p224::SecretKey),
P256(p256::SecretKey),
P384(p384::SecretKey),
P521(p521::SecretKey),
Secp256k1(k256::SecretKey),
}
#[derive(Clone)]
pub struct DhPrivateKey {
pub key: dh::PrivateKey,
pub params: DhParameter,
}
#[derive(Clone)]
pub enum AsymmetricPublicKey {
Rsa(rsa::RsaPublicKey),
RsaPss(RsaPssPublicKey),
Dsa(dsa::VerifyingKey),
Ec(EcPublicKey),
X25519(x25519_dalek::PublicKey),
Ed25519(ed25519_dalek::VerifyingKey),
X448([u8; 56]),
Ed448(ed448_goldilocks::VerifyingKey),
Dh(DhPublicKey),
}
#[derive(Clone)]
pub struct RsaPssPublicKey {
pub key: rsa::RsaPublicKey,
pub details: Option<RsaPssDetails>,
}
#[derive(Clone)]
pub enum EcPublicKey {
P224(p224::PublicKey),
P256(p256::PublicKey),
P384(p384::PublicKey),
P521(p521::PublicKey),
Secp256k1(k256::PublicKey),
}
#[derive(Clone)]
pub struct DhPublicKey {
pub key: dh::PublicKey,
pub params: DhParameter,
}
impl KeyObjectHandle {
pub fn as_private_key(&self) -> Option<&AsymmetricPrivateKey> {
match self {
KeyObjectHandle::AsymmetricPrivate(key) => Some(key),
_ => None,
}
}
pub fn as_public_key(&self) -> Option<Cow<'_, AsymmetricPublicKey>> {
match self {
KeyObjectHandle::AsymmetricPrivate(key) => {
Some(Cow::Owned(key.to_public_key()))
}
KeyObjectHandle::AsymmetricPublic(key) => Some(Cow::Borrowed(key)),
_ => None,
}
}
pub fn as_secret_key(&self) -> Option<&[u8]> {
match self {
KeyObjectHandle::Secret(key) => Some(key),
_ => None,
}
}
}
impl AsymmetricPrivateKey {
pub fn to_public_key(&self) -> AsymmetricPublicKey {
match self {
AsymmetricPrivateKey::Rsa(key) => {
AsymmetricPublicKey::Rsa(key.to_public_key())
}
AsymmetricPrivateKey::RsaPss(key) => {
AsymmetricPublicKey::RsaPss(key.to_public_key())
}
AsymmetricPrivateKey::Dsa(key) => {
AsymmetricPublicKey::Dsa(key.verifying_key().clone())
}
AsymmetricPrivateKey::Ec(key) => {
AsymmetricPublicKey::Ec(key.to_public_key())
}
AsymmetricPrivateKey::X25519(key) => {
AsymmetricPublicKey::X25519(x25519_dalek::PublicKey::from(key))
}
AsymmetricPrivateKey::Ed25519(key) => {
AsymmetricPublicKey::Ed25519(key.verifying_key())
}
AsymmetricPrivateKey::X448(key) => {
let mut scalar_bytes = [0u8; 57];
scalar_bytes[..56].copy_from_slice(&key[..56]);
let scalar = ed448_goldilocks::EdwardsScalar::from_bytes_mod_order(
&scalar_bytes.into(),
);
let point = &ed448_goldilocks::MontgomeryPoint::GENERATOR * &scalar;
AsymmetricPublicKey::X448(point.0)
}
AsymmetricPrivateKey::Ed448(key) => {
AsymmetricPublicKey::Ed448(key.verifying_key())
}
AsymmetricPrivateKey::Dh(dh_key) => {
let prime = num_bigint_dig::BigUint::from_bytes_be(
dh_key.params.prime.as_bytes(),
);
let base =
num_bigint_dig::BigUint::from_bytes_be(dh_key.params.base.as_bytes());
let public_key = dh_key.key.compute_public_key(&base, &prime);
AsymmetricPublicKey::Dh(DhPublicKey {
key: public_key,
params: dh_key.params.clone(),
})
}
}
}
}
impl RsaPssPrivateKey {
pub fn to_public_key(&self) -> RsaPssPublicKey {
RsaPssPublicKey {
key: self.key.to_public_key(),
details: self.details,
}
}
}
impl EcPublicKey {
pub fn to_jwk(&self) -> Result<JwkEcKey, AsymmetricPublicKeyJwkError> {
match self {
EcPublicKey::P224(_) => {
Err(AsymmetricPublicKeyJwkError::UnsupportedJwkEcCurveP224)
}
EcPublicKey::P256(key) => Ok(key.to_jwk()),
EcPublicKey::P384(key) => Ok(key.to_jwk()),
EcPublicKey::P521(key) => Ok(key.to_jwk()),
EcPublicKey::Secp256k1(key) => Ok(key.to_jwk()),
}
}
}
impl EcPrivateKey {
pub fn to_public_key(&self) -> EcPublicKey {
match self {
EcPrivateKey::P224(key) => EcPublicKey::P224(key.public_key()),
EcPrivateKey::P256(key) => EcPublicKey::P256(key.public_key()),
EcPrivateKey::P384(key) => EcPublicKey::P384(key.public_key()),
EcPrivateKey::P521(key) => EcPublicKey::P521(key.public_key()),
EcPrivateKey::Secp256k1(key) => EcPublicKey::Secp256k1(key.public_key()),
}
}
pub fn to_jwk(&self) -> Result<JwkEcKey, AsymmetricPrivateKeyJwkError> {
match self {
EcPrivateKey::P224(_) => {
Err(AsymmetricPrivateKeyJwkError::UnsupportedJwkEcCurveP224)
}
EcPrivateKey::P256(key) => Ok(key.to_jwk()),
EcPrivateKey::P384(key) => Ok(key.to_jwk()),
EcPrivateKey::P521(key) => Ok(key.to_jwk()),
EcPrivateKey::Secp256k1(key) => Ok(key.to_jwk()),
}
}
}
impl PartialEq for EcPublicKey {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(EcPublicKey::P224(a), EcPublicKey::P224(b)) => a == b,
(EcPublicKey::P256(a), EcPublicKey::P256(b)) => a == b,
(EcPublicKey::P384(a), EcPublicKey::P384(b)) => a == b,
(EcPublicKey::P521(a), EcPublicKey::P521(b)) => a == b,
(EcPublicKey::Secp256k1(a), EcPublicKey::Secp256k1(b)) => a == b,
_ => false,
}
}
}
impl PartialEq for EcPrivateKey {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(EcPrivateKey::P224(a), EcPrivateKey::P224(b)) => a == b,
(EcPrivateKey::P256(a), EcPrivateKey::P256(b)) => a == b,
(EcPrivateKey::P384(a), EcPrivateKey::P384(b)) => a == b,
(EcPrivateKey::P521(a), EcPrivateKey::P521(b)) => a == b,
(EcPrivateKey::Secp256k1(a), EcPrivateKey::Secp256k1(b)) => a == b,
_ => false,
}
}
}
impl PartialEq for RsaPssPublicKey {
fn eq(&self, other: &Self) -> bool {
self.key == other.key && self.details == other.details
}
}
impl PartialEq for RsaPssPrivateKey {
fn eq(&self, other: &Self) -> bool {
self.key == other.key && self.details == other.details
}
}
fn dh_params_eq(a: &DhParameter, b: &DhParameter) -> bool {
let a_der = a.to_der().unwrap_or_default();
let b_der = b.to_der().unwrap_or_default();
a_der == b_der
}
impl PartialEq for DhPublicKey {
fn eq(&self, other: &Self) -> bool {
self.key == other.key && dh_params_eq(&self.params, &other.params)
}
}
impl PartialEq for DhPrivateKey {
fn eq(&self, other: &Self) -> bool {
self.key == other.key && dh_params_eq(&self.params, &other.params)
}
}
impl PartialEq for AsymmetricPublicKey {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Rsa(a), Self::Rsa(b)) => a == b,
(Self::RsaPss(a), Self::RsaPss(b)) => a == b,
(Self::Dsa(a), Self::Dsa(b)) => {
a.to_public_key_der().ok() == b.to_public_key_der().ok()
}
(Self::Ec(a), Self::Ec(b)) => a == b,
(Self::X25519(a), Self::X25519(b)) => a == b,
(Self::Ed25519(a), Self::Ed25519(b)) => a == b,
(Self::X448(a), Self::X448(b)) => a == b,
(Self::Ed448(a), Self::Ed448(b)) => a == b,
(Self::Dh(a), Self::Dh(b)) => a == b,
_ => false,
}
}
}
impl PartialEq for AsymmetricPrivateKey {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Rsa(a), Self::Rsa(b)) => a == b,
(Self::RsaPss(a), Self::RsaPss(b)) => a == b,
(Self::Dsa(a), Self::Dsa(b)) => {
a.to_pkcs8_der().ok().map(|d| d.to_bytes())
== b.to_pkcs8_der().ok().map(|d| d.to_bytes())
}
(Self::Ec(a), Self::Ec(b)) => a == b,
(Self::X25519(a), Self::X25519(b)) => a.to_bytes() == b.to_bytes(),
(Self::Ed25519(a), Self::Ed25519(b)) => a.to_bytes() == b.to_bytes(),
(Self::X448(a), Self::X448(b)) => a == b,
(Self::Ed448(a), Self::Ed448(b)) => a == b,
(Self::Dh(a), Self::Dh(b)) => a == b,
_ => false,
}
}
}
impl PartialEq for KeyObjectHandle {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::AsymmetricPrivate(a), Self::AsymmetricPrivate(b)) => a == b,
(Self::AsymmetricPublic(a), Self::AsymmetricPublic(b)) => a == b,
(Self::Secret(a), Self::Secret(b)) => {
use subtle::ConstantTimeEq;
a.ct_eq(b).into()
}
_ => false,
}
}
}
const ID_SHA1_OID: rsa::pkcs8::ObjectIdentifier =
rsa::pkcs8::ObjectIdentifier::new_unwrap("1.3.14.3.2.26");
const ID_SHA224_OID: rsa::pkcs8::ObjectIdentifier =
rsa::pkcs8::ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.4");
const ID_SHA256_OID: rsa::pkcs8::ObjectIdentifier =
rsa::pkcs8::ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.1");
const ID_SHA384_OID: rsa::pkcs8::ObjectIdentifier =
rsa::pkcs8::ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.2");
const ID_SHA512_OID: rsa::pkcs8::ObjectIdentifier =
rsa::pkcs8::ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.3");
const ID_SHA512_224_OID: rsa::pkcs8::ObjectIdentifier =
rsa::pkcs8::ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.5");
const ID_SHA512_256_OID: rsa::pkcs8::ObjectIdentifier =
rsa::pkcs8::ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.6");
const ID_MFG1: rsa::pkcs8::ObjectIdentifier =
rsa::pkcs8::ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.8");
pub const ID_SECP224R1_OID: const_oid::ObjectIdentifier =
const_oid::ObjectIdentifier::new_unwrap("1.3.132.0.33");
pub const ID_SECP256R1_OID: const_oid::ObjectIdentifier =
const_oid::ObjectIdentifier::new_unwrap("1.2.840.10045.3.1.7");
pub const ID_SECP384R1_OID: const_oid::ObjectIdentifier =
const_oid::ObjectIdentifier::new_unwrap("1.3.132.0.34");
pub const ID_SECP521R1_OID: const_oid::ObjectIdentifier =
const_oid::ObjectIdentifier::new_unwrap("1.3.132.0.35");
pub const ID_SECP256K1_OID: const_oid::ObjectIdentifier =
const_oid::ObjectIdentifier::new_unwrap("1.3.132.0.10");
pub const RSA_ENCRYPTION_OID: const_oid::ObjectIdentifier =
const_oid::ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.1");
pub const RSASSA_PSS_OID: const_oid::ObjectIdentifier =
const_oid::ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.10");
pub const DSA_OID: const_oid::ObjectIdentifier =
const_oid::ObjectIdentifier::new_unwrap("1.2.840.10040.4.1");
pub const EC_OID: const_oid::ObjectIdentifier =
const_oid::ObjectIdentifier::new_unwrap("1.2.840.10045.2.1");
pub const X25519_OID: const_oid::ObjectIdentifier =
const_oid::ObjectIdentifier::new_unwrap("1.3.101.110");
pub const ED25519_OID: const_oid::ObjectIdentifier =
const_oid::ObjectIdentifier::new_unwrap("1.3.101.112");
pub const X448_OID: const_oid::ObjectIdentifier =
const_oid::ObjectIdentifier::new_unwrap("1.3.101.111");
pub const ED448_OID: const_oid::ObjectIdentifier =
const_oid::ObjectIdentifier::new_unwrap("1.3.101.113");
pub const DH_KEY_AGREEMENT_OID: const_oid::ObjectIdentifier =
const_oid::ObjectIdentifier::new_unwrap("1.2.840.113549.1.3.1");
pub struct RsaPssParameters<'a> {
pub hash_algorithm: Option<rsa::pkcs8::AlgorithmIdentifierRef<'a>>,
pub mask_gen_algorithm: Option<rsa::pkcs8::AlgorithmIdentifierRef<'a>>,
pub salt_length: Option<u32>,
}
const HASH_ALGORITHM_TAG: rsa::pkcs8::der::TagNumber =
rsa::pkcs8::der::TagNumber::new(0);
const MASK_GEN_ALGORITHM_TAG: rsa::pkcs8::der::TagNumber =
rsa::pkcs8::der::TagNumber::new(1);
const SALT_LENGTH_TAG: rsa::pkcs8::der::TagNumber =
rsa::pkcs8::der::TagNumber::new(2);
impl<'a> TryFrom<rsa::pkcs8::der::asn1::AnyRef<'a>> for RsaPssParameters<'a> {
type Error = rsa::pkcs8::der::Error;
fn try_from(
any: rsa::pkcs8::der::asn1::AnyRef<'a>,
) -> rsa::pkcs8::der::Result<RsaPssParameters<'a>> {
any.sequence(|decoder| {
let hash_algorithm = decoder
.context_specific::<rsa::pkcs8::AlgorithmIdentifierRef>(
HASH_ALGORITHM_TAG,
pkcs8::der::TagMode::Explicit,
)?
.map(TryInto::try_into)
.transpose()?;
let mask_gen_algorithm = decoder
.context_specific::<rsa::pkcs8::AlgorithmIdentifierRef>(
MASK_GEN_ALGORITHM_TAG,
pkcs8::der::TagMode::Explicit,
)?
.map(TryInto::try_into)
.transpose()?;
let salt_length = decoder
.context_specific::<u32>(
SALT_LENGTH_TAG,
pkcs8::der::TagMode::Explicit,
)?
.map(TryInto::try_into)
.transpose()?;
Ok(Self {
hash_algorithm,
mask_gen_algorithm,
salt_length,
})
})
}
}
#[derive(Debug, thiserror::Error, deno_error::JsError)]
pub enum X509PublicKeyError {
#[class(generic)]
#[error(transparent)]
X509(#[from] X509Error),
#[class(generic)]
#[error(transparent)]
Rsa(#[from] rsa::Error),
#[class(generic)]
#[error(transparent)]
Asn1(#[from] x509_parser::der_parser::asn1_rs::Error),
#[class(generic)]
#[error(transparent)]
Ec(#[from] elliptic_curve::Error),
#[class(type)]
#[error("unsupported ec named curve")]
UnsupportedEcNamedCurve,
#[class(type)]
#[error("missing ec parameters")]
MissingEcParameters,
#[class(type)]
#[error("malformed DSS public key")]
MalformedDssPublicKey,
#[class(type)]
#[error("invalid Ed25519 public key")]
InvalidEd25519Key,
#[class(type)]
#[error("invalid Ed448 public key")]
InvalidEd448Key,
#[class(type)]
#[error("invalid X25519 public key")]
InvalidX25519Key,
#[class(type)]
#[error("unsupported x509 public key type")]
UnsupportedX509KeyType,
}
#[derive(Debug, thiserror::Error, deno_error::JsError)]
pub enum RsaJwkError {
#[class(generic)]
#[error(transparent)]
Base64(#[from] base64::DecodeError),
#[class(generic)]
#[error(transparent)]
Rsa(#[from] rsa::Error),
#[class(type)]
#[error("missing RSA private component")]
MissingRsaPrivateComponent,
}
#[derive(Debug, thiserror::Error, deno_error::JsError)]
pub enum EcJwkError {
#[class(generic)]
#[error(transparent)]
Ec(#[from] elliptic_curve::Error),
#[class(type)]
#[error("unsupported curve: {0}")]
UnsupportedCurve(String),
}
#[derive(Debug, thiserror::Error, deno_error::JsError)]
pub enum EdRawError {
#[class(generic)]
#[error(transparent)]
Ed25519Signature(#[from] ed25519_dalek::SignatureError),
#[class(type)]
#[error("invalid Ed25519 key")]
InvalidEd25519Key,
#[class(type)]
#[error("invalid Ed448 key")]
InvalidEd448Key,
#[class(type)]
#[error("invalid X448 key")]
InvalidX448Key,
#[class(type)]
#[error("unsupported curve")]
UnsupportedCurve,
}
#[derive(Debug, thiserror::Error, deno_error::JsError)]
#[class(generic)]
#[error("unsupported")]
#[property("code" = "ERR_OSSL_UNSUPPORTED")]
pub struct UnsupportedPrivateKeyOidError;
#[derive(Debug, thiserror::Error, deno_error::JsError)]
#[class(type)]
pub enum AsymmetricPrivateKeyError {
#[error("invalid PEM private key: not valid utf8 starting at byte {0}")]
InvalidPemPrivateKeyInvalidUtf8(usize),
#[class(generic)]
#[error("error:1E08010C:DECODER routines::unsupported")]
InvalidEncryptedPemPrivateKey,
#[class(generic)]
#[error("error:1C800064:Provider routines::bad decrypt")]
EncryptedPrivateKeyBadDecrypt,
#[error("error:1E08010C:DECODER routines::unsupported")]
InvalidPemPrivateKey,
#[class(generic)]
#[property("code" = "ERR_OSSL_EVP_BAD_DECRYPT")]
#[error("error:1C800064:Provider routines::bad decrypt")]
BadDecrypt,
#[class(generic)]
#[property("code" = "ERR_OSSL_CRYPTO_INTERRUPTED_OR_CANCELLED")]
#[error("error:07880109:common libcrypto routines::interrupted or cancelled")]
EncryptedPrivateKeyRequiresPassphraseToDecrypt,
#[property("code" = "ERR_MISSING_PASSPHRASE")]
#[error("Passphrase required for encrypted key")]
EncryptedPkcs8DerRequiresPassphrase,
#[error("error:1E08010C:DECODER routines::unsupported")]
InvalidPkcs1PrivateKey,
#[error("invalid SEC1 private key")]
InvalidSec1PrivateKey,
#[error("unsupported PEM label: {0}")]
UnsupportedPemLabel(String),
#[class(inherit)]
#[error(transparent)]
RsaPssParamsParse(
#[from]
#[inherit]
RsaPssParamsParseError,
),
#[error("invalid encrypted PKCS#8 private key")]
InvalidEncryptedPkcs8PrivateKey,
#[error("invalid PKCS#8 private key")]
InvalidPkcs8PrivateKey,
#[error("PKCS#1 private key does not support encryption with passphrase")]
Pkcs1PrivateKeyDoesNotSupportEncryptionWithPassphrase,
#[error("SEC1 private key does not support encryption with passphrase")]
Sec1PrivateKeyDoesNotSupportEncryptionWithPassphrase,
#[error("unsupported ec named curve")]
UnsupportedEcNamedCurve,
#[error("invalid private key")]
InvalidPrivateKey,
#[error("invalid DSA private key")]
InvalidDsaPrivateKey,
#[error("malformed or missing named curve in ec parameters")]
MalformedOrMissingNamedCurveInEcParameters,
#[error("unsupported key type: {0}")]
UnsupportedKeyType(String),
#[error("unsupported key format: {0}")]
UnsupportedKeyFormat(String),
#[error("invalid x25519 private key")]
InvalidX25519PrivateKey,
#[error("x25519 private key is the wrong length")]
X25519PrivateKeyIsWrongLength,
#[error("invalid Ed25519 private key")]
InvalidEd25519PrivateKey,
#[error("invalid x448 private key")]
InvalidX448PrivateKey,
#[error("x448 private key is the wrong length")]
X448PrivateKeyIsWrongLength,
#[error("invalid Ed448 private key")]
InvalidEd448PrivateKey,
#[error("missing dh parameters")]
MissingDhParameters,
#[class(inherit)]
#[error(transparent)]
UnsupportedPrivateKeyOid(
#[from]
#[inherit]
UnsupportedPrivateKeyOidError,
),
}
#[derive(Debug, thiserror::Error, deno_error::JsError)]
pub enum AsymmetricPublicKeyError {
#[class(type)]
#[error("invalid PEM private key: not valid utf8 starting at byte {0}")]
InvalidPemPrivateKeyInvalidUtf8(usize),
#[class(type)]
#[error("invalid PEM public key")]
InvalidPemPublicKey,
#[class(type)]
#[error("invalid PKCS#1 public key")]
InvalidPkcs1PublicKey,
#[class(inherit)]
#[error(transparent)]
AsymmetricPrivateKey(
#[from]
#[inherit]
AsymmetricPrivateKeyError,
),
#[class(type)]
#[error("invalid x509 certificate")]
InvalidX509Certificate,
#[class(generic)]
#[error(transparent)]
X509(#[from] x509_parser::nom::Err<X509Error>),
#[class(inherit)]
#[error(transparent)]
X509PublicKey(
#[from]
#[inherit]
X509PublicKeyError,
),
#[class(type)]
#[error("unsupported PEM label: {0}")]
UnsupportedPemLabel(String),
#[class(type)]
#[error("invalid SPKI public key")]
InvalidSpkiPublicKey,
#[class(type)]
#[error("unsupported key type: {0}")]
UnsupportedKeyType(String),
#[class(type)]
#[error("unsupported key format: {0}")]
UnsupportedKeyFormat(String),
#[class(generic)]
#[error(transparent)]
Spki(#[from] spki::Error),
#[class(generic)]
#[error(transparent)]
Pkcs1(#[from] rsa::pkcs1::Error),
#[class(inherit)]
#[error(transparent)]
RsaPssParamsParse(
#[from]
#[inherit]
RsaPssParamsParseError,
),
#[class(type)]
#[error("malformed DSS public key")]
MalformedDssPublicKey,
#[class(type)]
#[error("malformed or missing named curve in ec parameters")]
MalformedOrMissingNamedCurveInEcParameters,
#[class(type)]
#[error("malformed or missing public key in ec spki")]
MalformedOrMissingPublicKeyInEcSpki,
#[class(generic)]
#[error(transparent)]
Ec(#[from] elliptic_curve::Error),
#[class(type)]
#[error("unsupported ec named curve")]
UnsupportedEcNamedCurve,
#[class(type)]
#[error("malformed or missing public key in x25519 spki")]
MalformedOrMissingPublicKeyInX25519Spki,
#[class(type)]
#[error("x25519 public key is too short")]
X25519PublicKeyIsTooShort,
#[class(type)]
#[error("invalid Ed25519 public key")]
InvalidEd25519PublicKey,
#[class(type)]
#[error("malformed or missing public key in x448 spki")]
MalformedOrMissingPublicKeyInX448Spki,
#[class(type)]
#[error("x448 public key is too short")]
X448PublicKeyIsTooShort,
#[class(type)]
#[error("invalid Ed448 public key")]
InvalidEd448PublicKey,
#[class(type)]
#[error("missing dh parameters")]
MissingDhParameters,
#[class(type)]
#[error("malformed dh parameters")]
MalformedDhParameters,
#[class(type)]
#[error("malformed or missing public key in dh spki")]
MalformedOrMissingPublicKeyInDhSpki,
#[class(generic)]
#[error("unsupported")]
#[property("code" = "ERR_OSSL_EVP_DECODE_ERROR")]
UnsupportedPrivateKeyOid,
}
trait RsaPublicKeyExt: Sized {
fn from_pkcs1_der_lenient(der: &[u8]) -> Option<Self>;
}
impl RsaPublicKeyExt for RsaPublicKey {
fn from_pkcs1_der_lenient(der: &[u8]) -> Option<Self> {
let mut pos = 0;
let sequence_end = read_der_tag_and_len(der, &mut pos, 0x30)?;
if sequence_end != der.len() {
return None;
}
let n = read_lenient_positive_integer(der, &mut pos, sequence_end)?;
let e = read_lenient_positive_integer(der, &mut pos, sequence_end)?;
if pos != sequence_end {
return None;
}
RsaPublicKey::new(n, e).ok()
}
}
fn read_lenient_positive_integer(
der: &[u8],
pos: &mut usize,
limit: usize,
) -> Option<rsa::BigUint> {
let end = read_der_tag_and_len(der, pos, 0x02)?;
if end > limit || end == *pos {
return None;
}
let payload = &der[*pos..end];
*pos = end;
let first_non_zero = payload.iter().position(|&byte| byte != 0)?;
Some(rsa::BigUint::from_bytes_be(&payload[first_non_zero..]))
}
fn read_der_tag_and_len(der: &[u8], pos: &mut usize, tag: u8) -> Option<usize> {
if der.get(*pos) != Some(&tag) {
return None;
}
*pos += 1;
let len_byte = *der.get(*pos)?;
*pos += 1;
let len = if len_byte & 0x80 == 0 {
usize::from(len_byte)
} else {
let len_len = usize::from(len_byte & 0x7f);
if len_len == 0 || len_len > std::mem::size_of::<usize>() {
return None;
}
let len_bytes = der.get(*pos..pos.checked_add(len_len)?)?;
if len_bytes.first() == Some(&0) {
return None;
}
*pos += len_len;
let mut len = 0usize;
for &byte in len_bytes {
len = len.checked_shl(8)?.checked_add(usize::from(byte))?;
}
if len < 128 {
return None;
}
len
};
pos.checked_add(len).filter(|&end| end <= der.len())
}
fn ec_private_key_from_named_curve_and_sec1_der(
named_curve: Option<const_oid::ObjectIdentifier>,
sec1_der: &[u8],
) -> Result<AsymmetricPrivateKey, AsymmetricPrivateKeyError> {
let ec_key = sec1::EcPrivateKey::from_der(sec1_der)
.map_err(|_| AsymmetricPrivateKeyError::InvalidSec1PrivateKey)?;
let oid = named_curve.ok_or(
AsymmetricPrivateKeyError::MalformedOrMissingNamedCurveInEcParameters,
)?;
match oid {
ID_SECP224R1_OID => {
let key = p224::SecretKey::try_from(ec_key)
.map_err(|_| AsymmetricPrivateKeyError::InvalidSec1PrivateKey)?;
Ok(AsymmetricPrivateKey::Ec(EcPrivateKey::P224(key)))
}
ID_SECP256R1_OID => {
let key = p256::SecretKey::try_from(ec_key)
.map_err(|_| AsymmetricPrivateKeyError::InvalidSec1PrivateKey)?;
Ok(AsymmetricPrivateKey::Ec(EcPrivateKey::P256(key)))
}
ID_SECP384R1_OID => {
let key = p384::SecretKey::try_from(ec_key)
.map_err(|_| AsymmetricPrivateKeyError::InvalidSec1PrivateKey)?;
Ok(AsymmetricPrivateKey::Ec(EcPrivateKey::P384(key)))
}
ID_SECP521R1_OID => {
let key = p521::SecretKey::try_from(ec_key)
.map_err(|_| AsymmetricPrivateKeyError::InvalidSec1PrivateKey)?;
Ok(AsymmetricPrivateKey::Ec(EcPrivateKey::P521(key)))
}
ID_SECP256K1_OID => {
let key = k256::SecretKey::try_from(ec_key)
.map_err(|_| AsymmetricPrivateKeyError::InvalidSec1PrivateKey)?;
Ok(AsymmetricPrivateKey::Ec(EcPrivateKey::Secp256k1(key)))
}
_ => Err(AsymmetricPrivateKeyError::UnsupportedEcNamedCurve),
}
}
fn normalize_pem_line_width(pem: &str) -> Cow<'_, str> {
let mut needs_reformat = false;
let mut header = "";
let mut footer = "";
let mut base64_body = String::new();
let mut in_body = false;
for line in pem.lines() {
if line.starts_with("-----BEGIN ") {
header = line;
in_body = true;
} else if line.starts_with("-----END ") {
footer = line;
in_body = false;
} else if in_body {
let trimmed = line.trim();
if trimmed.len() > 64 {
needs_reformat = true;
}
base64_body.push_str(trimmed);
}
}
if !needs_reformat || header.is_empty() || footer.is_empty() {
return Cow::Borrowed(pem);
}
let mut result = String::with_capacity(pem.len() + 10);
result.push_str(header);
result.push('\n');
for chunk in base64_body.as_bytes().chunks(64) {
result.push_str(std::str::from_utf8(chunk).unwrap_or(""));
result.push('\n');
}
result.push_str(footer);
result.push('\n');
Cow::Owned(result)
}
impl KeyObjectHandle {
pub fn new_asymmetric_private_key_from_js(
key: &[u8],
format: &str,
typ: &str,
passphrase: Option<&[u8]>,
) -> Result<KeyObjectHandle, AsymmetricPrivateKeyError> {
let document = match format {
"pem" => {
let pem = std::str::from_utf8(key).map_err(|err| {
AsymmetricPrivateKeyError::InvalidPemPrivateKeyInvalidUtf8(
err.valid_up_to(),
)
})?;
if let Some((label, decrypted)) =
parse_legacy_encrypted_pem(pem, passphrase)?
{
match label {
"RSA PRIVATE KEY" => SecretDocument::from_pkcs1_der(&decrypted)
.map_err(|_| AsymmetricPrivateKeyError::InvalidPkcs1PrivateKey)?,
"EC PRIVATE KEY" => SecretDocument::from_sec1_der(&decrypted)
.map_err(|_| AsymmetricPrivateKeyError::InvalidSec1PrivateKey)?,
"PRIVATE KEY" => SecretDocument::from_pkcs8_der(&decrypted)
.map_err(|_| AsymmetricPrivateKeyError::InvalidPkcs8PrivateKey)?,
"DSA PRIVATE KEY" => {
let private_key = parse_traditional_dsa_private_key(&decrypted)?;
return Ok(KeyObjectHandle::AsymmetricPrivate(
AsymmetricPrivateKey::Dsa(private_key),
));
}
_ => {
return Err(AsymmetricPrivateKeyError::UnsupportedPemLabel(
label.to_string(),
));
}
}
} else if let Some(passphrase) = passphrase {
match SecretDocument::from_pkcs8_encrypted_pem(pem, passphrase) {
Ok(doc) => doc,
Err(pkcs8::Error::EncryptedPrivateKey(_)) => {
return Err(
AsymmetricPrivateKeyError::EncryptedPrivateKeyBadDecrypt,
);
}
Err(_) => {
let normalized = normalize_pem_line_width(pem);
let (label, doc) = SecretDocument::from_pem(&normalized)
.map_err(|_| AsymmetricPrivateKeyError::InvalidPemPrivateKey)?;
match label {
PrivateKeyInfo::PEM_LABEL => doc,
rsa::pkcs1::RsaPrivateKey::PEM_LABEL => {
SecretDocument::from_pkcs1_der(doc.as_bytes()).map_err(
|_| AsymmetricPrivateKeyError::InvalidPkcs1PrivateKey,
)?
}
sec1::EcPrivateKey::PEM_LABEL => {
SecretDocument::from_sec1_der(doc.as_bytes()).map_err(
|_| AsymmetricPrivateKeyError::InvalidSec1PrivateKey,
)?
}
_ => {
return Err(
AsymmetricPrivateKeyError::InvalidEncryptedPemPrivateKey,
);
}
}
}
}
} else {
let pem = skip_ec_parameters_block(pem);
let normalized = normalize_pem_line_width(pem);
let (label, doc) = SecretDocument::from_pem(&normalized)
.map_err(|_| AsymmetricPrivateKeyError::InvalidPemPrivateKey)?;
match label {
EncryptedPrivateKeyInfo::PEM_LABEL => {
return Err(AsymmetricPrivateKeyError::EncryptedPrivateKeyRequiresPassphraseToDecrypt);
}
PrivateKeyInfo::PEM_LABEL => doc,
rsa::pkcs1::RsaPrivateKey::PEM_LABEL => {
let pkcs1_der = doc.as_bytes();
SecretDocument::from_pkcs1_der(pkcs1_der).map_err(|_| {
AsymmetricPrivateKeyError::InvalidPkcs1PrivateKey
})?
}
sec1::EcPrivateKey::PEM_LABEL => {
SecretDocument::from_sec1_der(doc.as_bytes())
.map_err(|_| AsymmetricPrivateKeyError::InvalidSec1PrivateKey)?
}
"DSA PRIVATE KEY" => {
let private_key =
parse_traditional_dsa_private_key(doc.as_bytes())?;
return Ok(KeyObjectHandle::AsymmetricPrivate(
AsymmetricPrivateKey::Dsa(private_key),
));
}
_ => {
return Err(AsymmetricPrivateKeyError::UnsupportedPemLabel(
label.to_string(),
));
}
}
}
}
"der" => match typ {
"pkcs8" => {
if let Some(passphrase) = passphrase {
if EncryptedPrivateKeyInfo::try_from(key).is_ok() {
SecretDocument::from_pkcs8_encrypted_der(key, passphrase)
.map_err(|_| {
AsymmetricPrivateKeyError::InvalidEncryptedPkcs8PrivateKey
})?
} else {
SecretDocument::from_pkcs8_der(key).map_err(|_| {
AsymmetricPrivateKeyError::InvalidPkcs8PrivateKey
})?
}
} else if EncryptedPrivateKeyInfo::try_from(key).is_ok() {
return Err(
AsymmetricPrivateKeyError::EncryptedPkcs8DerRequiresPassphrase,
);
} else {
SecretDocument::from_pkcs8_der(key)
.map_err(|_| AsymmetricPrivateKeyError::InvalidPkcs8PrivateKey)?
}
}
"pkcs1" => {
if passphrase.is_some() {
return Err(AsymmetricPrivateKeyError::Pkcs1PrivateKeyDoesNotSupportEncryptionWithPassphrase);
}
SecretDocument::from_pkcs1_der(key)
.map_err(|_| AsymmetricPrivateKeyError::InvalidPkcs1PrivateKey)?
}
"sec1" => {
if passphrase.is_some() {
return Err(AsymmetricPrivateKeyError::Sec1PrivateKeyDoesNotSupportEncryptionWithPassphrase);
}
SecretDocument::from_sec1_der(key)
.map_err(|_| AsymmetricPrivateKeyError::InvalidSec1PrivateKey)?
}
_ => {
return Err(AsymmetricPrivateKeyError::UnsupportedKeyType(
typ.to_string(),
));
}
},
_ => {
return Err(AsymmetricPrivateKeyError::UnsupportedKeyFormat(
format.to_string(),
));
}
};
let document_bytes = document.as_bytes();
let pk_info = PrivateKeyInfo::try_from(document_bytes)
.map_err(|_| AsymmetricPrivateKeyError::InvalidPrivateKey)?;
let alg = pk_info.algorithm.oid;
let private_key = match alg {
RSA_ENCRYPTION_OID => {
let private_key =
rsa::RsaPrivateKey::from_pkcs1_der(pk_info.private_key)
.map_err(|_| AsymmetricPrivateKeyError::InvalidPkcs1PrivateKey)?;
AsymmetricPrivateKey::Rsa(private_key)
}
RSASSA_PSS_OID => {
let details = parse_rsa_pss_params(pk_info.algorithm.parameters)?;
let private_key =
rsa::RsaPrivateKey::from_pkcs1_der(pk_info.private_key)
.map_err(|_| AsymmetricPrivateKeyError::InvalidPkcs1PrivateKey)?;
AsymmetricPrivateKey::RsaPss(RsaPssPrivateKey {
key: private_key,
details,
})
}
DSA_OID => {
let private_key = dsa::SigningKey::try_from(pk_info)
.map_err(|_| AsymmetricPrivateKeyError::InvalidDsaPrivateKey)?;
AsymmetricPrivateKey::Dsa(private_key)
}
EC_OID => {
let named_curve =
pk_info.algorithm.parameters_oid().ok().or_else(|| {
let ec_key =
sec1::EcPrivateKey::from_der(pk_info.private_key).ok()?;
ec_key.parameters.and_then(|p| p.named_curve())
});
ec_private_key_from_named_curve_and_sec1_der(
named_curve,
pk_info.private_key,
)?
}
X25519_OID => {
let string_ref = OctetStringRef::from_der(pk_info.private_key)
.map_err(|_| AsymmetricPrivateKeyError::InvalidX25519PrivateKey)?;
if string_ref.as_bytes().len() != 32 {
return Err(AsymmetricPrivateKeyError::X25519PrivateKeyIsWrongLength);
}
let mut bytes = [0; 32];
bytes.copy_from_slice(string_ref.as_bytes());
AsymmetricPrivateKey::X25519(x25519_dalek::StaticSecret::from(bytes))
}
ED25519_OID => {
let signing_key = ed25519_dalek::SigningKey::try_from(pk_info)
.map_err(|_| AsymmetricPrivateKeyError::InvalidEd25519PrivateKey)?;
AsymmetricPrivateKey::Ed25519(signing_key)
}
X448_OID => {
let string_ref = OctetStringRef::from_der(pk_info.private_key)
.map_err(|_| AsymmetricPrivateKeyError::InvalidX448PrivateKey)?;
if string_ref.as_bytes().len() != 56 {
return Err(AsymmetricPrivateKeyError::X448PrivateKeyIsWrongLength);
}
let mut bytes = [0u8; 56];
bytes.copy_from_slice(string_ref.as_bytes());
AsymmetricPrivateKey::X448(bytes)
}
ED448_OID => {
let string_ref = OctetStringRef::from_der(pk_info.private_key)
.map_err(|_| AsymmetricPrivateKeyError::InvalidEd448PrivateKey)?;
let key_bytes = string_ref.as_bytes();
if key_bytes.len() != 57 {
return Err(AsymmetricPrivateKeyError::InvalidEd448PrivateKey);
}
let mut seed = [0u8; 57];
seed.copy_from_slice(key_bytes);
let seed = ed448_goldilocks::EdwardsScalarBytes::from(seed);
AsymmetricPrivateKey::Ed448(ed448_goldilocks::SigningKey::from(seed))
}
DH_KEY_AGREEMENT_OID => {
let params = pk_info
.algorithm
.parameters
.ok_or(AsymmetricPrivateKeyError::MissingDhParameters)?;
let params = pkcs3::DhParameter::from_der(¶ms.to_der().unwrap())
.map_err(|_| AsymmetricPrivateKeyError::MissingDhParameters)?;
let private_key_int =
<AnyRef<'_> as spki::der::Decode>::from_der(pk_info.private_key)
.map_err(|_| AsymmetricPrivateKeyError::MissingDhParameters)?;
AsymmetricPrivateKey::Dh(DhPrivateKey {
key: dh::PrivateKey::from_bytes(private_key_int.value()),
params,
})
}
_ => return Err(UnsupportedPrivateKeyOidError.into()),
};
Ok(KeyObjectHandle::AsymmetricPrivate(private_key))
}
pub fn new_x509_public_key(
spki: &x509::SubjectPublicKeyInfo,
) -> Result<KeyObjectHandle, X509PublicKeyError> {
use x509_parser::der_parser::asn1_rs::oid;
use x509_parser::public_key::PublicKey;
let key = match spki.parsed()? {
PublicKey::RSA(key) => {
let public_key = RsaPublicKey::new(
rsa::BigUint::from_bytes_be(key.modulus),
rsa::BigUint::from_bytes_be(key.exponent),
)?;
AsymmetricPublicKey::Rsa(public_key)
}
PublicKey::EC(point) => {
let data = point.data();
if let Some(params) = &spki.algorithm.parameters {
let curve_oid = params.as_oid()?;
const ID_SECP224R1: &[u8] = &oid!(raw 1.3.132.0.33);
const ID_SECP256R1: &[u8] = &oid!(raw 1.2.840.10045.3.1.7);
const ID_SECP384R1: &[u8] = &oid!(raw 1.3.132.0.34);
const ID_SECP521R1: &[u8] = &oid!(raw 1.3.132.0.35);
const ID_SECP256K1: &[u8] = &oid!(raw 1.3.132.0.10);
match curve_oid.as_bytes() {
ID_SECP224R1 => {
let public_key = p224::PublicKey::from_sec1_bytes(data)?;
AsymmetricPublicKey::Ec(EcPublicKey::P224(public_key))
}
ID_SECP256R1 => {
let public_key = p256::PublicKey::from_sec1_bytes(data)?;
AsymmetricPublicKey::Ec(EcPublicKey::P256(public_key))
}
ID_SECP384R1 => {
let public_key = p384::PublicKey::from_sec1_bytes(data)?;
AsymmetricPublicKey::Ec(EcPublicKey::P384(public_key))
}
ID_SECP521R1 => {
let public_key = p521::PublicKey::from_sec1_bytes(data)?;
AsymmetricPublicKey::Ec(EcPublicKey::P521(public_key))
}
ID_SECP256K1 => {
let public_key = k256::PublicKey::from_sec1_bytes(data)?;
AsymmetricPublicKey::Ec(EcPublicKey::Secp256k1(public_key))
}
_ => return Err(X509PublicKeyError::UnsupportedEcNamedCurve),
}
} else {
return Err(X509PublicKeyError::MissingEcParameters);
}
}
PublicKey::DSA(_) => {
let verifying_key = dsa::VerifyingKey::from_public_key_der(spki.raw)
.map_err(|_| X509PublicKeyError::MalformedDssPublicKey)?;
AsymmetricPublicKey::Dsa(verifying_key)
}
_ => {
const ID_ED25519: &[u8] = &oid!(raw 1.3.101.112);
const ID_X25519: &[u8] = &oid!(raw 1.3.101.110);
const ID_ED448: &[u8] = &oid!(raw 1.3.101.113);
const ID_X448: &[u8] = &oid!(raw 1.3.101.111);
match spki.algorithm.algorithm.as_bytes() {
ID_ED25519 => {
let data = spki.subject_public_key.as_ref();
let key_bytes: [u8; 32] = data
.try_into()
.map_err(|_| X509PublicKeyError::InvalidEd25519Key)?;
let verifying_key =
ed25519_dalek::VerifyingKey::from_bytes(&key_bytes)
.map_err(|_| X509PublicKeyError::InvalidEd25519Key)?;
AsymmetricPublicKey::Ed25519(verifying_key)
}
ID_X25519 => {
let data: &[u8] = spki.subject_public_key.as_ref();
let data: [u8; 32] = data
.try_into()
.map_err(|_| X509PublicKeyError::InvalidX25519Key)?;
AsymmetricPublicKey::X25519(x25519_dalek::PublicKey::from(data))
}
ID_ED448 => {
let data = spki.subject_public_key.as_ref();
let point_bytes: &[u8; 57] = data
.try_into()
.map_err(|_| X509PublicKeyError::InvalidEd448Key)?;
let vk = ed448_goldilocks::VerifyingKey::from_bytes(point_bytes)
.map_err(|_| X509PublicKeyError::InvalidEd448Key)?;
AsymmetricPublicKey::Ed448(vk)
}
ID_X448 => {
let data: &[u8] = spki.subject_public_key.as_ref();
let data: [u8; 56] = data
.try_into()
.map_err(|_| X509PublicKeyError::InvalidX25519Key)?;
AsymmetricPublicKey::X448(data)
}
_ => return Err(X509PublicKeyError::UnsupportedX509KeyType),
}
}
};
Ok(KeyObjectHandle::AsymmetricPublic(key))
}
pub fn new_rsa_jwk(
jwk: RsaJwkKey,
is_public: bool,
) -> Result<KeyObjectHandle, RsaJwkError> {
use base64::prelude::BASE64_URL_SAFE_NO_PAD;
let n = BASE64_URL_SAFE_NO_PAD.decode(jwk.n.as_bytes())?;
let e = BASE64_URL_SAFE_NO_PAD.decode(jwk.e.as_bytes())?;
if is_public {
let public_key = RsaPublicKey::new(
rsa::BigUint::from_bytes_be(&n),
rsa::BigUint::from_bytes_be(&e),
)?;
Ok(KeyObjectHandle::AsymmetricPublic(AsymmetricPublicKey::Rsa(
public_key,
)))
} else {
let d = BASE64_URL_SAFE_NO_PAD.decode(
jwk
.d
.ok_or(RsaJwkError::MissingRsaPrivateComponent)?
.as_bytes(),
)?;
let p = BASE64_URL_SAFE_NO_PAD.decode(
jwk
.p
.ok_or(RsaJwkError::MissingRsaPrivateComponent)?
.as_bytes(),
)?;
let q = BASE64_URL_SAFE_NO_PAD.decode(
jwk
.q
.ok_or(RsaJwkError::MissingRsaPrivateComponent)?
.as_bytes(),
)?;
let mut private_key = RsaPrivateKey::from_components(
rsa::BigUint::from_bytes_be(&n),
rsa::BigUint::from_bytes_be(&e),
rsa::BigUint::from_bytes_be(&d),
vec![
rsa::BigUint::from_bytes_be(&p),
rsa::BigUint::from_bytes_be(&q),
],
)?;
private_key.precompute()?;
Ok(KeyObjectHandle::AsymmetricPrivate(
AsymmetricPrivateKey::Rsa(private_key),
))
}
}
pub fn new_ec_jwk(
jwk: &JwkEcKey,
is_public: bool,
) -> Result<KeyObjectHandle, EcJwkError> {
let handle = match jwk.crv() {
"P-256" if is_public => {
KeyObjectHandle::AsymmetricPublic(AsymmetricPublicKey::Ec(
EcPublicKey::P256(p256::PublicKey::from_jwk(jwk)?),
))
}
"P-256" => KeyObjectHandle::AsymmetricPrivate(AsymmetricPrivateKey::Ec(
EcPrivateKey::P256(p256::SecretKey::from_jwk(jwk)?),
)),
"P-384" if is_public => {
KeyObjectHandle::AsymmetricPublic(AsymmetricPublicKey::Ec(
EcPublicKey::P384(p384::PublicKey::from_jwk(jwk)?),
))
}
"P-384" => KeyObjectHandle::AsymmetricPrivate(AsymmetricPrivateKey::Ec(
EcPrivateKey::P384(p384::SecretKey::from_jwk(jwk)?),
)),
"P-521" if is_public => {
KeyObjectHandle::AsymmetricPublic(AsymmetricPublicKey::Ec(
EcPublicKey::P521(p521::PublicKey::from_jwk(jwk)?),
))
}
"P-521" => KeyObjectHandle::AsymmetricPrivate(AsymmetricPrivateKey::Ec(
EcPrivateKey::P521(p521::SecretKey::from_jwk(jwk)?),
)),
"secp256k1" if is_public => {
KeyObjectHandle::AsymmetricPublic(AsymmetricPublicKey::Ec(
EcPublicKey::Secp256k1(k256::PublicKey::from_jwk(jwk)?),
))
}
"secp256k1" => {
KeyObjectHandle::AsymmetricPrivate(AsymmetricPrivateKey::Ec(
EcPrivateKey::Secp256k1(k256::SecretKey::from_jwk(jwk)?),
))
}
_ => {
return Err(EcJwkError::UnsupportedCurve(jwk.crv().to_string()));
}
};
Ok(handle)
}
pub fn new_ed_raw(
curve: &str,
data: &[u8],
is_public: bool,
) -> Result<KeyObjectHandle, EdRawError> {
match curve {
"Ed25519" => {
let data =
data.try_into().map_err(|_| EdRawError::InvalidEd25519Key)?;
if !is_public {
Ok(KeyObjectHandle::AsymmetricPrivate(
AsymmetricPrivateKey::Ed25519(
ed25519_dalek::SigningKey::from_bytes(data),
),
))
} else {
Ok(KeyObjectHandle::AsymmetricPublic(
AsymmetricPublicKey::Ed25519(
ed25519_dalek::VerifyingKey::from_bytes(data)?,
),
))
}
}
"X25519" => {
let data: [u8; 32] =
data.try_into().map_err(|_| EdRawError::InvalidEd25519Key)?;
if !is_public {
Ok(KeyObjectHandle::AsymmetricPrivate(
AsymmetricPrivateKey::X25519(x25519_dalek::StaticSecret::from(
data,
)),
))
} else {
Ok(KeyObjectHandle::AsymmetricPublic(
AsymmetricPublicKey::X25519(x25519_dalek::PublicKey::from(data)),
))
}
}
"Ed448" => {
if !is_public {
let key_bytes: [u8; 57] =
data.try_into().map_err(|_| EdRawError::InvalidEd448Key)?;
let seed = ed448_goldilocks::EdwardsScalarBytes::from(key_bytes);
Ok(KeyObjectHandle::AsymmetricPrivate(
AsymmetricPrivateKey::Ed448(ed448_goldilocks::SigningKey::from(
seed,
)),
))
} else {
let point_bytes: &[u8; 57] =
data.try_into().map_err(|_| EdRawError::InvalidEd448Key)?;
let vk = ed448_goldilocks::VerifyingKey::from_bytes(point_bytes)
.map_err(|_| EdRawError::InvalidEd448Key)?;
Ok(KeyObjectHandle::AsymmetricPublic(
AsymmetricPublicKey::Ed448(vk),
))
}
}
"X448" => {
let data: [u8; 56] =
data.try_into().map_err(|_| EdRawError::InvalidX448Key)?;
if !is_public {
Ok(KeyObjectHandle::AsymmetricPrivate(
AsymmetricPrivateKey::X448(data),
))
} else {
Ok(KeyObjectHandle::AsymmetricPublic(
AsymmetricPublicKey::X448(data),
))
}
}
_ => Err(EdRawError::UnsupportedCurve),
}
}
pub fn new_asymmetric_public_key_from_js(
key: &[u8],
format: &str,
typ: &str,
passphrase: Option<&[u8]>,
) -> Result<KeyObjectHandle, AsymmetricPublicKeyError> {
let document = match format {
"pem" => {
let pem = std::str::from_utf8(key).map_err(|err| {
AsymmetricPublicKeyError::InvalidPemPrivateKeyInvalidUtf8(
err.valid_up_to(),
)
})?;
if pem.contains("Proc-Type: 4,ENCRYPTED") {
let handle = KeyObjectHandle::new_asymmetric_private_key_from_js(
key, format, typ, passphrase,
)?;
match handle {
KeyObjectHandle::AsymmetricPrivate(private) => {
return Ok(KeyObjectHandle::AsymmetricPublic(
private.to_public_key(),
));
}
KeyObjectHandle::AsymmetricPublic(_)
| KeyObjectHandle::Secret(_) => unreachable!(),
}
}
let (label, document) = decode_pem_lenient(pem)
.ok_or(AsymmetricPublicKeyError::InvalidPemPublicKey)?;
match label.as_str() {
SubjectPublicKeyInfoRef::PEM_LABEL => document,
rsa::pkcs1::RsaPublicKey::PEM_LABEL => {
Document::from_pkcs1_der(document.as_bytes())
.map_err(|_| AsymmetricPublicKeyError::InvalidPkcs1PublicKey)?
}
EncryptedPrivateKeyInfo::PEM_LABEL
| PrivateKeyInfo::PEM_LABEL
| sec1::EcPrivateKey::PEM_LABEL
| rsa::pkcs1::RsaPrivateKey::PEM_LABEL
| "DSA PRIVATE KEY" => {
let handle = KeyObjectHandle::new_asymmetric_private_key_from_js(
key, format, typ, passphrase,
)?;
match handle {
KeyObjectHandle::AsymmetricPrivate(private) => {
return Ok(KeyObjectHandle::AsymmetricPublic(
private.to_public_key(),
));
}
KeyObjectHandle::AsymmetricPublic(_)
| KeyObjectHandle::Secret(_) => unreachable!(),
}
}
"CERTIFICATE" => {
let (_, pem) = x509_parser::pem::parse_x509_pem(pem.as_bytes())
.map_err(|_| AsymmetricPublicKeyError::InvalidX509Certificate)?;
let cert = pem.parse_x509()?;
let public_key = cert.tbs_certificate.subject_pki;
return KeyObjectHandle::new_x509_public_key(&public_key)
.map_err(Into::into);
}
_ => {
return Err(AsymmetricPublicKeyError::UnsupportedPemLabel(
label.to_string(),
));
}
}
}
"der" => match typ {
"pkcs1" => Document::from_pkcs1_der(key)
.map_err(|_| AsymmetricPublicKeyError::InvalidPkcs1PublicKey)?,
"spki" => Document::from_public_key_der(key)
.map_err(|_| AsymmetricPublicKeyError::InvalidSpkiPublicKey)?,
_ => {
return Err(AsymmetricPublicKeyError::UnsupportedKeyType(
typ.to_string(),
));
}
},
_ => {
return Err(AsymmetricPublicKeyError::UnsupportedKeyType(
format.to_string(),
));
}
};
let spki = SubjectPublicKeyInfoRef::try_from(document.as_bytes())?;
let public_key = match spki.algorithm.oid {
RSA_ENCRYPTION_OID => {
let der = spki
.subject_public_key
.as_bytes()
.ok_or(AsymmetricPublicKeyError::InvalidSpkiPublicKey)?;
let public_key = RsaPublicKey::from_pkcs1_der(der).or_else(|err| {
RsaPublicKey::from_pkcs1_der_lenient(der).ok_or(err)
})?;
AsymmetricPublicKey::Rsa(public_key)
}
RSASSA_PSS_OID => {
let details = parse_rsa_pss_params(spki.algorithm.parameters)?;
let der = spki
.subject_public_key
.as_bytes()
.ok_or(AsymmetricPublicKeyError::InvalidSpkiPublicKey)?;
let public_key = RsaPublicKey::from_pkcs1_der(der).or_else(|err| {
RsaPublicKey::from_pkcs1_der_lenient(der).ok_or(err)
})?;
AsymmetricPublicKey::RsaPss(RsaPssPublicKey {
key: public_key,
details,
})
}
DSA_OID => {
let verifying_key = dsa::VerifyingKey::try_from(spki)
.map_err(|_| AsymmetricPublicKeyError::MalformedDssPublicKey)?;
AsymmetricPublicKey::Dsa(verifying_key)
}
EC_OID => {
let named_curve = spki.algorithm.parameters_oid().map_err(|_| {
AsymmetricPublicKeyError::MalformedOrMissingNamedCurveInEcParameters
})?;
let data = spki.subject_public_key.as_bytes().ok_or(
AsymmetricPublicKeyError::MalformedOrMissingPublicKeyInEcSpki,
)?;
match named_curve {
ID_SECP224R1_OID => {
let public_key = p224::PublicKey::from_sec1_bytes(data)?;
AsymmetricPublicKey::Ec(EcPublicKey::P224(public_key))
}
ID_SECP256R1_OID => {
let public_key = p256::PublicKey::from_sec1_bytes(data)?;
AsymmetricPublicKey::Ec(EcPublicKey::P256(public_key))
}
ID_SECP384R1_OID => {
let public_key = p384::PublicKey::from_sec1_bytes(data)?;
AsymmetricPublicKey::Ec(EcPublicKey::P384(public_key))
}
ID_SECP521R1_OID => {
let public_key = p521::PublicKey::from_sec1_bytes(data)?;
AsymmetricPublicKey::Ec(EcPublicKey::P521(public_key))
}
ID_SECP256K1_OID => {
let public_key = k256::PublicKey::from_sec1_bytes(data)?;
AsymmetricPublicKey::Ec(EcPublicKey::Secp256k1(public_key))
}
_ => return Err(AsymmetricPublicKeyError::UnsupportedEcNamedCurve),
}
}
X25519_OID => {
let mut bytes = [0; 32];
let data = spki.subject_public_key.as_bytes().ok_or(
AsymmetricPublicKeyError::MalformedOrMissingPublicKeyInX25519Spki,
)?;
if data.len() < 32 {
return Err(AsymmetricPublicKeyError::X25519PublicKeyIsTooShort);
}
bytes.copy_from_slice(&data[0..32]);
AsymmetricPublicKey::X25519(x25519_dalek::PublicKey::from(bytes))
}
ED25519_OID => {
let verifying_key = ed25519_dalek::VerifyingKey::try_from(spki)
.map_err(|_| AsymmetricPublicKeyError::InvalidEd25519PublicKey)?;
AsymmetricPublicKey::Ed25519(verifying_key)
}
X448_OID => {
let mut bytes = [0; 56];
let data = spki.subject_public_key.as_bytes().ok_or(
AsymmetricPublicKeyError::MalformedOrMissingPublicKeyInX448Spki,
)?;
if data.len() < 56 {
return Err(AsymmetricPublicKeyError::X448PublicKeyIsTooShort);
}
bytes.copy_from_slice(&data[0..56]);
AsymmetricPublicKey::X448(bytes)
}
ED448_OID => {
let data = spki
.subject_public_key
.as_bytes()
.ok_or(AsymmetricPublicKeyError::InvalidEd448PublicKey)?;
let point_bytes: &[u8; 57] = data
.try_into()
.map_err(|_| AsymmetricPublicKeyError::InvalidEd448PublicKey)?;
let vk = ed448_goldilocks::VerifyingKey::from_bytes(point_bytes)
.map_err(|_| AsymmetricPublicKeyError::InvalidEd448PublicKey)?;
AsymmetricPublicKey::Ed448(vk)
}
DH_KEY_AGREEMENT_OID => {
let params = spki
.algorithm
.parameters
.ok_or(AsymmetricPublicKeyError::MissingDhParameters)?;
let params = pkcs3::DhParameter::from_der(¶ms.to_der().unwrap())
.map_err(|_| AsymmetricPublicKeyError::MalformedDhParameters)?;
let Some(subject_public_key) = spki.subject_public_key.as_bytes()
else {
return Err(
AsymmetricPublicKeyError::MalformedOrMissingPublicKeyInDhSpki,
);
};
let public_key_int =
<AnyRef<'_> as spki::der::Decode>::from_der(subject_public_key)
.map_err(|_| {
AsymmetricPublicKeyError::MalformedOrMissingPublicKeyInDhSpki
})?;
AsymmetricPublicKey::Dh(DhPublicKey {
key: dh::PublicKey::from_bytes(public_key_int.value()),
params,
})
}
_ => return Err(AsymmetricPublicKeyError::UnsupportedPrivateKeyOid),
};
Ok(KeyObjectHandle::AsymmetricPublic(public_key))
}
}
#[derive(Debug, thiserror::Error, deno_error::JsError)]
#[class(type)]
pub enum RsaPssParamsParseError {
#[error("malformed pss private key parameters")]
MalformedPssPrivateKeyParameters,
#[error("unsupported pss hash algorithm")]
UnsupportedPssHashAlgorithm,
#[error("unsupported pss mask gen algorithm")]
UnsupportedPssMaskGenAlgorithm,
#[error("malformed or missing pss mask gen algorithm parameters")]
MalformedOrMissingPssMaskGenAlgorithm,
}
fn skip_ec_parameters_block(pem: &str) -> &str {
const BEGIN_EC_PARAMS: &str = "-----BEGIN EC PARAMETERS-----";
const END_EC_PARAMS: &str = "-----END EC PARAMETERS-----";
let trimmed = pem.trim_start();
if trimmed.starts_with(BEGIN_EC_PARAMS)
&& let Some(pos) = trimmed.find(END_EC_PARAMS)
{
return trimmed[pos + END_EC_PARAMS.len()..].trim_start();
}
trimmed
}
fn parse_traditional_dsa_private_key(
der: &[u8],
) -> Result<dsa::SigningKey, AsymmetricPrivateKeyError> {
use spki::der::Decode;
use spki::der::Reader as _;
use spki::der::SliceReader;
let err = || AsymmetricPrivateKeyError::InvalidDsaPrivateKey;
let mut reader = SliceReader::new(der).map_err(|_| err())?;
reader
.sequence(|seq_reader| {
let _version = asn1::UintRef::decode(seq_reader)?;
let p_ref = asn1::UintRef::decode(seq_reader)?;
let q_ref = asn1::UintRef::decode(seq_reader)?;
let g_ref = asn1::UintRef::decode(seq_reader)?;
let y_ref = asn1::UintRef::decode(seq_reader)?;
let x_ref = asn1::UintRef::decode(seq_reader)?;
let p = num_bigint_dig::BigUint::from_bytes_be(p_ref.as_bytes());
let q = num_bigint_dig::BigUint::from_bytes_be(q_ref.as_bytes());
let g = num_bigint_dig::BigUint::from_bytes_be(g_ref.as_bytes());
let y = num_bigint_dig::BigUint::from_bytes_be(y_ref.as_bytes());
let x = num_bigint_dig::BigUint::from_bytes_be(x_ref.as_bytes());
let components = dsa::Components::from_components(p, q, g)
.map_err(|_| spki::der::Tag::Sequence.value_error())?;
let verifying_key = dsa::VerifyingKey::from_components(components, y)
.map_err(|_| spki::der::Tag::Sequence.value_error())?;
dsa::SigningKey::from_components(verifying_key, x)
.map_err(|_| spki::der::Tag::Sequence.value_error())
})
.map_err(|_| err())
}
fn decode_pem_lenient(pem: &str) -> Option<(String, Document)> {
if let Ok((label, doc)) = Document::from_pem(pem) {
return Some((label.to_string(), doc));
}
let pem = pem.trim();
let first_line = pem.lines().next()?;
let last_line = pem.lines().next_back()?;
let label = first_line
.strip_prefix("-----BEGIN ")
.and_then(|s: &str| s.strip_suffix("-----"))?;
let end_label = last_line
.strip_prefix("-----END ")
.and_then(|s: &str| s.strip_suffix("-----"))?;
if label != end_label {
return None;
}
let b64: String = pem
.lines()
.filter(|line| !line.starts_with("-----"))
.flat_map(|line| line.chars())
.filter(|c| !c.is_whitespace())
.collect();
let der = base64::engine::general_purpose::STANDARD
.decode(&b64)
.ok()?;
let doc = Document::from_der(&der).ok()?;
Some((label.to_string(), doc))
}
fn parse_rsa_pss_params(
parameters: Option<AnyRef<'_>>,
) -> Result<Option<RsaPssDetails>, RsaPssParamsParseError> {
let details = if let Some(parameters) = parameters {
let params = RsaPssParameters::try_from(parameters)
.map_err(|_| RsaPssParamsParseError::MalformedPssPrivateKeyParameters)?;
let hash_algorithm = match params.hash_algorithm.map(|k| k.oid) {
Some(ID_SHA1_OID) => RsaPssHashAlgorithm::Sha1,
Some(ID_SHA224_OID) => RsaPssHashAlgorithm::Sha224,
Some(ID_SHA256_OID) => RsaPssHashAlgorithm::Sha256,
Some(ID_SHA384_OID) => RsaPssHashAlgorithm::Sha384,
Some(ID_SHA512_OID) => RsaPssHashAlgorithm::Sha512,
Some(ID_SHA512_224_OID) => RsaPssHashAlgorithm::Sha512_224,
Some(ID_SHA512_256_OID) => RsaPssHashAlgorithm::Sha512_256,
None => RsaPssHashAlgorithm::Sha1,
_ => return Err(RsaPssParamsParseError::UnsupportedPssHashAlgorithm),
};
let mf1_hash_algorithm = match params.mask_gen_algorithm {
Some(alg) => {
if alg.oid != ID_MFG1 {
return Err(RsaPssParamsParseError::UnsupportedPssMaskGenAlgorithm);
}
let mgf1_params_any = alg.parameters.ok_or(
RsaPssParamsParseError::MalformedOrMissingPssMaskGenAlgorithm,
)?;
let mgf1_hash_oid = mgf1_params_any
.sequence(|reader| {
let oid = rsa::pkcs8::ObjectIdentifier::decode(reader)?;
while !reader.is_finished() {
rsa::pkcs8::der::asn1::AnyRef::decode(reader)?;
}
Ok(oid)
})
.map_err(|_| {
RsaPssParamsParseError::MalformedOrMissingPssMaskGenAlgorithm
})?;
match mgf1_hash_oid {
ID_SHA1_OID => RsaPssHashAlgorithm::Sha1,
ID_SHA224_OID => RsaPssHashAlgorithm::Sha224,
ID_SHA256_OID => RsaPssHashAlgorithm::Sha256,
ID_SHA384_OID => RsaPssHashAlgorithm::Sha384,
ID_SHA512_OID => RsaPssHashAlgorithm::Sha512,
ID_SHA512_224_OID => RsaPssHashAlgorithm::Sha512_224,
ID_SHA512_256_OID => RsaPssHashAlgorithm::Sha512_256,
_ => {
return Err(RsaPssParamsParseError::UnsupportedPssMaskGenAlgorithm);
}
}
}
None => hash_algorithm,
};
let salt_length = params.salt_length.unwrap_or(20);
Some(RsaPssDetails {
hash_algorithm,
mf1_hash_algorithm,
salt_length,
})
} else {
None
};
Ok(details)
}
fn bytes_to_b64(bytes: &[u8]) -> String {
use base64::prelude::BASE64_URL_SAFE_NO_PAD;
BASE64_URL_SAFE_NO_PAD.encode(bytes)
}
#[derive(Debug, thiserror::Error, deno_error::JsError)]
#[class(type)]
pub enum AsymmetricPrivateKeyJwkError {
#[error("key is not an asymmetric private key")]
KeyIsNotAsymmetricPrivateKey,
#[class(generic)]
#[property("code" = "ERR_CRYPTO_JWK_UNSUPPORTED_CURVE")]
#[error("Unsupported JWK EC curve: secp224r1.")]
UnsupportedJwkEcCurveP224,
#[class(generic)]
#[property("code" = "ERR_CRYPTO_JWK_UNSUPPORTED_KEY_TYPE")]
#[error("Unsupported JWK Key Type.")]
JwkExportNotImplementedForKeyType,
}
#[derive(Debug, thiserror::Error, deno_error::JsError)]
#[class(type)]
pub enum AsymmetricPublicKeyJwkError {
#[error("key is not an asymmetric public key")]
KeyIsNotAsymmetricPublicKey,
#[class(generic)]
#[property("code" = "ERR_CRYPTO_JWK_UNSUPPORTED_CURVE")]
#[error("Unsupported JWK EC curve: secp224r1.")]
UnsupportedJwkEcCurveP224,
#[class(generic)]
#[property("code" = "ERR_CRYPTO_JWK_UNSUPPORTED_KEY_TYPE")]
#[error("Unsupported JWK Key Type.")]
JwkExportNotImplementedForKeyType,
}
#[derive(Debug, thiserror::Error, deno_error::JsError)]
#[class(type)]
pub enum AsymmetricPublicKeyDerError {
#[error("key is not an asymmetric public key")]
KeyIsNotAsymmetricPublicKey,
#[error("invalid RSA public key")]
InvalidRsaPublicKey,
#[error("exporting non-RSA public key as PKCS#1 is not supported")]
ExportingNonRsaPublicKeyAsPkcs1Unsupported,
#[error("invalid EC public key")]
InvalidEcPublicKey,
#[error("exporting RSA-PSS public key as SPKI is not supported yet")]
ExportingNonRsaPssPublicKeyAsSpkiUnsupported,
#[error("invalid DSA public key")]
InvalidDsaPublicKey,
#[error("invalid X25519 public key")]
InvalidX25519PublicKey,
#[error("invalid Ed25519 public key")]
InvalidEd25519PublicKey,
#[error("invalid X448 public key")]
InvalidX448PublicKey,
#[error("invalid Ed448 public key")]
InvalidEd448PublicKey,
#[error("invalid DH public key")]
InvalidDhPublicKey,
#[error("unsupported key type: {0}")]
UnsupportedKeyType(String),
}
impl AsymmetricPublicKey {
fn export_jwk(
&self,
) -> Result<deno_core::serde_json::Value, AsymmetricPublicKeyJwkError> {
match self {
AsymmetricPublicKey::Ec(key) => {
let jwk = key.to_jwk()?;
Ok(deno_core::serde_json::json!(jwk))
}
AsymmetricPublicKey::X25519(key) => {
let bytes = key.as_bytes();
let jwk = deno_core::serde_json::json!({
"kty": "OKP",
"crv": "X25519",
"x": bytes_to_b64(bytes),
});
Ok(jwk)
}
AsymmetricPublicKey::Ed25519(key) => {
let bytes = key.to_bytes();
let jwk = deno_core::serde_json::json!({
"kty": "OKP",
"crv": "Ed25519",
"x": bytes_to_b64(&bytes),
});
Ok(jwk)
}
AsymmetricPublicKey::X448(key) => {
let jwk = deno_core::serde_json::json!({
"kty": "OKP",
"crv": "X448",
"x": bytes_to_b64(key),
});
Ok(jwk)
}
AsymmetricPublicKey::Ed448(key) => {
let bytes = key.to_bytes();
let jwk = deno_core::serde_json::json!({
"kty": "OKP",
"crv": "Ed448",
"x": bytes_to_b64(&bytes),
});
Ok(jwk)
}
AsymmetricPublicKey::Rsa(key) => {
let n = key.n();
let e = key.e();
let jwk = deno_core::serde_json::json!({
"kty": "RSA",
"n": bytes_to_b64(&n.to_bytes_be()),
"e": bytes_to_b64(&e.to_bytes_be()),
});
Ok(jwk)
}
_ => Err(AsymmetricPublicKeyJwkError::JwkExportNotImplementedForKeyType),
}
}
pub(crate) fn export_der(
&self,
typ: &str,
) -> Result<Box<[u8]>, AsymmetricPublicKeyDerError> {
match typ {
"pkcs1" => match self {
AsymmetricPublicKey::Rsa(key) => {
let der = key
.to_pkcs1_der()
.map_err(|_| AsymmetricPublicKeyDerError::InvalidRsaPublicKey)?
.into_vec()
.into_boxed_slice();
Ok(der)
}
_ => Err(AsymmetricPublicKeyDerError::ExportingNonRsaPublicKeyAsPkcs1Unsupported),
},
"spki" => {
let der = match self {
AsymmetricPublicKey::Rsa(key) => key
.to_public_key_der()
.map_err(|_| AsymmetricPublicKeyDerError::InvalidRsaPublicKey)?
.into_vec()
.into_boxed_slice(),
AsymmetricPublicKey::RsaPss(key) => {
let pkcs1_der = key.key
.to_pkcs1_der()
.map_err(|_| AsymmetricPublicKeyDerError::InvalidRsaPublicKey)?;
let spki = SubjectPublicKeyInfoRef {
algorithm: rsa::pkcs8::AlgorithmIdentifierRef {
oid: RSASSA_PSS_OID,
parameters: None,
},
subject_public_key: BitStringRef::from_bytes(pkcs1_der.as_bytes())
.map_err(|_| AsymmetricPublicKeyDerError::InvalidRsaPublicKey)?,
};
spki
.to_der()
.map_err(|_| AsymmetricPublicKeyDerError::InvalidRsaPublicKey)?
.into_boxed_slice()
}
AsymmetricPublicKey::Dsa(key) => key
.to_public_key_der()
.map_err(|_| AsymmetricPublicKeyDerError::InvalidDsaPublicKey)?
.into_vec()
.into_boxed_slice(),
AsymmetricPublicKey::Ec(key) => {
use elliptic_curve::sec1::ToEncodedPoint;
let (sec1, oid): (Box<[u8]>, _) = match key {
EcPublicKey::P224(key) => (
key.to_encoded_point(false).as_bytes().to_vec().into_boxed_slice(),
ID_SECP224R1_OID,
),
EcPublicKey::P256(key) => (
key.to_encoded_point(false).as_bytes().to_vec().into_boxed_slice(),
ID_SECP256R1_OID,
),
EcPublicKey::P384(key) => (
key.to_encoded_point(false).as_bytes().to_vec().into_boxed_slice(),
ID_SECP384R1_OID,
),
EcPublicKey::P521(key) => (
key.to_encoded_point(false).as_bytes().to_vec().into_boxed_slice(),
ID_SECP521R1_OID,
),
EcPublicKey::Secp256k1(key) => (
key.to_encoded_point(false).as_bytes().to_vec().into_boxed_slice(),
ID_SECP256K1_OID,
),
};
let spki = SubjectPublicKeyInfoRef {
algorithm: rsa::pkcs8::AlgorithmIdentifierRef {
oid: EC_OID,
parameters: Some(asn1::AnyRef::from(&oid)),
},
subject_public_key: BitStringRef::from_bytes(&sec1)
.map_err(|_| AsymmetricPublicKeyDerError::InvalidEcPublicKey)?,
};
spki
.to_der()
.map_err(|_| AsymmetricPublicKeyDerError::InvalidEcPublicKey)?
.into_boxed_slice()
}
AsymmetricPublicKey::X25519(key) => {
let spki = SubjectPublicKeyInfoRef {
algorithm: rsa::pkcs8::AlgorithmIdentifierRef {
oid: X25519_OID,
parameters: None,
},
subject_public_key: BitStringRef::from_bytes(key.as_bytes())
.map_err(|_| AsymmetricPublicKeyDerError::InvalidX25519PublicKey)?,
};
spki
.to_der()
.map_err(|_| AsymmetricPublicKeyDerError::InvalidX25519PublicKey)?
.into_boxed_slice()
}
AsymmetricPublicKey::Ed25519(key) => {
let spki = SubjectPublicKeyInfoRef {
algorithm: rsa::pkcs8::AlgorithmIdentifierRef {
oid: ED25519_OID,
parameters: None,
},
subject_public_key: BitStringRef::from_bytes(key.as_bytes())
.map_err(|_| AsymmetricPublicKeyDerError::InvalidEd25519PublicKey)?,
};
spki
.to_der()
.map_err(|_| AsymmetricPublicKeyDerError::InvalidEd25519PublicKey)?
.into_boxed_slice()
}
AsymmetricPublicKey::X448(key) => {
let spki = SubjectPublicKeyInfoRef {
algorithm: rsa::pkcs8::AlgorithmIdentifierRef {
oid: X448_OID,
parameters: None,
},
subject_public_key: BitStringRef::from_bytes(key)
.map_err(|_| AsymmetricPublicKeyDerError::InvalidX448PublicKey)?,
};
spki
.to_der()
.map_err(|_| AsymmetricPublicKeyDerError::InvalidX448PublicKey)?
.into_boxed_slice()
}
AsymmetricPublicKey::Ed448(key) => {
let bytes = key.to_bytes();
let spki = SubjectPublicKeyInfoRef {
algorithm: rsa::pkcs8::AlgorithmIdentifierRef {
oid: ED448_OID,
parameters: None,
},
subject_public_key: BitStringRef::from_bytes(&bytes)
.map_err(|_| AsymmetricPublicKeyDerError::InvalidEd448PublicKey)?,
};
spki
.to_der()
.map_err(|_| AsymmetricPublicKeyDerError::InvalidEd448PublicKey)?
.into_boxed_slice()
}
AsymmetricPublicKey::Dh(key) => {
let raw_key = key.key.clone().into_vec();
let public_key_int = asn1::Int::new(&raw_key).unwrap();
let public_key_der = public_key_int
.to_der()
.map_err(|_| AsymmetricPublicKeyDerError::InvalidDhPublicKey)?;
let params = key.params.to_der().unwrap();
let spki = SubjectPublicKeyInfoRef {
algorithm: rsa::pkcs8::AlgorithmIdentifierRef {
oid: DH_KEY_AGREEMENT_OID,
parameters: Some(AnyRef::new(Tag::Sequence, ¶ms).unwrap()),
},
subject_public_key: BitStringRef::from_bytes(&public_key_der)
.map_err(|_| {
AsymmetricPublicKeyDerError::InvalidDhPublicKey
})?,
};
spki
.to_der()
.map_err(|_| AsymmetricPublicKeyDerError::InvalidDhPublicKey)?
.into_boxed_slice()
}
};
Ok(der)
}
_ => Err(AsymmetricPublicKeyDerError::UnsupportedKeyType(typ.to_string())),
}
}
}
#[derive(Debug, thiserror::Error, deno_error::JsError)]
#[class(type)]
pub enum AsymmetricPrivateKeyDerError {
#[error("key is not an asymmetric private key")]
KeyIsNotAsymmetricPrivateKey,
#[error("invalid RSA private key")]
InvalidRsaPrivateKey,
#[error("exporting non-RSA private key as PKCS#1 is not supported")]
ExportingNonRsaPrivateKeyAsPkcs1Unsupported,
#[error("invalid EC private key")]
InvalidEcPrivateKey,
#[error("exporting non-EC private key as SEC1 is not supported")]
ExportingNonEcPrivateKeyAsSec1Unsupported,
#[class(type)]
#[error("exporting RSA-PSS private key as PKCS#8 is not supported yet")]
ExportingNonRsaPssPrivateKeyAsPkcs8Unsupported,
#[error("invalid DSA private key")]
InvalidDsaPrivateKey,
#[error("invalid X25519 private key")]
InvalidX25519PrivateKey,
#[error("invalid Ed25519 private key")]
InvalidEd25519PrivateKey,
#[error("invalid X448 private key")]
InvalidX448PrivateKey,
#[error("invalid Ed448 private key")]
InvalidEd448PrivateKey,
#[error("invalid DH private key")]
InvalidDhPrivateKey,
#[error("unsupported key type: {0}")]
UnsupportedKeyType(String),
}
fn rsa_private_to_jwk(key: &RsaPrivateKey) -> deno_core::serde_json::Value {
let n = key.n();
let e = key.e();
let d = key.d();
let p = &key.primes()[0];
let q = &key.primes()[1];
let dp = key.dp();
let dq = key.dq();
let qi = key.crt_coefficient();
let oth = &key.primes()[2..];
let mut obj = deno_core::serde_json::json!({
"kty": "RSA",
"n": bytes_to_b64(&n.to_bytes_be()),
"e": bytes_to_b64(&e.to_bytes_be()),
"d": bytes_to_b64(&d.to_bytes_be()),
"p": bytes_to_b64(&p.to_bytes_be()),
"q": bytes_to_b64(&q.to_bytes_be()),
"dp": dp.map(|dp| bytes_to_b64(&dp.to_bytes_be())),
"dq": dq.map(|dq| bytes_to_b64(&dq.to_bytes_be())),
"qi": qi.map(|qi| bytes_to_b64(&qi.to_bytes_be())),
});
if !oth.is_empty() {
obj["oth"] = deno_core::serde_json::json!(
oth.iter().map(|o| o.to_bytes_be()).collect::<Vec<_>>()
);
}
obj
}
impl AsymmetricPrivateKey {
fn export_jwk(
&self,
) -> Result<deno_core::serde_json::Value, AsymmetricPrivateKeyJwkError> {
match self {
AsymmetricPrivateKey::Rsa(key) => Ok(rsa_private_to_jwk(key)),
AsymmetricPrivateKey::RsaPss(_) => {
Err(AsymmetricPrivateKeyJwkError::JwkExportNotImplementedForKeyType)
}
AsymmetricPrivateKey::Ec(key) => {
let jwk = key.to_jwk()?;
Ok(deno_core::serde_json::json!(jwk))
}
AsymmetricPrivateKey::X25519(static_secret) => {
let bytes = static_secret.to_bytes();
let AsymmetricPublicKey::X25519(x) = self.to_public_key() else {
unreachable!();
};
Ok(deno_core::serde_json::json!({
"crv": "X25519",
"x": bytes_to_b64(x.as_bytes()),
"d": bytes_to_b64(&bytes),
"kty": "OKP",
}))
}
AsymmetricPrivateKey::Ed25519(key) => {
let bytes = key.to_bytes();
let AsymmetricPublicKey::Ed25519(x) = self.to_public_key() else {
unreachable!();
};
Ok(deno_core::serde_json::json!({
"crv": "Ed25519",
"x": bytes_to_b64(x.as_bytes()),
"d": bytes_to_b64(&bytes),
"kty": "OKP",
}))
}
AsymmetricPrivateKey::X448(key) => {
let AsymmetricPublicKey::X448(x) = self.to_public_key() else {
unreachable!();
};
Ok(deno_core::serde_json::json!({
"crv": "X448",
"x": bytes_to_b64(&x),
"d": bytes_to_b64(&key[..56]),
"kty": "OKP",
}))
}
AsymmetricPrivateKey::Ed448(key) => {
let bytes = key.to_bytes();
let AsymmetricPublicKey::Ed448(x) = self.to_public_key() else {
unreachable!();
};
let x_bytes = x.to_bytes();
Ok(deno_core::serde_json::json!({
"crv": "Ed448",
"x": bytes_to_b64(&x_bytes),
"d": bytes_to_b64(&bytes),
"kty": "OKP",
}))
}
_ => Err(AsymmetricPrivateKeyJwkError::JwkExportNotImplementedForKeyType),
}
}
fn export_der(
&self,
typ: &str,
) -> Result<Box<[u8]>, AsymmetricPrivateKeyDerError> {
match typ {
"pkcs1" => match self {
AsymmetricPrivateKey::Rsa(key) => {
let der = key
.to_pkcs1_der()
.map_err(|_| AsymmetricPrivateKeyDerError::InvalidRsaPrivateKey)?
.to_bytes()
.to_vec()
.into_boxed_slice();
Ok(der)
}
_ => Err(AsymmetricPrivateKeyDerError::ExportingNonRsaPrivateKeyAsPkcs1Unsupported),
},
"sec1" => match self {
AsymmetricPrivateKey::Ec(key) => {
let (sec1_der, curve_oid) = match key {
EcPrivateKey::P224(key) => {
(key.to_sec1_der(), ID_SECP224R1_OID)
}
EcPrivateKey::P256(key) => {
(key.to_sec1_der(), ID_SECP256R1_OID)
}
EcPrivateKey::P384(key) => {
(key.to_sec1_der(), ID_SECP384R1_OID)
}
EcPrivateKey::P521(key) => {
(key.to_sec1_der(), ID_SECP521R1_OID)
}
EcPrivateKey::Secp256k1(key) => {
(key.to_sec1_der(), ID_SECP256K1_OID)
}
};
let sec1_der = sec1_der
.map_err(|_| AsymmetricPrivateKeyDerError::InvalidEcPrivateKey)?;
let mut ec_key =
sec1::EcPrivateKey::from_der(&sec1_der)
.map_err(|_| {
AsymmetricPrivateKeyDerError::InvalidEcPrivateKey
})?;
ec_key.parameters = Some(curve_oid.into());
let der = ec_key
.to_der()
.map_err(|_| AsymmetricPrivateKeyDerError::InvalidEcPrivateKey)?;
Ok(der.into_boxed_slice())
}
_ => Err(AsymmetricPrivateKeyDerError::ExportingNonEcPrivateKeyAsSec1Unsupported),
},
"pkcs8" => {
let der = match self {
AsymmetricPrivateKey::Rsa(key) => {
let document = key
.to_pkcs8_der()
.map_err(|_| AsymmetricPrivateKeyDerError::InvalidRsaPrivateKey)?;
document.to_bytes().to_vec().into_boxed_slice()
}
AsymmetricPrivateKey::RsaPss(_key) => {
return Err(AsymmetricPrivateKeyDerError::ExportingNonRsaPssPrivateKeyAsPkcs8Unsupported)
}
AsymmetricPrivateKey::Dsa(key) => {
let document = key
.to_pkcs8_der()
.map_err(|_| AsymmetricPrivateKeyDerError::InvalidDsaPrivateKey)?;
document.to_bytes().to_vec().into_boxed_slice()
}
AsymmetricPrivateKey::Ec(key) => {
let document = match key {
EcPrivateKey::P224(key) => key.to_pkcs8_der(),
EcPrivateKey::P256(key) => key.to_pkcs8_der(),
EcPrivateKey::P384(key) => key.to_pkcs8_der(),
EcPrivateKey::P521(key) => key.to_pkcs8_der(),
EcPrivateKey::Secp256k1(key) => key.to_pkcs8_der(),
}
.map_err(|_| AsymmetricPrivateKeyDerError::InvalidEcPrivateKey)?;
document.to_bytes().to_vec().into_boxed_slice()
}
AsymmetricPrivateKey::X25519(key) => {
let private_key = OctetStringRef::new(key.as_bytes())
.map_err(|_| AsymmetricPrivateKeyDerError::InvalidX25519PrivateKey)?
.to_der()
.map_err(|_| AsymmetricPrivateKeyDerError::InvalidX25519PrivateKey)?;
let private_key = PrivateKeyInfo {
algorithm: rsa::pkcs8::AlgorithmIdentifierRef {
oid: X25519_OID,
parameters: None,
},
private_key: &private_key,
public_key: None,
};
let der = private_key
.to_der()
.map_err(|_| AsymmetricPrivateKeyDerError::InvalidX25519PrivateKey)?
.into_boxed_slice();
return Ok(der);
}
AsymmetricPrivateKey::Ed25519(key) => {
let private_key = OctetStringRef::new(key.as_bytes())
.map_err(|_| AsymmetricPrivateKeyDerError::InvalidEd25519PrivateKey)?
.to_der()
.map_err(|_| AsymmetricPrivateKeyDerError::InvalidEd25519PrivateKey)?;
let private_key = PrivateKeyInfo {
algorithm: rsa::pkcs8::AlgorithmIdentifierRef {
oid: ED25519_OID,
parameters: None,
},
private_key: &private_key,
public_key: None,
};
private_key
.to_der()
.map_err(|_| AsymmetricPrivateKeyDerError::InvalidEd25519PrivateKey)?
.into_boxed_slice()
}
AsymmetricPrivateKey::X448(key) => {
let private_key = OctetStringRef::new(&key[..56])
.map_err(|_| AsymmetricPrivateKeyDerError::InvalidX448PrivateKey)?
.to_der()
.map_err(|_| AsymmetricPrivateKeyDerError::InvalidX448PrivateKey)?;
let private_key = PrivateKeyInfo {
algorithm: rsa::pkcs8::AlgorithmIdentifierRef {
oid: X448_OID,
parameters: None,
},
private_key: &private_key,
public_key: None,
};
private_key
.to_der()
.map_err(|_| AsymmetricPrivateKeyDerError::InvalidX448PrivateKey)?
.into_boxed_slice()
}
AsymmetricPrivateKey::Ed448(key) => {
let seed = key.to_bytes();
let private_key = OctetStringRef::new(&seed)
.map_err(|_| AsymmetricPrivateKeyDerError::InvalidEd448PrivateKey)?
.to_der()
.map_err(|_| AsymmetricPrivateKeyDerError::InvalidEd448PrivateKey)?;
let private_key = PrivateKeyInfo {
algorithm: rsa::pkcs8::AlgorithmIdentifierRef {
oid: ED448_OID,
parameters: None,
},
private_key: &private_key,
public_key: None,
};
private_key
.to_der()
.map_err(|_| AsymmetricPrivateKeyDerError::InvalidEd448PrivateKey)?
.into_boxed_slice()
}
AsymmetricPrivateKey::Dh(key) => {
let raw_key = key.key.clone().into_vec();
let private_key_int = asn1::Int::new(&raw_key).unwrap();
let private_key_der = private_key_int
.to_der()
.map_err(|_| AsymmetricPrivateKeyDerError::InvalidDhPrivateKey)?;
let params = key.params.to_der().unwrap();
let private_key = PrivateKeyInfo {
algorithm: rsa::pkcs8::AlgorithmIdentifierRef {
oid: DH_KEY_AGREEMENT_OID,
parameters: Some(AnyRef::new(Tag::Sequence, ¶ms).unwrap()),
},
private_key: &private_key_der,
public_key: None,
};
private_key
.to_der()
.map_err(|_| AsymmetricPrivateKeyDerError::InvalidDhPrivateKey)?
.into_boxed_slice()
}
};
Ok(der)
}
_ => Err(AsymmetricPrivateKeyDerError::UnsupportedKeyType(typ.to_string())),
}
}
}
#[op2]
#[cppgc]
pub fn op_node_create_private_key(
#[buffer] key: &[u8],
#[string] format: &str,
#[string] typ: &str,
#[buffer] passphrase: Option<&[u8]>,
) -> Result<KeyObjectHandle, AsymmetricPrivateKeyError> {
KeyObjectHandle::new_asymmetric_private_key_from_js(
key, format, typ, passphrase,
)
}
#[op2]
#[cppgc]
pub fn op_node_create_ed_raw(
#[string] curve: &str,
#[buffer] key: &[u8],
is_public: bool,
) -> Result<KeyObjectHandle, EdRawError> {
KeyObjectHandle::new_ed_raw(curve, key, is_public)
}
#[derive(FromV8)]
pub struct RsaJwkKey {
n: String,
e: String,
d: Option<String>,
p: Option<String>,
q: Option<String>,
}
#[op2]
#[cppgc]
pub fn op_node_create_rsa_jwk(
#[scoped] jwk: RsaJwkKey,
is_public: bool,
) -> Result<KeyObjectHandle, RsaJwkError> {
KeyObjectHandle::new_rsa_jwk(jwk, is_public)
}
#[op2]
#[cppgc]
pub fn op_node_create_ec_jwk(
#[serde] jwk: JwkEcKey,
is_public: bool,
) -> Result<KeyObjectHandle, EcJwkError> {
KeyObjectHandle::new_ec_jwk(&jwk, is_public)
}
#[op2]
#[cppgc]
pub fn op_node_create_public_key(
#[buffer] key: &[u8],
#[string] format: &str,
#[string] typ: &str,
#[buffer] passphrase: Option<&[u8]>,
) -> Result<KeyObjectHandle, AsymmetricPublicKeyError> {
KeyObjectHandle::new_asymmetric_public_key_from_js(
key, format, typ, passphrase,
)
}
#[op2]
#[cppgc]
pub fn op_node_create_secret_key(
#[buffer(copy)] key: Box<[u8]>,
) -> KeyObjectHandle {
KeyObjectHandle::Secret(key)
}
#[op2]
#[string]
pub fn op_node_get_asymmetric_key_type(
#[cppgc] handle: &KeyObjectHandle,
) -> Result<&'static str, JsErrorBox> {
match handle {
KeyObjectHandle::AsymmetricPrivate(AsymmetricPrivateKey::Rsa(_))
| KeyObjectHandle::AsymmetricPublic(AsymmetricPublicKey::Rsa(_)) => {
Ok("rsa")
}
KeyObjectHandle::AsymmetricPrivate(AsymmetricPrivateKey::RsaPss(_))
| KeyObjectHandle::AsymmetricPublic(AsymmetricPublicKey::RsaPss(_)) => {
Ok("rsa-pss")
}
KeyObjectHandle::AsymmetricPrivate(AsymmetricPrivateKey::Dsa(_))
| KeyObjectHandle::AsymmetricPublic(AsymmetricPublicKey::Dsa(_)) => {
Ok("dsa")
}
KeyObjectHandle::AsymmetricPrivate(AsymmetricPrivateKey::Ec(_))
| KeyObjectHandle::AsymmetricPublic(AsymmetricPublicKey::Ec(_)) => Ok("ec"),
KeyObjectHandle::AsymmetricPrivate(AsymmetricPrivateKey::X25519(_))
| KeyObjectHandle::AsymmetricPublic(AsymmetricPublicKey::X25519(_)) => {
Ok("x25519")
}
KeyObjectHandle::AsymmetricPrivate(AsymmetricPrivateKey::Ed25519(_))
| KeyObjectHandle::AsymmetricPublic(AsymmetricPublicKey::Ed25519(_)) => {
Ok("ed25519")
}
KeyObjectHandle::AsymmetricPrivate(AsymmetricPrivateKey::X448(_))
| KeyObjectHandle::AsymmetricPublic(AsymmetricPublicKey::X448(_)) => {
Ok("x448")
}
KeyObjectHandle::AsymmetricPrivate(AsymmetricPrivateKey::Ed448(_))
| KeyObjectHandle::AsymmetricPublic(AsymmetricPublicKey::Ed448(_)) => {
Ok("ed448")
}
KeyObjectHandle::AsymmetricPrivate(AsymmetricPrivateKey::Dh(_))
| KeyObjectHandle::AsymmetricPublic(AsymmetricPublicKey::Dh(_)) => Ok("dh"),
KeyObjectHandle::Secret(_) => Err(JsErrorBox::type_error(
"symmetric key is not an asymmetric key",
)),
}
}
#[derive(ToV8)]
#[to_v8(untagged)]
pub enum AsymmetricKeyDetails {
Rsa {
modulus_length: usize,
public_exponent: deno_core::convert::BigInt,
},
RsaPss {
modulus_length: usize,
public_exponent: deno_core::convert::BigInt,
hash_algorithm: &'static str,
mgf1_hash_algorithm: &'static str,
salt_length: u32,
},
#[to_v8(rename = "rsaPss")]
RsaPssBasic {
modulus_length: usize,
public_exponent: deno_core::convert::BigInt,
},
Dsa {
modulus_length: usize,
divisor_length: usize,
},
Ec {
named_curve: &'static str,
},
X25519,
Ed25519,
X448,
Ed448,
Dh,
}
#[op2]
pub fn op_node_get_asymmetric_key_details(
#[cppgc] handle: &KeyObjectHandle,
) -> Result<AsymmetricKeyDetails, JsErrorBox> {
match handle {
KeyObjectHandle::AsymmetricPrivate(private_key) => match private_key {
AsymmetricPrivateKey::Rsa(key) => {
let modulus_length = key.n().bits();
let public_exponent =
BigInt::from_bytes_be(num_bigint::Sign::Plus, &key.e().to_bytes_be());
Ok(AsymmetricKeyDetails::Rsa {
modulus_length,
public_exponent: public_exponent.into(),
})
}
AsymmetricPrivateKey::RsaPss(key) => {
let modulus_length = key.key.n().bits();
let public_exponent = BigInt::from_bytes_be(
num_bigint::Sign::Plus,
&key.key.e().to_bytes_be(),
);
let public_exponent = public_exponent.into();
let details = match key.details {
Some(details) => AsymmetricKeyDetails::RsaPss {
modulus_length,
public_exponent,
hash_algorithm: details.hash_algorithm.as_str(),
mgf1_hash_algorithm: details.mf1_hash_algorithm.as_str(),
salt_length: details.salt_length,
},
None => AsymmetricKeyDetails::RsaPssBasic {
modulus_length,
public_exponent,
},
};
Ok(details)
}
AsymmetricPrivateKey::Dsa(key) => {
let components = key.verifying_key().components();
let modulus_length = components.p().bits();
let divisor_length = components.q().bits();
Ok(AsymmetricKeyDetails::Dsa {
modulus_length,
divisor_length,
})
}
AsymmetricPrivateKey::Ec(key) => {
let named_curve = match key {
EcPrivateKey::P224(_) => "secp224r1",
EcPrivateKey::P256(_) => "prime256v1",
EcPrivateKey::P384(_) => "secp384r1",
EcPrivateKey::P521(_) => "secp521r1",
EcPrivateKey::Secp256k1(_) => "secp256k1",
};
Ok(AsymmetricKeyDetails::Ec { named_curve })
}
AsymmetricPrivateKey::X25519(_) => Ok(AsymmetricKeyDetails::X25519),
AsymmetricPrivateKey::Ed25519(_) => Ok(AsymmetricKeyDetails::Ed25519),
AsymmetricPrivateKey::X448(_) => Ok(AsymmetricKeyDetails::X448),
AsymmetricPrivateKey::Ed448(_) => Ok(AsymmetricKeyDetails::Ed448),
AsymmetricPrivateKey::Dh(_) => Ok(AsymmetricKeyDetails::Dh),
},
KeyObjectHandle::AsymmetricPublic(public_key) => match public_key {
AsymmetricPublicKey::Rsa(key) => {
let modulus_length = key.n().bits();
let public_exponent =
BigInt::from_bytes_be(num_bigint::Sign::Plus, &key.e().to_bytes_be());
Ok(AsymmetricKeyDetails::Rsa {
modulus_length,
public_exponent: public_exponent.into(),
})
}
AsymmetricPublicKey::RsaPss(key) => {
let modulus_length = key.key.n().bits();
let public_exponent = BigInt::from_bytes_be(
num_bigint::Sign::Plus,
&key.key.e().to_bytes_be(),
);
let public_exponent = public_exponent.into();
let details = match key.details {
Some(details) => AsymmetricKeyDetails::RsaPss {
modulus_length,
public_exponent,
hash_algorithm: details.hash_algorithm.as_str(),
mgf1_hash_algorithm: details.mf1_hash_algorithm.as_str(),
salt_length: details.salt_length,
},
None => AsymmetricKeyDetails::RsaPssBasic {
modulus_length,
public_exponent,
},
};
Ok(details)
}
AsymmetricPublicKey::Dsa(key) => {
let components = key.components();
let modulus_length = components.p().bits();
let divisor_length = components.q().bits();
Ok(AsymmetricKeyDetails::Dsa {
modulus_length,
divisor_length,
})
}
AsymmetricPublicKey::Ec(key) => {
let named_curve = match key {
EcPublicKey::P224(_) => "secp224r1",
EcPublicKey::P256(_) => "prime256v1",
EcPublicKey::P384(_) => "secp384r1",
EcPublicKey::P521(_) => "secp521r1",
EcPublicKey::Secp256k1(_) => "secp256k1",
};
Ok(AsymmetricKeyDetails::Ec { named_curve })
}
AsymmetricPublicKey::X25519(_) => Ok(AsymmetricKeyDetails::X25519),
AsymmetricPublicKey::Ed25519(_) => Ok(AsymmetricKeyDetails::Ed25519),
AsymmetricPublicKey::X448(_) => Ok(AsymmetricKeyDetails::X448),
AsymmetricPublicKey::Ed448(_) => Ok(AsymmetricKeyDetails::Ed448),
AsymmetricPublicKey::Dh(_) => Ok(AsymmetricKeyDetails::Dh),
},
KeyObjectHandle::Secret(_) => Err(JsErrorBox::type_error(
"symmetric key is not an asymmetric key",
)),
}
}
#[op2(fast)]
#[smi]
pub fn op_node_get_symmetric_key_size(
#[cppgc] handle: &KeyObjectHandle,
) -> Result<usize, JsErrorBox> {
match handle {
KeyObjectHandle::AsymmetricPrivate(_)
| KeyObjectHandle::AsymmetricPublic(_) => Err(JsErrorBox::type_error(
"asymmetric key is not a symmetric key",
)),
KeyObjectHandle::Secret(key) => Ok(key.len()),
}
}
#[op2]
#[cppgc]
pub fn op_node_generate_secret_key(#[smi] len: usize) -> KeyObjectHandle {
let mut key = vec![0u8; len];
thread_rng().fill_bytes(&mut key);
KeyObjectHandle::Secret(key.into_boxed_slice())
}
#[op2]
#[cppgc]
pub async fn op_node_generate_secret_key_async(
#[smi] len: usize,
) -> KeyObjectHandle {
spawn_blocking(move || {
let mut key = vec![0u8; len];
thread_rng().fill_bytes(&mut key);
KeyObjectHandle::Secret(key.into_boxed_slice())
})
.await
.unwrap()
}
struct KeyObjectHandlePair {
private_key: RefCell<Option<KeyObjectHandle>>,
public_key: RefCell<Option<KeyObjectHandle>>,
}
unsafe impl GarbageCollected for KeyObjectHandlePair {
fn trace(&self, _visitor: &mut deno_core::v8::cppgc::Visitor) {}
fn get_name(&self) -> &'static std::ffi::CStr {
c"KeyObjectHandlePair"
}
}
impl KeyObjectHandlePair {
pub fn new(
private_key: AsymmetricPrivateKey,
public_key: AsymmetricPublicKey,
) -> Self {
Self {
private_key: RefCell::new(Some(KeyObjectHandle::AsymmetricPrivate(
private_key,
))),
public_key: RefCell::new(Some(KeyObjectHandle::AsymmetricPublic(
public_key,
))),
}
}
}
#[op2]
#[cppgc]
pub fn op_node_get_public_key_from_pair(
#[cppgc] pair: &KeyObjectHandlePair,
) -> Option<KeyObjectHandle> {
pair.public_key.borrow_mut().take()
}
#[op2]
#[cppgc]
pub fn op_node_get_private_key_from_pair(
#[cppgc] pair: &KeyObjectHandlePair,
) -> Option<KeyObjectHandle> {
pair.private_key.borrow_mut().take()
}
fn generate_rsa(
modulus_length: usize,
public_exponent: usize,
) -> Result<KeyObjectHandlePair, JsErrorBox> {
if public_exponent <= 1 || public_exponent.is_multiple_of(2) {
return Err(JsErrorBox::generic(format!(
"invalid RSA public exponent: {}",
public_exponent
)));
}
let private_key = RsaPrivateKey::new_with_exp(
&mut thread_rng(),
modulus_length,
&rsa::BigUint::from_usize(public_exponent).unwrap(),
)
.map_err(|e| JsErrorBox::generic(e.to_string()))?;
let private_key = AsymmetricPrivateKey::Rsa(private_key);
let public_key = private_key.to_public_key();
Ok(KeyObjectHandlePair::new(private_key, public_key))
}
#[op2]
#[cppgc]
pub fn op_node_generate_rsa_key(
#[smi] modulus_length: usize,
#[smi] public_exponent: usize,
) -> Result<KeyObjectHandlePair, JsErrorBox> {
generate_rsa(modulus_length, public_exponent)
}
#[op2]
#[cppgc]
pub async fn op_node_generate_rsa_key_async(
#[smi] modulus_length: usize,
#[smi] public_exponent: usize,
) -> Result<KeyObjectHandlePair, JsErrorBox> {
spawn_blocking(move || generate_rsa(modulus_length, public_exponent))
.await
.unwrap()
}
#[derive(Debug, thiserror::Error, deno_error::JsError)]
#[class(type)]
#[error("digest not allowed for RSA-PSS keys{}", .0.as_ref().map(|digest| format!(": {digest}")).unwrap_or_default())]
pub struct GenerateRsaPssError(Option<String>);
fn generate_rsa_pss(
modulus_length: usize,
public_exponent: usize,
hash_algorithm: Option<&str>,
mf1_hash_algorithm: Option<&str>,
salt_length: Option<u32>,
) -> Result<KeyObjectHandlePair, GenerateRsaPssError> {
let key = RsaPrivateKey::new_with_exp(
&mut thread_rng(),
modulus_length,
&rsa::BigUint::from_usize(public_exponent).unwrap(),
)
.unwrap();
let details = if hash_algorithm.is_none()
&& mf1_hash_algorithm.is_none()
&& salt_length.is_none()
{
None
} else {
let hash_algorithm = hash_algorithm.unwrap_or("sha1");
let mf1_hash_algorithm = mf1_hash_algorithm.unwrap_or(hash_algorithm);
let hash_algorithm = match_fixed_digest_with_oid!(
hash_algorithm,
fn (algorithm: Option<RsaPssHashAlgorithm>) {
algorithm.ok_or(GenerateRsaPssError(None))?
},
_ => {
return Err(GenerateRsaPssError(Some(hash_algorithm.to_string())))
}
);
let mf1_hash_algorithm = match_fixed_digest_with_oid!(
mf1_hash_algorithm,
fn (algorithm: Option<RsaPssHashAlgorithm>) {
algorithm.ok_or(GenerateRsaPssError(None))?
},
_ => {
return Err(GenerateRsaPssError(Some(mf1_hash_algorithm.to_string())))
}
);
let salt_length =
salt_length.unwrap_or_else(|| hash_algorithm.salt_length());
Some(RsaPssDetails {
hash_algorithm,
mf1_hash_algorithm,
salt_length,
})
};
let private_key =
AsymmetricPrivateKey::RsaPss(RsaPssPrivateKey { key, details });
let public_key = private_key.to_public_key();
Ok(KeyObjectHandlePair::new(private_key, public_key))
}
#[op2]
#[cppgc]
pub fn op_node_generate_rsa_pss_key(
#[smi] modulus_length: usize,
#[smi] public_exponent: usize,
#[string] hash_algorithm: Option<String>, #[string] mf1_hash_algorithm: Option<String>, #[smi] salt_length: Option<u32>,
) -> Result<KeyObjectHandlePair, GenerateRsaPssError> {
generate_rsa_pss(
modulus_length,
public_exponent,
hash_algorithm.as_deref(),
mf1_hash_algorithm.as_deref(),
salt_length,
)
}
#[op2]
#[cppgc]
pub async fn op_node_generate_rsa_pss_key_async(
#[smi] modulus_length: usize,
#[smi] public_exponent: usize,
#[string] hash_algorithm: Option<String>, #[string] mf1_hash_algorithm: Option<String>, #[smi] salt_length: Option<u32>,
) -> Result<KeyObjectHandlePair, GenerateRsaPssError> {
spawn_blocking(move || {
generate_rsa_pss(
modulus_length,
public_exponent,
hash_algorithm.as_deref(),
mf1_hash_algorithm.as_deref(),
salt_length,
)
})
.await
.unwrap()
}
fn dsa_generate_components<R: rand::Rng + rand::CryptoRng>(
rng: &mut R,
l: u32,
n: u32,
) -> (
num_bigint_dig::BigUint,
num_bigint_dig::BigUint,
num_bigint_dig::BigUint,
) {
use num_bigint_dig::BigUint;
use num_bigint_dig::RandBigInt;
use num_bigint_dig::RandPrime;
use num_bigint_dig::prime::probably_prime;
use num_traits::One;
use num_traits::Pow;
const MR_ROUNDS: usize = 64;
let two = || BigUint::from(2u8);
let bounds = |size: u32| -> (BigUint, BigUint) {
let lower = two().pow(size - 1);
let upper = two().pow(size);
(lower, upper)
};
let (p_min, p_max) = bounds(l);
let (q_min, q_max) = bounds(n);
let (p, q) = 'gen_pq: loop {
let q = rng.gen_prime(n as usize);
if q < q_min || q > q_max {
continue;
}
for _ in 0..4096 {
let m = 'gen_m: loop {
let m = rng.gen_biguint(l as usize);
if m > p_min && m < p_max {
break 'gen_m m;
}
};
let mr = &m % (two() * &q);
let p = m - mr + BigUint::one();
if probably_prime(&p, MR_ROUNDS) {
break 'gen_pq (p, q);
}
}
};
let e = (&p - BigUint::one()) / &q;
let mut h = BigUint::one();
let g = loop {
let g = h.modpow(&e, &p);
if !num_traits::One::is_one(&g) {
break g;
}
h += BigUint::one();
};
(p, q, g)
}
fn dsa_generate(
modulus_length: usize,
divisor_length: usize,
) -> Result<KeyObjectHandlePair, JsErrorBox> {
use dsa::Components;
use dsa::SigningKey;
if modulus_length < 2 || divisor_length < 2 {
return Err(JsErrorBox::type_error(
"Invalid modulusLength+divisorLength combination",
));
}
if divisor_length >= modulus_length {
return Err(JsErrorBox::type_error(
"Invalid modulusLength+divisorLength combination",
));
}
if u32::try_from(modulus_length).is_err()
|| u32::try_from(divisor_length).is_err()
{
return Err(JsErrorBox::type_error(
"Invalid modulusLength+divisorLength combination",
));
}
let mut rng = rand::thread_rng();
let (p, q, g) = dsa_generate_components(
&mut rng,
modulus_length as u32,
divisor_length as u32,
);
let components = Components::from_components(p, q, g).map_err(|_| {
JsErrorBox::type_error("Invalid modulusLength+divisorLength combination")
})?;
let signing_key = SigningKey::generate(&mut rng, components);
let private_key = AsymmetricPrivateKey::Dsa(signing_key);
let public_key = private_key.to_public_key();
Ok(KeyObjectHandlePair::new(private_key, public_key))
}
#[op2]
#[cppgc]
pub fn op_node_generate_dsa_key(
#[smi] modulus_length: usize,
#[smi] divisor_length: usize,
) -> Result<KeyObjectHandlePair, JsErrorBox> {
dsa_generate(modulus_length, divisor_length)
}
#[op2]
#[cppgc]
pub async fn op_node_generate_dsa_key_async(
#[smi] modulus_length: usize,
#[smi] divisor_length: usize,
) -> Result<KeyObjectHandlePair, JsErrorBox> {
spawn_blocking(move || dsa_generate(modulus_length, divisor_length))
.await
.unwrap()
}
fn ec_generate(named_curve: &str) -> Result<KeyObjectHandlePair, JsErrorBox> {
let mut rng = rand::thread_rng();
let private_key = match named_curve {
"P-224" | "prime224v1" | "secp224r1" => {
let key = p224::SecretKey::random(&mut rng);
AsymmetricPrivateKey::Ec(EcPrivateKey::P224(key))
}
"P-256" | "prime256v1" | "secp256r1" => {
let key = p256::SecretKey::random(&mut rng);
AsymmetricPrivateKey::Ec(EcPrivateKey::P256(key))
}
"P-384" | "prime384v1" | "secp384r1" => {
let key = p384::SecretKey::random(&mut rng);
AsymmetricPrivateKey::Ec(EcPrivateKey::P384(key))
}
"P-521" | "secp521r1" => {
let key = p521::SecretKey::random(&mut rng);
AsymmetricPrivateKey::Ec(EcPrivateKey::P521(key))
}
"secp256k1" => {
let key = k256::SecretKey::random(&mut rng);
AsymmetricPrivateKey::Ec(EcPrivateKey::Secp256k1(key))
}
_ => {
return Err(JsErrorBox::type_error("Invalid EC curve name"));
}
};
let public_key = private_key.to_public_key();
Ok(KeyObjectHandlePair::new(private_key, public_key))
}
#[op2]
#[cppgc]
pub fn op_node_generate_ec_key(
#[string] named_curve: &str,
) -> Result<KeyObjectHandlePair, JsErrorBox> {
ec_generate(named_curve)
}
#[op2]
#[cppgc]
pub async fn op_node_generate_ec_key_async(
#[string] named_curve: String,
) -> Result<KeyObjectHandlePair, JsErrorBox> {
spawn_blocking(move || ec_generate(&named_curve))
.await
.unwrap()
}
fn x25519_generate() -> KeyObjectHandlePair {
let keypair = x25519_dalek::StaticSecret::random_from_rng(thread_rng());
let private_key = AsymmetricPrivateKey::X25519(keypair);
let public_key = private_key.to_public_key();
KeyObjectHandlePair::new(private_key, public_key)
}
#[op2]
#[cppgc]
pub fn op_node_generate_x25519_key() -> KeyObjectHandlePair {
x25519_generate()
}
#[op2]
#[cppgc]
pub async fn op_node_generate_x25519_key_async() -> KeyObjectHandlePair {
spawn_blocking(x25519_generate).await.unwrap()
}
fn ed25519_generate() -> KeyObjectHandlePair {
let keypair = ed25519_dalek::SigningKey::generate(&mut thread_rng());
let private_key = AsymmetricPrivateKey::Ed25519(keypair);
let public_key = private_key.to_public_key();
KeyObjectHandlePair::new(private_key, public_key)
}
#[op2]
#[cppgc]
pub fn op_node_generate_ed25519_key() -> KeyObjectHandlePair {
ed25519_generate()
}
#[op2]
#[cppgc]
pub async fn op_node_generate_ed25519_key_async() -> KeyObjectHandlePair {
spawn_blocking(ed25519_generate).await.unwrap()
}
fn x448_generate() -> KeyObjectHandlePair {
let mut seed = [0u8; 56];
thread_rng().fill_bytes(&mut seed);
let private_key = AsymmetricPrivateKey::X448(seed);
let public_key = private_key.to_public_key();
KeyObjectHandlePair::new(private_key, public_key)
}
#[op2]
#[cppgc]
pub fn op_node_generate_x448_key() -> KeyObjectHandlePair {
x448_generate()
}
#[op2]
#[cppgc]
pub async fn op_node_generate_x448_key_async() -> KeyObjectHandlePair {
spawn_blocking(x448_generate).await.unwrap()
}
fn ed448_generate() -> KeyObjectHandlePair {
let mut seed = [0u8; 57];
thread_rng().fill_bytes(&mut seed);
let signing_key = ed448_goldilocks::SigningKey::from(
ed448_goldilocks::EdwardsScalarBytes::from(seed),
);
let private_key = AsymmetricPrivateKey::Ed448(signing_key);
let public_key = private_key.to_public_key();
KeyObjectHandlePair::new(private_key, public_key)
}
#[op2]
#[cppgc]
pub fn op_node_generate_ed448_key() -> KeyObjectHandlePair {
ed448_generate()
}
#[op2]
#[cppgc]
pub async fn op_node_generate_ed448_key_async() -> KeyObjectHandlePair {
spawn_blocking(ed448_generate).await.unwrap()
}
fn dh_group_generate(
group_name: &str,
) -> Result<KeyObjectHandlePair, JsErrorBox> {
let (dh, prime, generator) = match group_name {
"modp5" => (
dh::DiffieHellman::group::<dh::Modp1536>(),
dh::Modp1536::MODULUS,
dh::Modp1536::GENERATOR,
),
"modp14" => (
dh::DiffieHellman::group::<dh::Modp2048>(),
dh::Modp2048::MODULUS,
dh::Modp2048::GENERATOR,
),
"modp15" => (
dh::DiffieHellman::group::<dh::Modp3072>(),
dh::Modp3072::MODULUS,
dh::Modp3072::GENERATOR,
),
"modp16" => (
dh::DiffieHellman::group::<dh::Modp4096>(),
dh::Modp4096::MODULUS,
dh::Modp4096::GENERATOR,
),
"modp17" => (
dh::DiffieHellman::group::<dh::Modp6144>(),
dh::Modp6144::MODULUS,
dh::Modp6144::GENERATOR,
),
"modp18" => (
dh::DiffieHellman::group::<dh::Modp8192>(),
dh::Modp8192::MODULUS,
dh::Modp8192::GENERATOR,
),
_ => return Err(JsErrorBox::type_error("Unsupported group name")),
};
let mut prime_bytes: Vec<u8> =
prime.iter().flat_map(|x| x.to_be_bytes()).collect();
if prime_bytes.first().is_some_and(|b| b & 0x80 != 0) {
prime_bytes.insert(0, 0x00);
}
let gen_bytes = [generator as u8];
let params = DhParameter {
prime: asn1::Int::new(&prime_bytes).unwrap(),
base: asn1::Int::new(&gen_bytes).unwrap(),
private_value_length: None,
};
Ok(KeyObjectHandlePair::new(
AsymmetricPrivateKey::Dh(DhPrivateKey {
key: dh.private_key,
params: params.clone(),
}),
AsymmetricPublicKey::Dh(DhPublicKey {
key: dh.public_key,
params,
}),
))
}
#[op2]
#[cppgc]
pub fn op_node_generate_dh_group_key(
#[string] group_name: &str,
) -> Result<KeyObjectHandlePair, JsErrorBox> {
dh_group_generate(group_name)
}
#[op2]
#[cppgc]
pub async fn op_node_generate_dh_group_key_async(
#[string] group_name: String,
) -> Result<KeyObjectHandlePair, JsErrorBox> {
spawn_blocking(move || dh_group_generate(&group_name))
.await
.unwrap()
}
fn dh_generate(
prime: Option<&[u8]>,
prime_len: usize,
generator: usize,
) -> KeyObjectHandlePair {
let prime = prime
.map(|p| p.into())
.unwrap_or_else(|| Prime::generate(prime_len));
let dh = dh::DiffieHellman::new(prime.clone(), generator);
let gen_bytes = if generator <= 0xFF {
vec![generator as u8]
} else if generator <= 0xFFFF {
vec![(generator >> 8) as u8, generator as u8]
} else {
generator
.to_be_bytes()
.iter()
.copied()
.skip_while(|&b| b == 0)
.collect()
};
let mut prime_bytes = prime.0.to_bytes_be();
if prime_bytes.first().is_some_and(|b| b & 0x80 != 0) {
prime_bytes.insert(0, 0x00);
}
let params = DhParameter {
prime: asn1::Int::new(&prime_bytes).unwrap(),
base: asn1::Int::new(&gen_bytes).unwrap(),
private_value_length: None,
};
KeyObjectHandlePair::new(
AsymmetricPrivateKey::Dh(DhPrivateKey {
key: dh.private_key,
params: params.clone(),
}),
AsymmetricPublicKey::Dh(DhPublicKey {
key: dh.public_key,
params,
}),
)
}
#[op2]
#[cppgc]
pub fn op_node_generate_dh_key(
#[buffer] prime: Option<&[u8]>,
#[smi] prime_len: usize,
#[smi] generator: usize,
) -> KeyObjectHandlePair {
dh_generate(prime, prime_len, generator)
}
#[op2]
#[cppgc]
pub async fn op_node_generate_dh_key_async(
#[buffer(copy)] prime: Option<Box<[u8]>>,
#[smi] prime_len: usize,
#[smi] generator: usize,
) -> KeyObjectHandlePair {
spawn_blocking(move || dh_generate(prime.as_deref(), prime_len, generator))
.await
.unwrap()
}
#[op2]
pub fn op_node_dh_keys_generate_and_export(
#[buffer] prime: Option<&[u8]>,
#[smi] prime_len: usize,
#[smi] generator: usize,
) -> (Uint8Array, Uint8Array) {
let prime = prime
.map(|p| p.into())
.unwrap_or_else(|| Prime::generate(prime_len));
let dh = dh::DiffieHellman::new(prime, generator);
let private_key = dh.private_key.into_vec();
let public_key = dh.public_key.into_vec();
(private_key.into(), public_key.into())
}
#[op2]
pub fn op_node_export_secret_key(
#[cppgc] handle: &KeyObjectHandle,
) -> Result<Uint8Array, JsErrorBox> {
let key = handle
.as_secret_key()
.ok_or_else(|| JsErrorBox::type_error("key is not a secret key"))?;
Ok(key.to_vec().into())
}
#[op2]
#[string]
pub fn op_node_export_secret_key_b64url(
#[cppgc] handle: &KeyObjectHandle,
) -> Result<String, JsErrorBox> {
let key = handle
.as_secret_key()
.ok_or_else(|| JsErrorBox::type_error("key is not a secret key"))?;
Ok(base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(key))
}
#[op2]
#[serde]
pub fn op_node_export_public_key_jwk(
#[cppgc] handle: &KeyObjectHandle,
) -> Result<deno_core::serde_json::Value, AsymmetricPublicKeyJwkError> {
let public_key = handle
.as_public_key()
.ok_or(AsymmetricPublicKeyJwkError::KeyIsNotAsymmetricPublicKey)?;
public_key.export_jwk()
}
#[derive(Debug, thiserror::Error, deno_error::JsError)]
pub enum ExportPublicKeyPemError {
#[class(inherit)]
#[error(transparent)]
AsymmetricPublicKeyDer(
#[from]
#[inherit]
AsymmetricPublicKeyDerError,
),
#[class(type)]
#[error("very large data")]
VeryLargeData,
#[class(generic)]
#[error(transparent)]
Der(#[from] der::Error),
}
#[op2]
#[string]
pub fn op_node_export_public_key_pem(
#[cppgc] handle: &KeyObjectHandle,
#[string] typ: &str,
) -> Result<String, ExportPublicKeyPemError> {
let public_key = handle
.as_public_key()
.ok_or(AsymmetricPublicKeyDerError::KeyIsNotAsymmetricPublicKey)?;
let data = public_key.export_der(typ)?;
let label = match typ {
"pkcs1" => "RSA PUBLIC KEY",
"spki" => "PUBLIC KEY",
_ => unreachable!("export_der would have errored"),
};
let pem_len = der::pem::encapsulated_len(label, LineEnding::LF, data.len())
.map_err(|_| ExportPublicKeyPemError::VeryLargeData)?;
let mut out = vec![0; pem_len];
let mut writer = PemWriter::new(label, LineEnding::LF, &mut out)?;
writer.write(&data)?;
let len = writer.finish()?;
out.truncate(len);
Ok(String::from_utf8(out).expect("invalid pem is not possible"))
}
#[op2]
#[buffer]
pub fn op_node_export_public_key_der(
#[cppgc] handle: &KeyObjectHandle,
#[string] typ: &str,
) -> Result<Box<[u8]>, AsymmetricPublicKeyDerError> {
let public_key = handle
.as_public_key()
.ok_or(AsymmetricPublicKeyDerError::KeyIsNotAsymmetricPublicKey)?;
public_key.export_der(typ)
}
#[derive(Debug, thiserror::Error, deno_error::JsError)]
pub enum ExportPrivateKeyPemError {
#[class(inherit)]
#[error(transparent)]
AsymmetricPublicKeyDer(
#[from]
#[inherit]
AsymmetricPrivateKeyDerError,
),
#[class(type)]
#[error("very large data")]
VeryLargeData,
#[class(generic)]
#[error(transparent)]
Der(#[from] der::Error),
#[class(type)]
#[error("{0}")]
UnsupportedCipher(String),
#[class(type)]
#[error(
"cipher and passphrase must both be provided for encrypted key export"
)]
MissingCipherOrPassphrase,
}
fn parse_legacy_encrypted_pem<'a>(
pem: &'a str,
passphrase: Option<&[u8]>,
) -> Result<Option<(&'a str, Vec<u8>)>, AsymmetricPrivateKeyError> {
if !pem.contains("Proc-Type: 4,ENCRYPTED") {
return Ok(None);
}
let passphrase = match passphrase {
Some(p) => p,
None => {
return Err(
AsymmetricPrivateKeyError::EncryptedPrivateKeyRequiresPassphraseToDecrypt,
);
}
};
let mut lines = pem.lines();
let label = loop {
match lines.next() {
Some(line)
if line.starts_with("-----BEGIN ") && line.ends_with("-----") =>
{
break &line[11..line.len() - 5];
}
Some(_) => continue,
None => return Err(AsymmetricPrivateKeyError::InvalidPemPrivateKey),
}
};
let proc_type_line = lines
.next()
.ok_or(AsymmetricPrivateKeyError::InvalidPemPrivateKey)?;
if !proc_type_line.starts_with("Proc-Type:") {
return Err(AsymmetricPrivateKeyError::InvalidPemPrivateKey);
}
let dek_info_line = lines
.next()
.ok_or(AsymmetricPrivateKeyError::InvalidPemPrivateKey)?;
let dek_info = dek_info_line
.strip_prefix("DEK-Info: ")
.ok_or(AsymmetricPrivateKeyError::InvalidPemPrivateKey)?;
let (cipher_name, iv_hex) = dek_info
.split_once(',')
.ok_or(AsymmetricPrivateKeyError::InvalidPemPrivateKey)?;
let _ = lines.next();
let mut b64_data = String::new();
for line in lines {
if line.starts_with("-----END ") {
break;
}
b64_data.push_str(line.trim());
}
let encrypted_data = base64::engine::general_purpose::STANDARD
.decode(&b64_data)
.map_err(|_| AsymmetricPrivateKeyError::InvalidPemPrivateKey)?;
if !iv_hex.len().is_multiple_of(2) {
return Err(AsymmetricPrivateKeyError::InvalidPemPrivateKey);
}
let mut iv = vec![0u8; iv_hex.len() / 2];
faster_hex::hex_decode(iv_hex.as_bytes(), &mut iv)
.map_err(|_| AsymmetricPrivateKeyError::InvalidPemPrivateKey)?;
let (key_len, expected_iv_len) = match cipher_name {
"AES-128-CBC" => (16, 16),
"AES-192-CBC" => (24, 16),
"AES-256-CBC" => (32, 16),
"DES-EDE3-CBC" => (24, 8),
_ => {
return Err(AsymmetricPrivateKeyError::InvalidEncryptedPemPrivateKey);
}
};
if iv.len() != expected_iv_len {
return Err(AsymmetricPrivateKeyError::InvalidEncryptedPemPrivateKey);
}
let mut salt = [0u8; 8];
salt.copy_from_slice(&iv[..8]);
let key = evp_bytes_to_key(passphrase, &salt, key_len);
let decrypted =
decrypt_legacy_pem_data(cipher_name, &key, &iv, &encrypted_data)
.map_err(|_| AsymmetricPrivateKeyError::BadDecrypt)?;
Ok(Some((label, decrypted)))
}
fn decrypt_legacy_pem_data(
cipher_name: &str,
key: &[u8],
iv: &[u8],
data: &[u8],
) -> Result<Vec<u8>, ()> {
use aes::cipher::BlockDecryptMut;
use aes::cipher::KeyIvInit;
use aes::cipher::block_padding::NoPadding;
let (mut decrypted, block_size) = match cipher_name {
"AES-128-CBC" => (
cbc::Decryptor::<aes::Aes128>::new_from_slices(key, iv)
.map_err(|_| ())?
.decrypt_padded_vec_mut::<NoPadding>(data)
.map_err(|_| ())?,
16usize,
),
"AES-192-CBC" => (
cbc::Decryptor::<aes::Aes192>::new_from_slices(key, iv)
.map_err(|_| ())?
.decrypt_padded_vec_mut::<NoPadding>(data)
.map_err(|_| ())?,
16usize,
),
"AES-256-CBC" => (
cbc::Decryptor::<aes::Aes256>::new_from_slices(key, iv)
.map_err(|_| ())?
.decrypt_padded_vec_mut::<NoPadding>(data)
.map_err(|_| ())?,
16usize,
),
"DES-EDE3-CBC" => (
cbc::Decryptor::<des::TdesEde3>::new_from_slices(key, iv)
.map_err(|_| ())?
.decrypt_padded_vec_mut::<NoPadding>(data)
.map_err(|_| ())?,
8usize,
),
_ => return Err(()),
};
pkcs7_unpad_ct(&mut decrypted, block_size).ok_or(())?;
Ok(decrypted)
}
fn pkcs7_unpad_ct(buf: &mut Vec<u8>, block_size: usize) -> Option<()> {
use subtle::Choice;
use subtle::ConstantTimeEq;
let len = buf.len();
if len == 0 || len < block_size || !len.is_multiple_of(block_size) {
return None;
}
let pad_byte = buf[len - 1];
let pad_nonzero = !pad_byte.ct_eq(&0u8);
let pad_in_range = u8_le_ct(pad_byte, block_size as u8);
let mut valid: Choice = pad_nonzero & pad_in_range;
let start = len - block_size;
for i in 0..block_size {
let pos_from_end = (block_size - 1 - i) as u8;
let is_in_pad = u8_gt_ct(pad_byte, pos_from_end);
let byte_matches = buf[start + i].ct_eq(&pad_byte);
valid &= !is_in_pad | byte_matches;
}
if !bool::from(valid) {
return None;
}
let pad_len = pad_byte as usize;
buf.truncate(len - pad_len);
Some(())
}
#[inline]
fn u8_gt_ct(a: u8, b: u8) -> subtle::Choice {
let diff = (b as i16).wrapping_sub(a as i16);
subtle::Choice::from(((diff as u16) >> 15) as u8)
}
#[inline]
fn u8_le_ct(a: u8, b: u8) -> subtle::Choice {
!u8_gt_ct(a, b)
}
fn evp_bytes_to_key(
passphrase: &[u8],
salt: &[u8; 8],
key_len: usize,
) -> Vec<u8> {
use digest::Digest;
let mut key = Vec::with_capacity(key_len);
let mut prev_hash: Option<[u8; 16]> = None;
while key.len() < key_len {
let mut hasher = md5::Md5::new();
if let Some(ref prev) = prev_hash {
hasher.update(prev);
}
hasher.update(passphrase);
hasher.update(salt);
let hash: [u8; 16] = hasher.finalize().into();
key.extend_from_slice(&hash);
prev_hash = Some(hash);
}
key.truncate(key_len);
key
}
fn encrypt_pkcs8_private_key_pem(
data: &[u8],
cipher_name: &str,
passphrase: &[u8],
) -> Result<String, ExportPrivateKeyPemError> {
use pkcs8::pkcs5::pbes2;
let mut salt = [0u8; 16];
thread_rng().fill_bytes(&mut salt);
let pbkdf2_iters: u32 = 2048;
let mut aes_iv = [0u8; 16];
let pbes2_params = match cipher_name {
"aes-128-cbc" => {
thread_rng().fill_bytes(&mut aes_iv);
pbes2::Parameters::pbkdf2_sha256_aes128cbc(pbkdf2_iters, &salt, &aes_iv)
.map_err(|_| {
ExportPrivateKeyPemError::UnsupportedCipher(format!(
"Unsupported cipher for PKCS#8 encryption: {cipher_name}"
))
})?
}
"aes-192-cbc" => {
thread_rng().fill_bytes(&mut aes_iv);
let kdf = pbes2::Pbkdf2Params::hmac_with_sha256(pbkdf2_iters, &salt)
.map_err(|_| {
ExportPrivateKeyPemError::UnsupportedCipher(format!(
"Unsupported cipher for PKCS#8 encryption: {cipher_name}"
))
})?
.into();
pbes2::Parameters {
kdf,
encryption: pbes2::EncryptionScheme::Aes192Cbc { iv: &aes_iv },
}
}
"aes-256-cbc" => {
thread_rng().fill_bytes(&mut aes_iv);
pbes2::Parameters::pbkdf2_sha256_aes256cbc(pbkdf2_iters, &salt, &aes_iv)
.map_err(|_| {
ExportPrivateKeyPemError::UnsupportedCipher(format!(
"Unsupported cipher for PKCS#8 encryption: {cipher_name}"
))
})?
}
_ => {
return Err(ExportPrivateKeyPemError::UnsupportedCipher(format!(
"Unsupported cipher for PKCS#8 encryption: {cipher_name}"
)));
}
};
let pk_info = PrivateKeyInfo::try_from(data).map_err(|_| {
ExportPrivateKeyPemError::UnsupportedCipher(
"could not parse PKCS#8 private key for encryption".to_string(),
)
})?;
let secret_doc = pk_info
.encrypt_with_params(pbes2_params, passphrase)
.map_err(|_| {
ExportPrivateKeyPemError::UnsupportedCipher(format!(
"Failed to encrypt PKCS#8 with cipher {cipher_name}"
))
})?;
let pem = secret_doc
.to_pem(EncryptedPrivateKeyInfo::PEM_LABEL, LineEnding::LF)
.map_err(|_| ExportPrivateKeyPemError::VeryLargeData)?;
Ok(pem.to_string())
}
fn encrypt_private_key_pem(
label: &str,
data: &[u8],
cipher_name: &str,
passphrase: &[u8],
) -> Result<String, ExportPrivateKeyPemError> {
use aes::cipher::BlockEncryptMut;
use aes::cipher::KeyIvInit;
use aes::cipher::block_padding::Pkcs7;
let (key_len, dek_info_name, iv_len) = match cipher_name {
"aes-128-cbc" => (16, "AES-128-CBC", 16),
"aes-192-cbc" => (24, "AES-192-CBC", 16),
"aes-256-cbc" => (32, "AES-256-CBC", 16),
"des-ede3-cbc" => (24, "DES-EDE3-CBC", 8),
_ => {
return Err(ExportPrivateKeyPemError::UnsupportedCipher(format!(
"Unsupported cipher for PEM encryption: {cipher_name}"
)));
}
};
let mut iv = vec![0u8; iv_len];
thread_rng().fill_bytes(&mut iv);
let mut salt = [0u8; 8];
salt.copy_from_slice(&iv[..8]);
let key = evp_bytes_to_key(passphrase, &salt, key_len);
let encrypted = match cipher_name {
"aes-128-cbc" => cbc::Encryptor::<aes::Aes128>::new_from_slices(&key, &iv)
.unwrap()
.encrypt_padded_vec_mut::<Pkcs7>(data),
"aes-192-cbc" => cbc::Encryptor::<aes::Aes192>::new_from_slices(&key, &iv)
.unwrap()
.encrypt_padded_vec_mut::<Pkcs7>(data),
"aes-256-cbc" => cbc::Encryptor::<aes::Aes256>::new_from_slices(&key, &iv)
.unwrap()
.encrypt_padded_vec_mut::<Pkcs7>(data),
"des-ede3-cbc" => {
cbc::Encryptor::<des::TdesEde3>::new_from_slices(&key, &iv)
.unwrap()
.encrypt_padded_vec_mut::<Pkcs7>(data)
}
_ => unreachable!(),
};
let iv_hex = iv.iter().map(|b| format!("{b:02X}")).collect::<String>();
let b64 = base64::engine::general_purpose::STANDARD.encode(&encrypted);
let mut pem = String::new();
pem.push_str(&format!("-----BEGIN {label}-----\n"));
pem.push_str("Proc-Type: 4,ENCRYPTED\n");
pem.push_str(&format!("DEK-Info: {dek_info_name},{iv_hex}\n"));
pem.push('\n');
for chunk in b64.as_bytes().chunks(64) {
pem.push_str(std::str::from_utf8(chunk).unwrap());
pem.push('\n');
}
pem.push_str(&format!("-----END {label}-----\n"));
Ok(pem)
}
#[op2]
#[string]
pub fn op_node_export_private_key_pem(
#[cppgc] handle: &KeyObjectHandle,
#[string] typ: &str,
#[string] cipher: Option<String>,
#[string] passphrase: Option<String>,
) -> Result<String, ExportPrivateKeyPemError> {
let private_key = handle
.as_private_key()
.ok_or(AsymmetricPrivateKeyDerError::KeyIsNotAsymmetricPrivateKey)?;
let data = private_key.export_der(typ)?;
let label = match typ {
"pkcs1" => "RSA PRIVATE KEY",
"pkcs8" => "PRIVATE KEY",
"sec1" => "EC PRIVATE KEY",
_ => unreachable!("export_der would have errored"),
};
match (&cipher, &passphrase) {
(Some(cipher), Some(passphrase)) => {
if typ == "pkcs8" {
return encrypt_pkcs8_private_key_pem(
&data,
cipher,
passphrase.as_bytes(),
);
}
return encrypt_private_key_pem(
label,
&data,
cipher,
passphrase.as_bytes(),
);
}
(Some(_), None) | (None, Some(_)) => {
return Err(ExportPrivateKeyPemError::MissingCipherOrPassphrase);
}
(None, None) => {}
}
let pem_len = der::pem::encapsulated_len(label, LineEnding::LF, data.len())
.map_err(|_| ExportPrivateKeyPemError::VeryLargeData)?;
let mut out = vec![0; pem_len];
let mut writer = PemWriter::new(label, LineEnding::LF, &mut out)?;
writer.write(&data)?;
let len = writer.finish()?;
out.truncate(len);
Ok(String::from_utf8(out).expect("invalid pem is not possible"))
}
#[derive(Debug, thiserror::Error, deno_error::JsError)]
pub enum ExportPrivateKeyJwkError {
#[class(inherit)]
#[error(transparent)]
AsymmetricPublicKeyJwk(#[from] AsymmetricPrivateKeyJwkError),
#[class(type)]
#[error("very large data")]
VeryLargeData,
#[class(generic)]
#[error(transparent)]
Der(#[from] der::Error),
}
#[op2]
#[serde]
pub fn op_node_export_private_key_jwk(
#[cppgc] handle: &KeyObjectHandle,
) -> Result<deno_core::serde_json::Value, ExportPrivateKeyJwkError> {
let private_key = handle
.as_private_key()
.ok_or(AsymmetricPrivateKeyJwkError::KeyIsNotAsymmetricPrivateKey)?;
Ok(private_key.export_jwk()?)
}
#[derive(Debug, thiserror::Error, deno_error::JsError)]
pub enum ExportPrivateKeyDerError {
#[class(inherit)]
#[error(transparent)]
AsymmetricPrivateKeyDer(
#[from]
#[inherit]
AsymmetricPrivateKeyDerError,
),
#[class(type)]
#[error("{0}")]
UnsupportedCipher(String),
#[class(type)]
#[error(
"cipher and passphrase must both be provided for encrypted key export"
)]
MissingCipherOrPassphrase,
#[class(type)]
#[error("encryption is only supported for PKCS#8 private keys")]
EncryptionRequiresPkcs8,
#[class(generic)]
#[error("failed to encrypt private key")]
EncryptionFailed,
}
fn encrypt_private_key_pkcs8_der(
data: &[u8],
cipher_name: &str,
passphrase: &[u8],
) -> Result<Vec<u8>, ExportPrivateKeyDerError> {
use pkcs8::pkcs5::pbes2;
let mut salt = [0u8; 16];
let mut iv = [0u8; 16];
thread_rng().fill_bytes(&mut salt);
thread_rng().fill_bytes(&mut iv);
const ITERATIONS: u32 = 2048;
let pbes2_params = match cipher_name {
"aes-128-cbc" => {
pbes2::Parameters::pbkdf2_sha256_aes128cbc(ITERATIONS, &salt, &iv)
.map_err(|_| ExportPrivateKeyDerError::EncryptionFailed)?
}
"aes-256-cbc" => {
pbes2::Parameters::pbkdf2_sha256_aes256cbc(ITERATIONS, &salt, &iv)
.map_err(|_| ExportPrivateKeyDerError::EncryptionFailed)?
}
_ => {
return Err(ExportPrivateKeyDerError::UnsupportedCipher(format!(
"Unsupported cipher for PKCS#8 DER encryption: {cipher_name}"
)));
}
};
let encrypted_data = pbes2_params
.encrypt(passphrase, data)
.map_err(|_| ExportPrivateKeyDerError::EncryptionFailed)?;
let info = pkcs8::EncryptedPrivateKeyInfo {
encryption_algorithm: pbes2_params.into(),
encrypted_data: &encrypted_data,
};
let doc: SecretDocument = (&info)
.try_into()
.map_err(|_| ExportPrivateKeyDerError::EncryptionFailed)?;
Ok(doc.as_bytes().to_vec())
}
#[op2]
#[buffer]
pub fn op_node_export_private_key_der(
#[cppgc] handle: &KeyObjectHandle,
#[string] typ: &str,
#[string] cipher: Option<String>,
#[string] passphrase: Option<String>,
) -> Result<Box<[u8]>, ExportPrivateKeyDerError> {
let private_key = handle
.as_private_key()
.ok_or(AsymmetricPrivateKeyDerError::KeyIsNotAsymmetricPrivateKey)?;
let data = private_key.export_der(typ)?;
match (&cipher, &passphrase) {
(Some(cipher), Some(passphrase)) => {
if typ != "pkcs8" {
return Err(ExportPrivateKeyDerError::EncryptionRequiresPkcs8);
}
let encrypted =
encrypt_private_key_pkcs8_der(&data, cipher, passphrase.as_bytes())?;
Ok(encrypted.into_boxed_slice())
}
(Some(_), None) | (None, Some(_)) => {
Err(ExportPrivateKeyDerError::MissingCipherOrPassphrase)
}
(None, None) => Ok(data),
}
}
#[op2]
#[string]
pub fn op_node_key_type(#[cppgc] handle: &KeyObjectHandle) -> &'static str {
match handle {
KeyObjectHandle::AsymmetricPrivate(_) => "private",
KeyObjectHandle::AsymmetricPublic(_) => "public",
KeyObjectHandle::Secret(_) => "secret",
}
}
#[op2(fast)]
pub fn op_node_key_equals(
#[cppgc] handle: &KeyObjectHandle,
#[cppgc] other: &KeyObjectHandle,
) -> bool {
handle == other
}
#[op2]
#[cppgc]
pub fn op_node_derive_public_key_from_private_key(
#[cppgc] handle: &KeyObjectHandle,
) -> Result<KeyObjectHandle, JsErrorBox> {
let Some(private_key) = handle.as_private_key() else {
return Err(JsErrorBox::type_error("expected private key"));
};
Ok(KeyObjectHandle::AsymmetricPublic(
private_key.to_public_key(),
))
}
#[derive(Debug, thiserror::Error, deno_error::JsError)]
pub enum PfxLoadError {
#[class(generic)]
#[error("not enough data")]
NotEnoughData,
#[class(generic)]
#[error("mac verify failure")]
MacVerifyFailure,
#[class(generic)]
#[error("PFX contains no usable certificate")]
NoCert,
#[class(generic)]
#[error("PFX contains no usable private key")]
NoKey,
#[class(generic)]
#[error(
"failed to decrypt PFX private key (wrong passphrase or unsupported encryption)"
)]
KeyDecryptFailed,
#[class(generic)]
#[error("failed to encode PFX contents: {0}")]
Encode(String),
}
#[derive(Debug, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct LoadPfxResult {
pub cert: String,
pub key: String,
pub ca: Vec<String>,
}
fn der_to_pem(label: &str, der: &[u8]) -> String {
use base64::engine::general_purpose::STANDARD;
let body = STANDARD.encode(der);
let mut out = String::with_capacity(body.len() + 64);
out.push_str("-----BEGIN ");
out.push_str(label);
out.push_str("-----\n");
for chunk in body.as_bytes().chunks(64) {
out.push_str(std::str::from_utf8(chunk).unwrap());
out.push('\n');
}
out.push_str("-----END ");
out.push_str(label);
out.push_str("-----\n");
out
}
const PFX_PBKDF_ITERATIONS_CAP: u64 = 600_000;
const PFX_SCRYPT_MEMORY_CAP: u128 = 1 << 30;
#[op2]
#[serde]
pub fn op_node_load_pfx(
#[buffer] pfx: &[u8],
#[string] passphrase: Option<String>,
) -> Result<LoadPfxResult, PfxLoadError> {
let parsed = Pkcs12::parse(pfx).map_err(|_| PfxLoadError::NotEnoughData)?;
let password = passphrase.as_deref().unwrap_or("");
let bmp_password = bmp_string(password);
if let Some(mac_data) = &parsed.mac_data {
let iterations = u64::from(mac_data.iterations);
if iterations > PFX_PBKDF_ITERATIONS_CAP {
return Err(PfxLoadError::MacVerifyFailure);
}
let data = parsed
.auth_safe
.data(&bmp_password)
.ok_or(PfxLoadError::MacVerifyFailure)?;
let ok = verify_pkcs12_mac(
&mac_data.mac.digest_algorithm,
&mac_data.mac.digest,
&mac_data.salt,
iterations,
&data,
&bmp_password,
)
.ok_or(PfxLoadError::MacVerifyFailure)?;
if !ok {
return Err(PfxLoadError::MacVerifyFailure);
}
}
let safe_bags = pfx_safe_bags(&parsed, password, &bmp_password)?;
let mut cert_ders: Vec<Vec<u8>> = Vec::new();
let mut key_ders: Vec<Vec<u8>> = Vec::new();
for bag in &safe_bags {
match &bag.bag {
Pkcs12SafeBagKind::CertBag(Pkcs12CertBag::X509(der)) => {
cert_ders.push(der.clone());
}
Pkcs12SafeBagKind::Pkcs8ShroudedKeyBag(epki) => {
let der = decrypt_pfx_blob(
&epki.encryption_algorithm,
&epki.encrypted_data,
password,
&bmp_password,
)
.ok_or(PfxLoadError::KeyDecryptFailed)?;
key_ders.push(der);
}
_ => {}
}
}
if cert_ders.is_empty() {
return Err(PfxLoadError::NoCert);
}
if key_ders.is_empty() {
return Err(PfxLoadError::NoKey);
}
let mut iter = cert_ders.into_iter();
let leaf = iter.next().unwrap();
let cert = der_to_pem("CERTIFICATE", &leaf);
let ca: Vec<String> =
iter.map(|der| der_to_pem("CERTIFICATE", &der)).collect();
let key = der_to_pem("PRIVATE KEY", &key_ders[0]);
Ok(LoadPfxResult { cert, key, ca })
}
const PBES2_OID_ARCS: [u64; 7] = [1, 2, 840, 113_549, 1, 5, 13];
fn decrypt_pfx_blob(
alg: &Pkcs12AlgorithmIdentifier,
ciphertext: &[u8],
utf8_password: &str,
bmp_password: &[u8],
) -> Option<Vec<u8>> {
match alg {
Pkcs12AlgorithmIdentifier::PbewithSHAAnd40BitRC2CBC(params)
| Pkcs12AlgorithmIdentifier::PbeWithSHAAnd3KeyTripleDESCBC(params) => {
if params.iterations > PFX_PBKDF_ITERATIONS_CAP {
return None;
}
alg.decrypt_pbe(ciphertext, bmp_password)
}
Pkcs12AlgorithmIdentifier::OtherAlg(other)
if other.algorithm_type.components().as_slice()
== PBES2_OID_ARCS.as_slice() =>
{
let alg_der = yasna::construct_der(|w| alg.write(w));
let scheme = pkcs8::pkcs5::EncryptionScheme::from_der(&alg_der).ok()?;
if let Some(pbkdf2) = scheme.pbes2().and_then(|p| p.kdf.pbkdf2())
&& u64::from(pbkdf2.iteration_count) > PFX_PBKDF_ITERATIONS_CAP
{
return None;
}
if let Some(scrypt) = scheme.pbes2().and_then(|p| p.kdf.scrypt()) {
let working_set = u128::from(scrypt.cost_parameter)
.saturating_mul(u128::from(scrypt.block_size))
.saturating_mul(128);
if working_set > PFX_SCRYPT_MEMORY_CAP {
return None;
}
}
scheme.decrypt(utf8_password.as_bytes(), ciphertext).ok()
}
_ => None,
}
}
fn pfx_safe_bags(
parsed: &Pkcs12,
utf8_password: &str,
bmp_password: &[u8],
) -> Result<Vec<Pkcs12SafeBag>, PfxLoadError> {
let outer = pfx_content_data(&parsed.auth_safe, utf8_password, bmp_password)?;
let safe_contents: Vec<Pkcs12ContentInfo> = yasna::parse_der(&outer, |r| {
r.collect_sequence_of(Pkcs12ContentInfo::parse)
})
.map_err(|e| PfxLoadError::Encode(e.to_string()))?;
let mut bags: Vec<Pkcs12SafeBag> = Vec::new();
for content in &safe_contents {
let bytes = pfx_content_data(content, utf8_password, bmp_password)?;
let parsed: Vec<Pkcs12SafeBag> =
yasna::parse_der(&bytes, |r| r.collect_sequence_of(Pkcs12SafeBag::parse))
.map_err(|e| PfxLoadError::Encode(e.to_string()))?;
bags.extend(parsed);
}
Ok(bags)
}
fn pfx_content_data(
ci: &Pkcs12ContentInfo,
utf8_password: &str,
bmp_password: &[u8],
) -> Result<Vec<u8>, PfxLoadError> {
match ci {
Pkcs12ContentInfo::Data(d) => Ok(d.clone()),
Pkcs12ContentInfo::EncryptedData(e) => decrypt_pfx_blob(
&e.encrypted_content_info.content_encryption_algorithm,
&e.encrypted_content_info.encrypted_content,
utf8_password,
bmp_password,
)
.ok_or_else(|| {
PfxLoadError::Encode(
"failed to decrypt PFX contents (wrong passphrase or unsupported encryption)"
.into(),
)
}),
Pkcs12ContentInfo::OtherContext(_) => {
Err(PfxLoadError::Encode("unsupported PFX content info".into()))
}
}
}
fn bmp_string(s: &str) -> Vec<u8> {
let utf16: Vec<u16> = s.encode_utf16().collect();
let mut bytes = Vec::with_capacity(utf16.len() * 2 + 2);
for c in utf16 {
bytes.extend_from_slice(&c.to_be_bytes());
}
bytes.extend_from_slice(&[0x00, 0x00]);
bytes
}
fn pkcs12_pbkdf<D: Digest + FixedOutputReset>(
pass: &[u8],
salt: &[u8],
iterations: u64,
id: u8,
size: usize,
v: usize,
) -> Vec<u8> {
let u = <D as Digest>::output_size();
let d = vec![id; v];
let s: Vec<u8> = if salt.is_empty() {
Vec::new()
} else {
salt
.iter()
.cycle()
.take(v * salt.len().div_ceil(v))
.copied()
.collect()
};
let p: Vec<u8> = if pass.is_empty() {
Vec::new()
} else {
pass
.iter()
.cycle()
.take(v * pass.len().div_ceil(v))
.copied()
.collect()
};
let mut i: Vec<u8> = Vec::with_capacity(s.len() + p.len());
i.extend_from_slice(&s);
i.extend_from_slice(&p);
let c = size.div_ceil(u);
let mut out: Vec<u8> = Vec::with_capacity(c * u);
let mut hasher = D::new();
for _ in 0..c {
Digest::update(&mut hasher, &d);
Digest::update(&mut hasher, &i);
let mut ai = hasher.finalize_reset().to_vec();
for _ in 1..iterations {
Digest::update(&mut hasher, &ai);
ai = hasher.finalize_reset().to_vec();
}
out.extend_from_slice(&ai);
if i.is_empty() {
continue;
}
let b: Vec<u8> = ai.iter().cycle().take(v).copied().collect();
for chunk in i.chunks_mut(v) {
let mut carry: u16 = 1;
for j in (0..v).rev() {
let sum = chunk[j] as u16 + b[j] as u16 + carry;
chunk[j] = (sum & 0xff) as u8;
carry = sum >> 8;
}
}
}
out.truncate(size);
out
}
fn pkcs12_hmac<D>(key: &[u8], data: &[u8]) -> Vec<u8>
where
D: Digest
+ digest::core_api::CoreProxy
+ FixedOutputReset
+ digest::core_api::BlockSizeUser,
<D as digest::core_api::CoreProxy>::Core: digest::core_api::BufferKindUser<
BufferKind = digest::block_buffer::Eager,
> + digest::core_api::FixedOutputCore
+ digest::HashMarker
+ Default
+ Clone,
<<D as digest::core_api::CoreProxy>::Core as digest::core_api::BlockSizeUser>::BlockSize: digest::typenum::IsLess<digest::typenum::U256>,
digest::typenum::Le<<<D as digest::core_api::CoreProxy>::Core as digest::core_api::BlockSizeUser>::BlockSize, digest::typenum::U256>: digest::typenum::NonZero,
{
let mut mac = <Hmac<D> as Mac>::new_from_slice(key).unwrap();
Mac::update(&mut mac, data);
mac.finalize().into_bytes().to_vec()
}
const OID_SHA1: &[u64] = &[1, 3, 14, 3, 2, 26];
const OID_SHA224: &[u64] = &[2, 16, 840, 1, 101, 3, 4, 2, 4];
const OID_SHA256: &[u64] = &[2, 16, 840, 1, 101, 3, 4, 2, 1];
const OID_SHA384: &[u64] = &[2, 16, 840, 1, 101, 3, 4, 2, 2];
const OID_SHA512: &[u64] = &[2, 16, 840, 1, 101, 3, 4, 2, 3];
const OID_SHA512_224: &[u64] = &[2, 16, 840, 1, 101, 3, 4, 2, 5];
const OID_SHA512_256: &[u64] = &[2, 16, 840, 1, 101, 3, 4, 2, 6];
enum Pkcs12MacAlgorithm {
Sha1,
Sha224,
Sha256,
Sha384,
Sha512,
Sha512_224,
Sha512_256,
}
fn pkcs12_mac_algorithm(
algorithm: &Pkcs12AlgorithmIdentifier,
) -> Option<Pkcs12MacAlgorithm> {
let oid = match algorithm {
Pkcs12AlgorithmIdentifier::Sha1 => return Some(Pkcs12MacAlgorithm::Sha1),
Pkcs12AlgorithmIdentifier::OtherAlg(other) => {
other.algorithm_type.components().as_slice()
}
_ => return None,
};
if oid == OID_SHA1 {
Some(Pkcs12MacAlgorithm::Sha1)
} else if oid == OID_SHA224 {
Some(Pkcs12MacAlgorithm::Sha224)
} else if oid == OID_SHA256 {
Some(Pkcs12MacAlgorithm::Sha256)
} else if oid == OID_SHA384 {
Some(Pkcs12MacAlgorithm::Sha384)
} else if oid == OID_SHA512 {
Some(Pkcs12MacAlgorithm::Sha512)
} else if oid == OID_SHA512_224 {
Some(Pkcs12MacAlgorithm::Sha512_224)
} else if oid == OID_SHA512_256 {
Some(Pkcs12MacAlgorithm::Sha512_256)
} else {
None
}
}
fn verify_pkcs12_mac(
algorithm: &Pkcs12AlgorithmIdentifier,
expected: &[u8],
salt: &[u8],
iterations: u64,
data: &[u8],
password: &[u8],
) -> Option<bool> {
let mac_alg = pkcs12_mac_algorithm(algorithm)?;
let computed = match mac_alg {
Pkcs12MacAlgorithm::Sha1 => {
let key =
pkcs12_pbkdf::<sha1::Sha1>(password, salt, iterations, 3, 20, 64);
pkcs12_hmac::<sha1::Sha1>(&key, data)
}
Pkcs12MacAlgorithm::Sha224 => {
let key =
pkcs12_pbkdf::<sha2::Sha224>(password, salt, iterations, 3, 28, 64);
pkcs12_hmac::<sha2::Sha224>(&key, data)
}
Pkcs12MacAlgorithm::Sha256 => {
let key =
pkcs12_pbkdf::<sha2::Sha256>(password, salt, iterations, 3, 32, 64);
pkcs12_hmac::<sha2::Sha256>(&key, data)
}
Pkcs12MacAlgorithm::Sha384 => {
let key =
pkcs12_pbkdf::<sha2::Sha384>(password, salt, iterations, 3, 48, 128);
pkcs12_hmac::<sha2::Sha384>(&key, data)
}
Pkcs12MacAlgorithm::Sha512 => {
let key =
pkcs12_pbkdf::<sha2::Sha512>(password, salt, iterations, 3, 64, 128);
pkcs12_hmac::<sha2::Sha512>(&key, data)
}
Pkcs12MacAlgorithm::Sha512_224 => {
let key = pkcs12_pbkdf::<sha2::Sha512_224>(
password, salt, iterations, 3, 28, 128,
);
pkcs12_hmac::<sha2::Sha512_224>(&key, data)
}
Pkcs12MacAlgorithm::Sha512_256 => {
let key = pkcs12_pbkdf::<sha2::Sha512_256>(
password, salt, iterations, 3, 32, 128,
);
pkcs12_hmac::<sha2::Sha512_256>(&key, data)
}
};
use subtle::ConstantTimeEq;
Some(bool::from(computed.ct_eq(expected)))
}
#[derive(Debug, thiserror::Error, deno_error::JsError)]
pub enum CrlValidationError {
#[class(generic)]
#[error("Failed to parse CRL")]
ParseFailed,
}
#[op2(fast)]
pub fn op_node_validate_crl(
#[buffer] crl: &[u8],
) -> Result<(), CrlValidationError> {
if crl.starts_with(b"-----") {
match x509_parser::pem::parse_x509_pem(crl) {
Ok((_, pem)) => {
x509_parser::parse_x509_crl(&pem.contents)
.map_err(|_| CrlValidationError::ParseFailed)?;
}
Err(_) => return Err(CrlValidationError::ParseFailed),
}
} else {
x509_parser::parse_x509_crl(crl)
.map_err(|_| CrlValidationError::ParseFailed)?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pkcs12_pbkdf_sha1() {
let pass = bmp_string("");
assert_eq!(pass, vec![0, 0]);
let salt: [u8; 8] = [0x9a, 0xf4, 0x70, 0x29, 0x58, 0xa8, 0xe9, 0x5c];
let got = pkcs12_pbkdf::<sha1::Sha1>(&pass, &salt, 2048, 1, 24, 64);
let expected: [u8; 24] = [
0xc2, 0x29, 0x4a, 0xa6, 0xd0, 0x29, 0x30, 0xeb, 0x5c, 0xe9, 0xc3, 0x29,
0xec, 0xcb, 0x9a, 0xee, 0x1c, 0xb1, 0x36, 0xba, 0xea, 0x74, 0x65, 0x57,
];
assert_eq!(got, expected);
}
#[test]
fn pkcs12_pbkdf_sha256() {
let pass = bmp_string("secret");
let salt: [u8; 8] = [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef];
let got = pkcs12_pbkdf::<sha2::Sha256>(&pass, &salt, 1000, 3, 32, 64);
let expected: [u8; 32] = [
0x7f, 0xe1, 0x91, 0x75, 0x7d, 0xca, 0xf1, 0xed, 0x6a, 0x29, 0x77, 0xb9,
0xb9, 0x15, 0x3f, 0x60, 0x82, 0xaf, 0x0b, 0xda, 0xfd, 0x09, 0x35, 0x2d,
0xcd, 0xaa, 0x96, 0x7f, 0x57, 0x17, 0x82, 0xb0,
];
assert_eq!(got, expected);
}
#[test]
fn pkcs12_pbkdf_sha384() {
let pass = bmp_string("secret");
let salt: [u8; 8] = [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef];
let got = pkcs12_pbkdf::<sha2::Sha384>(&pass, &salt, 1000, 3, 48, 128);
let expected: [u8; 48] = [
0x6a, 0x59, 0x71, 0x72, 0x05, 0x22, 0x76, 0x31, 0x21, 0xf4, 0x9a, 0x1d,
0x5c, 0x04, 0x10, 0xa1, 0xdb, 0x42, 0x0b, 0xe4, 0x96, 0x6d, 0xc5, 0x2f,
0x51, 0x91, 0x9d, 0x91, 0x15, 0x2d, 0x60, 0x2d, 0x31, 0x1c, 0x4c, 0xb0,
0x8d, 0x99, 0x83, 0xad, 0xaf, 0x68, 0xff, 0x5d, 0xdd, 0x69, 0x0b, 0x87,
];
assert_eq!(got, expected);
}
#[test]
fn pkcs12_pbkdf_sha512() {
let pass = bmp_string("secret");
let salt: [u8; 8] = [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef];
let got = pkcs12_pbkdf::<sha2::Sha512>(&pass, &salt, 1000, 3, 64, 128);
let expected: [u8; 64] = [
0x3e, 0x5c, 0x5d, 0xb5, 0xf5, 0xe3, 0xd0, 0xc2, 0x23, 0x2e, 0xbf, 0xbb,
0x86, 0x08, 0x23, 0x7a, 0x2b, 0x0a, 0xf6, 0x00, 0x30, 0xe6, 0xa0, 0x08,
0x2a, 0xbe, 0x7c, 0x19, 0x3f, 0x52, 0x5e, 0x98, 0x97, 0x6f, 0xb2, 0xbb,
0x1c, 0x88, 0xc3, 0xc6, 0xf8, 0xb5, 0x64, 0x91, 0x74, 0x3f, 0xc5, 0x04,
0xfb, 0x3d, 0xd9, 0x10, 0xf4, 0xf9, 0x5e, 0xf6, 0x99, 0xc2, 0x48, 0x15,
0xf1, 0x3e, 0xc1, 0x74,
];
assert_eq!(got, expected);
}
#[test]
fn bmp_string_basic() {
assert_eq!(bmp_string(""), vec![0x00, 0x00]);
assert_eq!(
bmp_string("Beavis"),
vec![
0x00, 0x42, 0x00, 0x65, 0x00, 0x61, 0x00, 0x76, 0x00, 0x69, 0x00, 0x73,
0x00, 0x00,
],
);
}
}