diff options
Diffstat (limited to 'ext/crypto')
-rw-r--r-- | ext/crypto/00_crypto.js | 181 | ||||
-rw-r--r-- | ext/crypto/01_webidl.js | 13 | ||||
-rw-r--r-- | ext/crypto/key.rs | 2 | ||||
-rw-r--r-- | ext/crypto/lib.deno_crypto.d.ts | 8 | ||||
-rw-r--r-- | ext/crypto/lib.rs | 430 |
5 files changed, 628 insertions, 6 deletions
diff --git a/ext/crypto/00_crypto.js b/ext/crypto/00_crypto.js index b3131a4f8..5eb283b22 100644 --- a/ext/crypto/00_crypto.js +++ b/ext/crypto/00_crypto.js @@ -68,6 +68,7 @@ }, Pbkdf2Params: { hash: "HashAlgorithmIdentifier", salt: "BufferSource" }, RsaOaepParams: { label: "BufferSource" }, + RsaHashedImportParams: { hash: "HashAlgorithmIdentifier" }, }; const supportedAlgorithms = { @@ -102,6 +103,9 @@ "HMAC": null, }, "importKey": { + "RSASSA-PKCS1-v1_5": "RsaHashedImportParams", + "RSA-PSS": "RsaHashedImportParams", + "RSA-OAEP": "RsaHashedImportParams", "HMAC": "HmacImportParams", "HKDF": null, "PBKDF2": null, @@ -664,7 +668,6 @@ * @param {KeyUsages[]} keyUsages * @returns {Promise<any>} */ - // deno-lint-ignore require-await async importKey(format, keyData, algorithm, extractable, keyUsages) { webidl.assertBranded(this, SubtleCrypto); const prefix = "Failed to execute 'importKey' on 'SubtleCrypto'"; @@ -898,8 +901,180 @@ return key; } - // TODO(@littledivy): RSASSA-PKCS1-v1_5 - // TODO(@littledivy): RSA-PSS + case "RSASSA-PKCS1-v1_5": { + switch (format) { + case "pkcs8": { + // 1. + if ( + ArrayPrototypeFind( + keyUsages, + (u) => !ArrayPrototypeIncludes(["sign"], u), + ) !== undefined + ) { + throw new DOMException("Invalid key usages", "SyntaxError"); + } + + if (keyUsages.length == 0) { + throw new DOMException("Key usage is empty", "SyntaxError"); + } + + // 2-9. + const { modulusLength, publicExponent, data } = await core + .opAsync( + "op_crypto_import_key", + { + algorithm: "RSASSA-PKCS1-v1_5", + format: "pkcs8", + // Needed to perform step 7 without normalization. + hash: normalizedAlgorithm.hash.name, + }, + keyData, + ); + + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, { + // PKCS#1 for RSA + type: "raw", + data, + }); + + const algorithm = { + name: "RSASSA-PKCS1-v1_5", + modulusLength, + publicExponent, + hash: normalizedAlgorithm.hash, + }; + + const key = constructKey( + "private", + extractable, + usageIntersection(keyUsages, recognisedUsages), + algorithm, + handle, + ); + + return key; + } + default: + throw new DOMException("Not implemented", "NotSupportedError"); + } + } + case "RSA-PSS": { + switch (format) { + case "pkcs8": { + // 1. + if ( + ArrayPrototypeFind( + keyUsages, + (u) => !ArrayPrototypeIncludes(["sign"], u), + ) !== undefined + ) { + throw new DOMException("Invalid key usages", "SyntaxError"); + } + + if (keyUsages.length == 0) { + throw new DOMException("Key usage is empty", "SyntaxError"); + } + + // 2-9. + const { modulusLength, publicExponent, data } = await core + .opAsync( + "op_crypto_import_key", + { + algorithm: "RSA-PSS", + format: "pkcs8", + // Needed to perform step 7 without normalization. + hash: normalizedAlgorithm.hash.name, + }, + keyData, + ); + + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, { + // PKCS#1 for RSA + type: "raw", + data, + }); + + const algorithm = { + name: "RSA-PSS", + modulusLength, + publicExponent, + hash: normalizedAlgorithm.hash, + }; + + const key = constructKey( + "private", + extractable, + usageIntersection(keyUsages, recognisedUsages), + algorithm, + handle, + ); + + return key; + } + default: + throw new DOMException("Not implemented", "NotSupportedError"); + } + } + case "RSA-OAEP": { + switch (format) { + case "pkcs8": { + // 1. + if ( + ArrayPrototypeFind( + keyUsages, + (u) => !ArrayPrototypeIncludes(["decrypt", "unwrapKey"], u), + ) !== undefined + ) { + throw new DOMException("Invalid key usages", "SyntaxError"); + } + + if (keyUsages.length == 0) { + throw new DOMException("Key usage is empty", "SyntaxError"); + } + + // 2-9. + const { modulusLength, publicExponent, data } = await core + .opAsync( + "op_crypto_import_key", + { + algorithm: "RSA-OAEP", + format: "pkcs8", + // Needed to perform step 7 without normalization. + hash: normalizedAlgorithm.hash.name, + }, + keyData, + ); + + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, { + // PKCS#1 for RSA + type: "raw", + data, + }); + + const algorithm = { + name: "RSA-OAEP", + modulusLength, + publicExponent, + hash: normalizedAlgorithm.hash, + }; + + const key = constructKey( + "private", + extractable, + usageIntersection(keyUsages, recognisedUsages), + algorithm, + handle, + ); + + return key; + } + default: + throw new DOMException("Not implemented", "NotSupportedError"); + } + } // TODO(@littledivy): ECDSA case "HKDF": { if (format !== "raw") { diff --git a/ext/crypto/01_webidl.js b/ext/crypto/01_webidl.js index 78a897fbc..8d9e061c6 100644 --- a/ext/crypto/01_webidl.js +++ b/ext/crypto/01_webidl.js @@ -103,6 +103,19 @@ dictRsaHashedKeyGenParams, ); + const dictRsaHashedImportParams = [ + ...dictAlgorithm, + { + key: "hash", + converter: webidl.converters.HashAlgorithmIdentifier, + required: true, + }, + ]; + + webidl.converters.RsaHashedImportParams = webidl.createDictionaryConverter( + "RsaHashedImportParams", + dictRsaHashedImportParams, + ); webidl.converters.NamedCurve = webidl.converters.DOMString; const dictEcKeyGenParams = [ diff --git a/ext/crypto/key.rs b/ext/crypto/key.rs index d55a1d062..63d48dd10 100644 --- a/ext/crypto/key.rs +++ b/ext/crypto/key.rs @@ -17,7 +17,7 @@ pub enum KeyType { Secret, } -#[derive(Serialize, Deserialize, Copy, Clone)] +#[derive(Serialize, Deserialize, Copy, Clone, PartialEq)] pub enum CryptoHash { #[serde(rename = "SHA-1")] Sha1, diff --git a/ext/crypto/lib.deno_crypto.d.ts b/ext/crypto/lib.deno_crypto.d.ts index 5a554015a..55b94c24d 100644 --- a/ext/crypto/lib.deno_crypto.d.ts +++ b/ext/crypto/lib.deno_crypto.d.ts @@ -69,6 +69,10 @@ interface EcdsaParams extends Algorithm { hash: HashAlgorithmIdentifier; } +interface RsaHashedImportParams extends Algorithm { + hash: HashAlgorithmIdentifier; +} + interface RsaHashedKeyGenParams extends RsaKeyGenParams { hash: HashAlgorithmIdentifier; } @@ -170,9 +174,9 @@ interface SubtleCrypto { keyUsages: KeyUsage[], ): Promise<CryptoKey>; importKey( - format: "raw", + format: Exclude<KeyFormat, "jwk">, keyData: BufferSource, - algorithm: AlgorithmIdentifier | HmacImportParams, + algorithm: AlgorithmIdentifier | HmacImportParams | RsaHashedImportParams, extractable: boolean, keyUsages: KeyUsage[], ): Promise<CryptoKey>; diff --git a/ext/crypto/lib.rs b/ext/crypto/lib.rs index 47137b210..ef32499b9 100644 --- a/ext/crypto/lib.rs +++ b/ext/crypto/lib.rs @@ -12,8 +12,10 @@ use deno_core::Extension; use deno_core::OpState; use deno_core::ZeroCopyBuf; use serde::Deserialize; +use serde::Serialize; use std::cell::RefCell; +use std::convert::TryFrom; use std::convert::TryInto; use std::num::NonZeroU32; use std::rc::Rc; @@ -37,6 +39,8 @@ use ring::signature::EcdsaSigningAlgorithm; use ring::signature::EcdsaVerificationAlgorithm; use ring::signature::KeyPair; use rsa::padding::PaddingScheme; +use rsa::pkcs1::der::Decodable; +use rsa::pkcs1::der::Encodable; use rsa::pkcs1::FromRsaPrivateKey; use rsa::pkcs1::ToRsaPrivateKey; use rsa::pkcs8::der::asn1; @@ -66,6 +70,31 @@ lazy_static! { static ref PUB_EXPONENT_2: BigUint = BigUint::from_u64(65537).unwrap(); } +const RSA_ENCRYPTION_OID: rsa::pkcs8::ObjectIdentifier = + rsa::pkcs8::ObjectIdentifier::new("1.2.840.113549.1.1.1"); +const SHA1_RSA_ENCRYPTION_OID: rsa::pkcs8::ObjectIdentifier = + rsa::pkcs8::ObjectIdentifier::new("1.2.840.113549.1.1.5"); +const SHA256_RSA_ENCRYPTION_OID: rsa::pkcs8::ObjectIdentifier = + rsa::pkcs8::ObjectIdentifier::new("1.2.840.113549.1.1.11"); +const SHA384_RSA_ENCRYPTION_OID: rsa::pkcs8::ObjectIdentifier = + rsa::pkcs8::ObjectIdentifier::new("1.2.840.113549.1.1.12"); +const SHA512_RSA_ENCRYPTION_OID: rsa::pkcs8::ObjectIdentifier = + rsa::pkcs8::ObjectIdentifier::new("1.2.840.113549.1.1.13"); +const RSASSA_PSS_OID: rsa::pkcs8::ObjectIdentifier = + rsa::pkcs8::ObjectIdentifier::new("1.2.840.113549.1.1.10"); +const ID_SHA1_OID: rsa::pkcs8::ObjectIdentifier = + rsa::pkcs8::ObjectIdentifier::new("1.3.14.3.2.26"); +const ID_SHA256_OID: rsa::pkcs8::ObjectIdentifier = + rsa::pkcs8::ObjectIdentifier::new("2.16.840.1.101.3.4.2.1"); +const ID_SHA384_OID: rsa::pkcs8::ObjectIdentifier = + rsa::pkcs8::ObjectIdentifier::new("2.16.840.1.101.3.4.2.2"); +const ID_SHA512_OID: rsa::pkcs8::ObjectIdentifier = + rsa::pkcs8::ObjectIdentifier::new("2.16.840.1.101.3.4.2.3"); +const ID_MFG1: rsa::pkcs8::ObjectIdentifier = + rsa::pkcs8::ObjectIdentifier::new("1.2.840.113549.1.1.8"); +const RSAES_OAEP_OID: rsa::pkcs8::ObjectIdentifier = + rsa::pkcs8::ObjectIdentifier::new("1.2.840.113549.1.1.7"); + pub fn init(maybe_seed: Option<u64>) -> Extension { Extension::builder() .js(include_js_files!( @@ -82,6 +111,7 @@ pub fn init(maybe_seed: Option<u64>) -> Extension { ("op_crypto_sign_key", op_async(op_crypto_sign_key)), ("op_crypto_verify_key", op_async(op_crypto_verify_key)), ("op_crypto_derive_bits", op_async(op_crypto_derive_bits)), + ("op_crypto_import_key", op_async(op_crypto_import_key)), ("op_crypto_export_key", op_async(op_crypto_export_key)), ("op_crypto_encrypt_key", op_async(op_crypto_encrypt_key)), ("op_crypto_decrypt_key", op_async(op_crypto_decrypt_key)), @@ -817,6 +847,406 @@ pub async fn op_crypto_encrypt_key( } } +// The parameters field associated with OID id-RSASSA-PSS +// Defined in RFC 3447, section A.2.3 +// +// RSASSA-PSS-params ::= SEQUENCE { +// hashAlgorithm [0] HashAlgorithm DEFAULT sha1, +// maskGenAlgorithm [1] MaskGenAlgorithm DEFAULT mgf1SHA1, +// saltLength [2] INTEGER DEFAULT 20, +// trailerField [3] TrailerField DEFAULT trailerFieldBC +// } +pub struct PssPrivateKeyParameters<'a> { + pub hash_algorithm: rsa::pkcs8::AlgorithmIdentifier<'a>, + pub mask_gen_algorithm: rsa::pkcs8::AlgorithmIdentifier<'a>, + pub salt_length: u32, +} + +// Context-specific tag number for hashAlgorithm. +const HASH_ALGORITHM_TAG: rsa::pkcs8::der::TagNumber = + rsa::pkcs8::der::TagNumber::new(0); + +// Context-specific tag number for maskGenAlgorithm. +const MASK_GEN_ALGORITHM_TAG: rsa::pkcs8::der::TagNumber = + rsa::pkcs8::der::TagNumber::new(1); + +// Context-specific tag number for saltLength. +const SALT_LENGTH_TAG: rsa::pkcs8::der::TagNumber = + rsa::pkcs8::der::TagNumber::new(2); + +impl<'a> TryFrom<rsa::pkcs8::der::asn1::Any<'a>> + for PssPrivateKeyParameters<'a> +{ + type Error = rsa::pkcs8::der::Error; + + fn try_from( + any: rsa::pkcs8::der::asn1::Any<'a>, + ) -> rsa::pkcs8::der::Result<PssPrivateKeyParameters> { + any.sequence(|decoder| { + let hash_algorithm = decoder + .context_specific(HASH_ALGORITHM_TAG)? + .map(TryInto::try_into) + .transpose()? + .unwrap(); + + let mask_gen_algorithm = decoder + .context_specific(MASK_GEN_ALGORITHM_TAG)? + .map(TryInto::try_into) + .transpose()? + .unwrap(); + + let salt_length = decoder + .context_specific(SALT_LENGTH_TAG)? + .map(TryInto::try_into) + .transpose()? + .unwrap_or(20); + + Ok(Self { + hash_algorithm, + mask_gen_algorithm, + salt_length, + }) + }) + } +} + +// The parameters field associated with OID id-RSAES-OAEP +// Defined in RFC 3447, section A.2.1 +// +// RSAES-OAEP-params ::= SEQUENCE { +// hashAlgorithm [0] HashAlgorithm DEFAULT sha1, +// maskGenAlgorithm [1] MaskGenAlgorithm DEFAULT mgf1SHA1, +// pSourceAlgorithm [2] PSourceAlgorithm DEFAULT pSpecifiedEmpty +// } +pub struct OaepPrivateKeyParameters<'a> { + pub hash_algorithm: rsa::pkcs8::AlgorithmIdentifier<'a>, + pub mask_gen_algorithm: rsa::pkcs8::AlgorithmIdentifier<'a>, + pub p_source_algorithm: rsa::pkcs8::AlgorithmIdentifier<'a>, +} + +impl<'a> TryFrom<rsa::pkcs8::der::asn1::Any<'a>> + for OaepPrivateKeyParameters<'a> +{ + type Error = rsa::pkcs8::der::Error; + + fn try_from( + any: rsa::pkcs8::der::asn1::Any<'a>, + ) -> rsa::pkcs8::der::Result<OaepPrivateKeyParameters> { + any.sequence(|decoder| { + let hash_algorithm = decoder.decode()?; + let mask_gen_algorithm = decoder.decode()?; + let p_source_algorithm = decoder.decode()?; + Ok(Self { + hash_algorithm, + mask_gen_algorithm, + p_source_algorithm, + }) + }) + } +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ImportKeyArg { + algorithm: Algorithm, + format: KeyFormat, + // RSASSA-PKCS1-v1_5 + hash: Option<CryptoHash>, +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ImportKeyResult { + data: ZeroCopyBuf, + // RSASSA-PKCS1-v1_5 + public_exponent: Option<ZeroCopyBuf>, + modulus_length: Option<usize>, +} + +pub async fn op_crypto_import_key( + _state: Rc<RefCell<OpState>>, + args: ImportKeyArg, + zero_copy: Option<ZeroCopyBuf>, +) -> Result<ImportKeyResult, AnyError> { + let zero_copy = zero_copy.ok_or_else(null_opbuf)?; + let data = &*zero_copy; + let algorithm = args.algorithm; + + match algorithm { + Algorithm::RsassaPkcs1v15 => { + match args.format { + KeyFormat::Pkcs8 => { + let hash = args + .hash + .ok_or_else(|| type_error("Missing argument hash".to_string()))?; + + // 2-3. + let pk_info = + rsa::pkcs8::PrivateKeyInfo::from_der(data).map_err(|e| { + custom_error("DOMExceptionOperationError", e.to_string()) + })?; + + // 4-5. + let alg = pk_info.algorithm.oid; + + // 6. + let pk_hash = match alg { + // rsaEncryption + RSA_ENCRYPTION_OID => None, + // sha1WithRSAEncryption + SHA1_RSA_ENCRYPTION_OID => Some(CryptoHash::Sha1), + // sha256WithRSAEncryption + SHA256_RSA_ENCRYPTION_OID => Some(CryptoHash::Sha256), + // sha384WithRSAEncryption + SHA384_RSA_ENCRYPTION_OID => Some(CryptoHash::Sha384), + // sha512WithRSAEncryption + SHA512_RSA_ENCRYPTION_OID => Some(CryptoHash::Sha512), + _ => return Err(type_error("Unsupported algorithm".to_string())), + }; + + // 7. + if let Some(pk_hash) = pk_hash { + if pk_hash != hash { + // TODO(@littledivy): DataError + return Err(type_error("Hash mismatch".to_string())); + } + } + + // 8-9. + let private_key = + rsa::pkcs1::RsaPrivateKey::from_der(pk_info.private_key).map_err( + |e| custom_error("DOMExceptionOperationError", e.to_string()), + )?; + + let bytes_consumed = private_key.encoded_len().map_err(|e| { + // TODO(@littledivy): DataError + custom_error("DOMExceptionOperationError", e.to_string()) + })?; + + if bytes_consumed + != rsa::pkcs1::der::Length::new(pk_info.private_key.len() as u16) + { + // TODO(@littledivy): DataError + return Err(type_error("Some bytes were not consumed".to_string())); + } + + Ok(ImportKeyResult { + data: pk_info.private_key.to_vec().into(), + public_exponent: Some( + private_key.public_exponent.as_bytes().to_vec().into(), + ), + modulus_length: Some(private_key.modulus.as_bytes().len() * 8), + }) + } + // TODO(@littledivy): spki + // TODO(@littledivy): jwk + _ => Err(type_error("Unsupported format".to_string())), + } + } + Algorithm::RsaPss => { + match args.format { + KeyFormat::Pkcs8 => { + let hash = args + .hash + .ok_or_else(|| type_error("Missing argument hash".to_string()))?; + + // 2-3. + let pk_info = + rsa::pkcs8::PrivateKeyInfo::from_der(data).map_err(|e| { + custom_error("DOMExceptionOperationError", e.to_string()) + })?; + + // 4-5. + let alg = pk_info.algorithm.oid; + + // 6. + let pk_hash = match alg { + // rsaEncryption + RSA_ENCRYPTION_OID => None, + // id-RSASSA-PSS + RSASSA_PSS_OID => { + // TODO(@littledivy): NotSupported error + let params = PssPrivateKeyParameters::try_from( + pk_info.algorithm.parameters.ok_or_else(|| { + type_error("Malformed parameters".to_string()) + })?, + ) + .map_err(|_| type_error("Malformed parameters".to_string()))?; + + let hash_alg = params.hash_algorithm; + let hash = match hash_alg.oid { + // id-sha1 + ID_SHA1_OID => Some(CryptoHash::Sha1), + // id-sha256 + ID_SHA256_OID => Some(CryptoHash::Sha256), + // id-sha384 + ID_SHA384_OID => Some(CryptoHash::Sha384), + // id-sha256 + ID_SHA512_OID => Some(CryptoHash::Sha512), + // TODO(@littledivy): DataError + _ => { + return Err(type_error( + "Unsupported hash algorithm".to_string(), + )) + } + }; + + if params.mask_gen_algorithm.oid != ID_MFG1 { + // TODO(@littledivy): NotSupportedError + return Err(type_error( + "Unsupported hash algorithm".to_string(), + )); + } + + hash + } + // TODO(@littledivy): DataError + _ => return Err(type_error("Unsupported algorithm".to_string())), + }; + + // 7. + if let Some(pk_hash) = pk_hash { + if pk_hash != hash { + // TODO(@littledivy): DataError + return Err(type_error("Hash mismatch".to_string())); + } + } + + // 8-9. + let private_key = + rsa::pkcs1::RsaPrivateKey::from_der(pk_info.private_key).map_err( + |e| custom_error("DOMExceptionOperationError", e.to_string()), + )?; + + let bytes_consumed = private_key.encoded_len().map_err(|e| { + // TODO(@littledivy): DataError + custom_error("DOMExceptionOperationError", e.to_string()) + })?; + + if bytes_consumed + != rsa::pkcs1::der::Length::new(pk_info.private_key.len() as u16) + { + // TODO(@littledivy): DataError + return Err(type_error("Some bytes were not consumed".to_string())); + } + + Ok(ImportKeyResult { + data: pk_info.private_key.to_vec().into(), + public_exponent: Some( + private_key.public_exponent.as_bytes().to_vec().into(), + ), + modulus_length: Some(private_key.modulus.as_bytes().len() * 8), + }) + } + // TODO(@littledivy): spki + // TODO(@littledivy): jwk + _ => Err(type_error("Unsupported format".to_string())), + } + } + Algorithm::RsaOaep => { + match args.format { + KeyFormat::Pkcs8 => { + let hash = args + .hash + .ok_or_else(|| type_error("Missing argument hash".to_string()))?; + + // 2-3. + let pk_info = + rsa::pkcs8::PrivateKeyInfo::from_der(data).map_err(|e| { + custom_error("DOMExceptionOperationError", e.to_string()) + })?; + + // 4-5. + let alg = pk_info.algorithm.oid; + + // 6. + let pk_hash = match alg { + // rsaEncryption + RSA_ENCRYPTION_OID => None, + // id-RSAES-OAEP + RSAES_OAEP_OID => { + // TODO(@littledivy): NotSupported error + let params = OaepPrivateKeyParameters::try_from( + pk_info.algorithm.parameters.ok_or_else(|| { + type_error("Malformed parameters".to_string()) + })?, + ) + .map_err(|_| type_error("Malformed parameters".to_string()))?; + + let hash_alg = params.hash_algorithm; + let hash = match hash_alg.oid { + // id-sha1 + ID_SHA1_OID => Some(CryptoHash::Sha1), + // id-sha256 + ID_SHA256_OID => Some(CryptoHash::Sha256), + // id-sha384 + ID_SHA384_OID => Some(CryptoHash::Sha384), + // id-sha256 + ID_SHA512_OID => Some(CryptoHash::Sha512), + // TODO(@littledivy): DataError + _ => { + return Err(type_error( + "Unsupported hash algorithm".to_string(), + )) + } + }; + + if params.mask_gen_algorithm.oid != ID_MFG1 { + // TODO(@littledivy): NotSupportedError + return Err(type_error( + "Unsupported hash algorithm".to_string(), + )); + } + + hash + } + // TODO(@littledivy): DataError + _ => return Err(type_error("Unsupported algorithm".to_string())), + }; + + // 7. + if let Some(pk_hash) = pk_hash { + if pk_hash != hash { + // TODO(@littledivy): DataError + return Err(type_error("Hash mismatch".to_string())); + } + } + + // 8-9. + let private_key = + rsa::pkcs1::RsaPrivateKey::from_der(pk_info.private_key).map_err( + |e| custom_error("DOMExceptionOperationError", e.to_string()), + )?; + + let bytes_consumed = private_key.encoded_len().map_err(|e| { + // TODO(@littledivy): DataError + custom_error("DOMExceptionOperationError", e.to_string()) + })?; + + if bytes_consumed + != rsa::pkcs1::der::Length::new(pk_info.private_key.len() as u16) + { + // TODO(@littledivy): DataError + return Err(type_error("Some bytes were not consumed".to_string())); + } + + Ok(ImportKeyResult { + data: pk_info.private_key.to_vec().into(), + public_exponent: Some( + private_key.public_exponent.as_bytes().to_vec().into(), + ), + modulus_length: Some(private_key.modulus.as_bytes().len() * 8), + }) + } + // TODO(@littledivy): spki + // TODO(@littledivy): jwk + _ => Err(type_error("Unsupported format".to_string())), + } + } + _ => Err(type_error("Unsupported algorithm".to_string())), + } +} + #[derive(Deserialize)] #[serde(rename_all = "camelCase")] pub struct DecryptArg { |