mirror of
https://github.com/denoland/deno.git
synced 2024-12-27 01:29:14 -05:00
bfd5f1598c
Part 1 of a potential 3 part series. Ref #13449 The current implementation passes key material back and forth RustCrypto group of crates and ring. ring does not implement p521 yet. This PR adds support for P521 named curve in `generateKey` and `importKey` where we use RustCrypto. Other parts should be moved over to the RustGroup group of crates for consistency.
834 lines
24 KiB
Rust
834 lines
24 KiB
Rust
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
|
|
|
use base64::Engine;
|
|
use deno_core::error::AnyError;
|
|
use deno_core::op2;
|
|
use deno_core::JsBuffer;
|
|
use deno_core::ToJsBuffer;
|
|
use elliptic_curve::pkcs8::PrivateKeyInfo;
|
|
use p256::pkcs8::EncodePrivateKey;
|
|
use ring::signature::EcdsaKeyPair;
|
|
use rsa::pkcs1::UintRef;
|
|
use rsa::pkcs8::der::Encode;
|
|
use serde::Deserialize;
|
|
use serde::Serialize;
|
|
use spki::der::Decode;
|
|
|
|
use crate::key::CryptoNamedCurve;
|
|
use crate::shared::*;
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub enum KeyData {
|
|
Spki(JsBuffer),
|
|
Pkcs8(JsBuffer),
|
|
Raw(JsBuffer),
|
|
JwkSecret {
|
|
k: String,
|
|
},
|
|
JwkPublicRsa {
|
|
n: String,
|
|
e: String,
|
|
},
|
|
JwkPrivateRsa {
|
|
n: String,
|
|
e: String,
|
|
d: String,
|
|
p: String,
|
|
q: String,
|
|
dp: String,
|
|
dq: String,
|
|
qi: String,
|
|
},
|
|
JwkPublicEc {
|
|
x: String,
|
|
y: String,
|
|
},
|
|
JwkPrivateEc {
|
|
x: String,
|
|
y: String,
|
|
d: String,
|
|
},
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(rename_all = "camelCase", tag = "algorithm")]
|
|
pub enum ImportKeyOptions {
|
|
#[serde(rename = "RSASSA-PKCS1-v1_5")]
|
|
RsassaPkcs1v15 {},
|
|
#[serde(rename = "RSA-PSS")]
|
|
RsaPss {},
|
|
#[serde(rename = "RSA-OAEP")]
|
|
RsaOaep {},
|
|
#[serde(rename = "ECDSA", rename_all = "camelCase")]
|
|
Ecdsa { named_curve: EcNamedCurve },
|
|
#[serde(rename = "ECDH", rename_all = "camelCase")]
|
|
Ecdh { named_curve: EcNamedCurve },
|
|
#[serde(rename = "AES", rename_all = "camelCase")]
|
|
Aes {},
|
|
#[serde(rename = "HMAC", rename_all = "camelCase")]
|
|
Hmac {},
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
#[serde(untagged)]
|
|
pub enum ImportKeyResult {
|
|
#[serde(rename_all = "camelCase")]
|
|
Rsa {
|
|
raw_data: RustRawKeyData,
|
|
modulus_length: usize,
|
|
public_exponent: ToJsBuffer,
|
|
},
|
|
#[serde(rename_all = "camelCase")]
|
|
Ec { raw_data: RustRawKeyData },
|
|
#[serde(rename_all = "camelCase")]
|
|
#[allow(dead_code)]
|
|
Aes { raw_data: RustRawKeyData },
|
|
#[serde(rename_all = "camelCase")]
|
|
Hmac { raw_data: RustRawKeyData },
|
|
}
|
|
|
|
#[op2]
|
|
#[serde]
|
|
pub fn op_crypto_import_key(
|
|
#[serde] opts: ImportKeyOptions,
|
|
#[serde] key_data: KeyData,
|
|
) -> Result<ImportKeyResult, AnyError> {
|
|
match opts {
|
|
ImportKeyOptions::RsassaPkcs1v15 {} => import_key_rsassa(key_data),
|
|
ImportKeyOptions::RsaPss {} => import_key_rsapss(key_data),
|
|
ImportKeyOptions::RsaOaep {} => import_key_rsaoaep(key_data),
|
|
ImportKeyOptions::Ecdsa { named_curve }
|
|
| ImportKeyOptions::Ecdh { named_curve } => {
|
|
import_key_ec(key_data, named_curve)
|
|
}
|
|
ImportKeyOptions::Aes {} => import_key_aes(key_data),
|
|
ImportKeyOptions::Hmac {} => import_key_hmac(key_data),
|
|
}
|
|
}
|
|
|
|
const BASE64_URL_SAFE_FORGIVING:
|
|
base64::engine::general_purpose::GeneralPurpose =
|
|
base64::engine::general_purpose::GeneralPurpose::new(
|
|
&base64::alphabet::URL_SAFE,
|
|
base64::engine::general_purpose::GeneralPurposeConfig::new()
|
|
.with_decode_allow_trailing_bits(true)
|
|
.with_decode_padding_mode(base64::engine::DecodePaddingMode::Indifferent),
|
|
);
|
|
|
|
macro_rules! jwt_b64_int_or_err {
|
|
($name:ident, $b64:expr, $err:expr) => {
|
|
let bytes = BASE64_URL_SAFE_FORGIVING
|
|
.decode($b64)
|
|
.map_err(|_| data_error($err))?;
|
|
let $name = UintRef::new(&bytes).map_err(|_| data_error($err))?;
|
|
};
|
|
}
|
|
|
|
fn import_key_rsa_jwk(
|
|
key_data: KeyData,
|
|
) -> Result<ImportKeyResult, deno_core::anyhow::Error> {
|
|
match key_data {
|
|
KeyData::JwkPublicRsa { n, e } => {
|
|
jwt_b64_int_or_err!(modulus, &n, "invalid modulus");
|
|
jwt_b64_int_or_err!(public_exponent, &e, "invalid public exponent");
|
|
|
|
let public_key = rsa::pkcs1::RsaPublicKey {
|
|
modulus,
|
|
public_exponent,
|
|
};
|
|
|
|
let mut data = Vec::new();
|
|
public_key
|
|
.encode_to_vec(&mut data)
|
|
.map_err(|_| data_error("invalid rsa public key"))?;
|
|
|
|
let public_exponent =
|
|
public_key.public_exponent.as_bytes().to_vec().into();
|
|
let modulus_length = public_key.modulus.as_bytes().len() * 8;
|
|
|
|
Ok(ImportKeyResult::Rsa {
|
|
raw_data: RustRawKeyData::Public(data.into()),
|
|
modulus_length,
|
|
public_exponent,
|
|
})
|
|
}
|
|
KeyData::JwkPrivateRsa {
|
|
n,
|
|
e,
|
|
d,
|
|
p,
|
|
q,
|
|
dp,
|
|
dq,
|
|
qi,
|
|
} => {
|
|
jwt_b64_int_or_err!(modulus, &n, "invalid modulus");
|
|
jwt_b64_int_or_err!(public_exponent, &e, "invalid public exponent");
|
|
jwt_b64_int_or_err!(private_exponent, &d, "invalid private exponent");
|
|
jwt_b64_int_or_err!(prime1, &p, "invalid first prime factor");
|
|
jwt_b64_int_or_err!(prime2, &q, "invalid second prime factor");
|
|
jwt_b64_int_or_err!(exponent1, &dp, "invalid first CRT exponent");
|
|
jwt_b64_int_or_err!(exponent2, &dq, "invalid second CRT exponent");
|
|
jwt_b64_int_or_err!(coefficient, &qi, "invalid CRT coefficient");
|
|
|
|
let private_key = rsa::pkcs1::RsaPrivateKey {
|
|
modulus,
|
|
public_exponent,
|
|
private_exponent,
|
|
prime1,
|
|
prime2,
|
|
exponent1,
|
|
exponent2,
|
|
coefficient,
|
|
other_prime_infos: None,
|
|
};
|
|
|
|
let mut data = Vec::new();
|
|
private_key
|
|
.encode_to_vec(&mut data)
|
|
.map_err(|_| data_error("invalid rsa private key"))?;
|
|
|
|
let public_exponent =
|
|
private_key.public_exponent.as_bytes().to_vec().into();
|
|
let modulus_length = private_key.modulus.as_bytes().len() * 8;
|
|
|
|
Ok(ImportKeyResult::Rsa {
|
|
raw_data: RustRawKeyData::Private(data.into()),
|
|
modulus_length,
|
|
public_exponent,
|
|
})
|
|
}
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
fn import_key_rsassa(
|
|
key_data: KeyData,
|
|
) -> Result<ImportKeyResult, deno_core::anyhow::Error> {
|
|
match key_data {
|
|
KeyData::Spki(data) => {
|
|
// 2-3.
|
|
let pk_info = spki::SubjectPublicKeyInfoRef::try_from(&*data)
|
|
.map_err(|e| data_error(e.to_string()))?;
|
|
|
|
// 4-5.
|
|
let alg = pk_info.algorithm.oid;
|
|
|
|
// 6-7. (skipped, only support rsaEncryption for interoperability)
|
|
if alg != RSA_ENCRYPTION_OID {
|
|
return Err(data_error("unsupported algorithm"));
|
|
}
|
|
|
|
// 8-9.
|
|
let public_key = rsa::pkcs1::RsaPublicKey::from_der(
|
|
pk_info.subject_public_key.raw_bytes(),
|
|
)
|
|
.map_err(|e| data_error(e.to_string()))?;
|
|
|
|
let bytes_consumed = public_key
|
|
.encoded_len()
|
|
.map_err(|e| data_error(e.to_string()))?;
|
|
|
|
if bytes_consumed
|
|
!= rsa::pkcs1::der::Length::new(
|
|
pk_info.subject_public_key.raw_bytes().len() as u16,
|
|
)
|
|
{
|
|
return Err(data_error("public key is invalid (too long)"));
|
|
}
|
|
|
|
let data = pk_info.subject_public_key.raw_bytes().to_vec().into();
|
|
let public_exponent =
|
|
public_key.public_exponent.as_bytes().to_vec().into();
|
|
let modulus_length = public_key.modulus.as_bytes().len() * 8;
|
|
|
|
Ok(ImportKeyResult::Rsa {
|
|
raw_data: RustRawKeyData::Public(data),
|
|
modulus_length,
|
|
public_exponent,
|
|
})
|
|
}
|
|
KeyData::Pkcs8(data) => {
|
|
// 2-3.
|
|
let pk_info = PrivateKeyInfo::from_der(&data)
|
|
.map_err(|e| data_error(e.to_string()))?;
|
|
|
|
// 4-5.
|
|
let alg = pk_info.algorithm.oid;
|
|
|
|
// 6-7. (skipped, only support rsaEncryption for interoperability)
|
|
if alg != RSA_ENCRYPTION_OID {
|
|
return Err(data_error("unsupported algorithm"));
|
|
}
|
|
|
|
// 8-9.
|
|
let private_key =
|
|
rsa::pkcs1::RsaPrivateKey::from_der(pk_info.private_key)
|
|
.map_err(|e| data_error(e.to_string()))?;
|
|
|
|
let bytes_consumed = private_key
|
|
.encoded_len()
|
|
.map_err(|e| data_error(e.to_string()))?;
|
|
|
|
if bytes_consumed
|
|
!= rsa::pkcs1::der::Length::new(pk_info.private_key.len() as u16)
|
|
{
|
|
return Err(data_error("private key is invalid (too long)"));
|
|
}
|
|
|
|
let data = pk_info.private_key.to_vec().into();
|
|
let public_exponent =
|
|
private_key.public_exponent.as_bytes().to_vec().into();
|
|
let modulus_length = private_key.modulus.as_bytes().len() * 8;
|
|
|
|
Ok(ImportKeyResult::Rsa {
|
|
raw_data: RustRawKeyData::Private(data),
|
|
modulus_length,
|
|
public_exponent,
|
|
})
|
|
}
|
|
KeyData::JwkPublicRsa { .. } | KeyData::JwkPrivateRsa { .. } => {
|
|
import_key_rsa_jwk(key_data)
|
|
}
|
|
_ => Err(unsupported_format()),
|
|
}
|
|
}
|
|
|
|
fn import_key_rsapss(
|
|
key_data: KeyData,
|
|
) -> Result<ImportKeyResult, deno_core::anyhow::Error> {
|
|
match key_data {
|
|
KeyData::Spki(data) => {
|
|
// 2-3.
|
|
let pk_info = spki::SubjectPublicKeyInfoRef::try_from(&*data)
|
|
.map_err(|e| data_error(e.to_string()))?;
|
|
|
|
// 4-5.
|
|
let alg = pk_info.algorithm.oid;
|
|
|
|
// 6-7. (skipped, only support rsaEncryption for interoperability)
|
|
if alg != RSA_ENCRYPTION_OID {
|
|
return Err(data_error("unsupported algorithm"));
|
|
}
|
|
|
|
// 8-9.
|
|
let public_key = rsa::pkcs1::RsaPublicKey::from_der(
|
|
pk_info.subject_public_key.raw_bytes(),
|
|
)
|
|
.map_err(|e| data_error(e.to_string()))?;
|
|
|
|
let bytes_consumed = public_key
|
|
.encoded_len()
|
|
.map_err(|e| data_error(e.to_string()))?;
|
|
|
|
if bytes_consumed
|
|
!= rsa::pkcs1::der::Length::new(
|
|
pk_info.subject_public_key.raw_bytes().len() as u16,
|
|
)
|
|
{
|
|
return Err(data_error("public key is invalid (too long)"));
|
|
}
|
|
|
|
let data = pk_info.subject_public_key.raw_bytes().to_vec().into();
|
|
let public_exponent =
|
|
public_key.public_exponent.as_bytes().to_vec().into();
|
|
let modulus_length = public_key.modulus.as_bytes().len() * 8;
|
|
|
|
Ok(ImportKeyResult::Rsa {
|
|
raw_data: RustRawKeyData::Public(data),
|
|
modulus_length,
|
|
public_exponent,
|
|
})
|
|
}
|
|
KeyData::Pkcs8(data) => {
|
|
// 2-3.
|
|
let pk_info = PrivateKeyInfo::from_der(&data)
|
|
.map_err(|e| data_error(e.to_string()))?;
|
|
|
|
// 4-5.
|
|
let alg = pk_info.algorithm.oid;
|
|
|
|
// 6-7. (skipped, only support rsaEncryption for interoperability)
|
|
if alg != RSA_ENCRYPTION_OID {
|
|
return Err(data_error("unsupported algorithm"));
|
|
}
|
|
|
|
// 8-9.
|
|
let private_key =
|
|
rsa::pkcs1::RsaPrivateKey::from_der(pk_info.private_key)
|
|
.map_err(|e| data_error(e.to_string()))?;
|
|
|
|
let bytes_consumed = private_key
|
|
.encoded_len()
|
|
.map_err(|e| data_error(e.to_string()))?;
|
|
|
|
if bytes_consumed
|
|
!= rsa::pkcs1::der::Length::new(pk_info.private_key.len() as u16)
|
|
{
|
|
return Err(data_error("private key is invalid (too long)"));
|
|
}
|
|
|
|
let data = pk_info.private_key.to_vec().into();
|
|
let public_exponent =
|
|
private_key.public_exponent.as_bytes().to_vec().into();
|
|
let modulus_length = private_key.modulus.as_bytes().len() * 8;
|
|
|
|
Ok(ImportKeyResult::Rsa {
|
|
raw_data: RustRawKeyData::Private(data),
|
|
modulus_length,
|
|
public_exponent,
|
|
})
|
|
}
|
|
KeyData::JwkPublicRsa { .. } | KeyData::JwkPrivateRsa { .. } => {
|
|
import_key_rsa_jwk(key_data)
|
|
}
|
|
_ => Err(unsupported_format()),
|
|
}
|
|
}
|
|
|
|
fn import_key_rsaoaep(
|
|
key_data: KeyData,
|
|
) -> Result<ImportKeyResult, deno_core::anyhow::Error> {
|
|
match key_data {
|
|
KeyData::Spki(data) => {
|
|
// 2-3.
|
|
let pk_info = spki::SubjectPublicKeyInfoRef::try_from(&*data)
|
|
.map_err(|e| data_error(e.to_string()))?;
|
|
|
|
// 4-5.
|
|
let alg = pk_info.algorithm.oid;
|
|
|
|
// 6-7. (skipped, only support rsaEncryption for interoperability)
|
|
if alg != RSA_ENCRYPTION_OID {
|
|
return Err(data_error("unsupported algorithm"));
|
|
}
|
|
|
|
// 8-9.
|
|
let public_key = rsa::pkcs1::RsaPublicKey::from_der(
|
|
pk_info.subject_public_key.raw_bytes(),
|
|
)
|
|
.map_err(|e| data_error(e.to_string()))?;
|
|
|
|
let bytes_consumed = public_key
|
|
.encoded_len()
|
|
.map_err(|e| data_error(e.to_string()))?;
|
|
|
|
if bytes_consumed
|
|
!= rsa::pkcs1::der::Length::new(
|
|
pk_info.subject_public_key.raw_bytes().len() as u16,
|
|
)
|
|
{
|
|
return Err(data_error("public key is invalid (too long)"));
|
|
}
|
|
|
|
let data = pk_info.subject_public_key.raw_bytes().to_vec().into();
|
|
let public_exponent =
|
|
public_key.public_exponent.as_bytes().to_vec().into();
|
|
let modulus_length = public_key.modulus.as_bytes().len() * 8;
|
|
|
|
Ok(ImportKeyResult::Rsa {
|
|
raw_data: RustRawKeyData::Public(data),
|
|
modulus_length,
|
|
public_exponent,
|
|
})
|
|
}
|
|
KeyData::Pkcs8(data) => {
|
|
// 2-3.
|
|
let pk_info = PrivateKeyInfo::from_der(&data)
|
|
.map_err(|e| data_error(e.to_string()))?;
|
|
|
|
// 4-5.
|
|
let alg = pk_info.algorithm.oid;
|
|
|
|
// 6-7. (skipped, only support rsaEncryption for interoperability)
|
|
if alg != RSA_ENCRYPTION_OID {
|
|
return Err(data_error("unsupported algorithm"));
|
|
}
|
|
|
|
// 8-9.
|
|
let private_key =
|
|
rsa::pkcs1::RsaPrivateKey::from_der(pk_info.private_key)
|
|
.map_err(|e| data_error(e.to_string()))?;
|
|
|
|
let bytes_consumed = private_key
|
|
.encoded_len()
|
|
.map_err(|e| data_error(e.to_string()))?;
|
|
|
|
if bytes_consumed
|
|
!= rsa::pkcs1::der::Length::new(pk_info.private_key.len() as u16)
|
|
{
|
|
return Err(data_error("private key is invalid (too long)"));
|
|
}
|
|
|
|
let data = pk_info.private_key.to_vec().into();
|
|
let public_exponent =
|
|
private_key.public_exponent.as_bytes().to_vec().into();
|
|
let modulus_length = private_key.modulus.as_bytes().len() * 8;
|
|
|
|
Ok(ImportKeyResult::Rsa {
|
|
raw_data: RustRawKeyData::Private(data),
|
|
modulus_length,
|
|
public_exponent,
|
|
})
|
|
}
|
|
KeyData::JwkPublicRsa { .. } | KeyData::JwkPrivateRsa { .. } => {
|
|
import_key_rsa_jwk(key_data)
|
|
}
|
|
_ => Err(unsupported_format()),
|
|
}
|
|
}
|
|
|
|
fn decode_b64url_to_field_bytes<C: elliptic_curve::Curve>(
|
|
b64: &str,
|
|
) -> Result<elliptic_curve::FieldBytes<C>, deno_core::anyhow::Error> {
|
|
jwt_b64_int_or_err!(val, b64, "invalid b64 coordinate");
|
|
|
|
let mut bytes = elliptic_curve::FieldBytes::<C>::default();
|
|
let original_bytes = val.as_bytes();
|
|
let mut new_bytes: Vec<u8> = vec![];
|
|
if original_bytes.len() < bytes.len() {
|
|
new_bytes = vec![0; bytes.len() - original_bytes.len()];
|
|
}
|
|
new_bytes.extend_from_slice(original_bytes);
|
|
|
|
let val = new_bytes.as_slice();
|
|
|
|
if val.len() != bytes.len() {
|
|
return Err(data_error("invalid b64 coordinate"));
|
|
}
|
|
bytes.copy_from_slice(val);
|
|
|
|
Ok(bytes)
|
|
}
|
|
|
|
fn import_key_ec_jwk_to_point(
|
|
x: String,
|
|
y: String,
|
|
named_curve: EcNamedCurve,
|
|
) -> Result<Vec<u8>, deno_core::anyhow::Error> {
|
|
let point_bytes = match named_curve {
|
|
EcNamedCurve::P256 => {
|
|
let x = decode_b64url_to_field_bytes::<p256::NistP256>(&x)?;
|
|
let y = decode_b64url_to_field_bytes::<p256::NistP256>(&y)?;
|
|
|
|
p256::EncodedPoint::from_affine_coordinates(&x, &y, false).to_bytes()
|
|
}
|
|
EcNamedCurve::P384 => {
|
|
let x = decode_b64url_to_field_bytes::<p384::NistP384>(&x)?;
|
|
let y = decode_b64url_to_field_bytes::<p384::NistP384>(&y)?;
|
|
|
|
p384::EncodedPoint::from_affine_coordinates(&x, &y, false).to_bytes()
|
|
}
|
|
EcNamedCurve::P521 => {
|
|
let x = decode_b64url_to_field_bytes::<p521::NistP521>(&x)?;
|
|
let y = decode_b64url_to_field_bytes::<p521::NistP521>(&y)?;
|
|
|
|
p521::EncodedPoint::from_affine_coordinates(&x, &y, false).to_bytes()
|
|
}
|
|
};
|
|
|
|
Ok(point_bytes.to_vec())
|
|
}
|
|
|
|
fn import_key_ec_jwk(
|
|
key_data: KeyData,
|
|
named_curve: EcNamedCurve,
|
|
) -> Result<ImportKeyResult, deno_core::anyhow::Error> {
|
|
match key_data {
|
|
KeyData::JwkPublicEc { x, y } => {
|
|
let point_bytes = import_key_ec_jwk_to_point(x, y, named_curve)?;
|
|
|
|
Ok(ImportKeyResult::Ec {
|
|
raw_data: RustRawKeyData::Public(point_bytes.into()),
|
|
})
|
|
}
|
|
KeyData::JwkPrivateEc { d, x, y } => {
|
|
jwt_b64_int_or_err!(private_d, &d, "invalid JWK private key");
|
|
let point_bytes = import_key_ec_jwk_to_point(x, y, named_curve)?;
|
|
let pkcs8_der = match named_curve {
|
|
EcNamedCurve::P256 => {
|
|
let d = decode_b64url_to_field_bytes::<p256::NistP256>(&d)?;
|
|
let pk = p256::SecretKey::from_bytes(&d)?;
|
|
|
|
pk.to_pkcs8_der()
|
|
.map_err(|_| data_error("invalid JWK private key"))?
|
|
}
|
|
EcNamedCurve::P384 => {
|
|
let d = decode_b64url_to_field_bytes::<p384::NistP384>(&d)?;
|
|
let pk = p384::SecretKey::from_bytes(&d)?;
|
|
|
|
pk.to_pkcs8_der()
|
|
.map_err(|_| data_error("invalid JWK private key"))?
|
|
}
|
|
EcNamedCurve::P521 => {
|
|
return Err(data_error("Unsupported named curve"))
|
|
}
|
|
};
|
|
|
|
// Import using ring, to validate key
|
|
let key_alg = match named_curve {
|
|
EcNamedCurve::P256 => CryptoNamedCurve::P256.into(),
|
|
EcNamedCurve::P384 => CryptoNamedCurve::P256.into(),
|
|
EcNamedCurve::P521 => {
|
|
return Err(data_error("Unsupported named curve"))
|
|
}
|
|
};
|
|
|
|
let rng = ring::rand::SystemRandom::new();
|
|
let _key_pair = EcdsaKeyPair::from_private_key_and_public_key(
|
|
key_alg,
|
|
private_d.as_bytes(),
|
|
point_bytes.as_ref(),
|
|
&rng,
|
|
);
|
|
|
|
Ok(ImportKeyResult::Ec {
|
|
raw_data: RustRawKeyData::Private(pkcs8_der.as_bytes().to_vec().into()),
|
|
})
|
|
}
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
pub struct ECParametersSpki {
|
|
pub named_curve_alg: spki::der::asn1::ObjectIdentifier,
|
|
}
|
|
|
|
impl<'a> TryFrom<spki::der::asn1::AnyRef<'a>> for ECParametersSpki {
|
|
type Error = spki::der::Error;
|
|
|
|
fn try_from(
|
|
any: spki::der::asn1::AnyRef<'a>,
|
|
) -> spki::der::Result<ECParametersSpki> {
|
|
let x = any.try_into()?;
|
|
|
|
Ok(Self { named_curve_alg: x })
|
|
}
|
|
}
|
|
|
|
fn import_key_ec(
|
|
key_data: KeyData,
|
|
named_curve: EcNamedCurve,
|
|
) -> Result<ImportKeyResult, AnyError> {
|
|
match key_data {
|
|
KeyData::Raw(data) => {
|
|
// The point is parsed and validated, ultimately the original data is
|
|
// returned though.
|
|
match named_curve {
|
|
EcNamedCurve::P256 => {
|
|
// 1-2.
|
|
let point = p256::EncodedPoint::from_bytes(&data)
|
|
.map_err(|_| data_error("invalid P-256 elliptic curve point"))?;
|
|
// 3.
|
|
if point.is_identity() {
|
|
return Err(data_error("invalid P-256 elliptic curve point"));
|
|
}
|
|
}
|
|
EcNamedCurve::P384 => {
|
|
// 1-2.
|
|
let point = p384::EncodedPoint::from_bytes(&data)
|
|
.map_err(|_| data_error("invalid P-384 elliptic curve point"))?;
|
|
// 3.
|
|
if point.is_identity() {
|
|
return Err(data_error("invalid P-384 elliptic curve point"));
|
|
}
|
|
}
|
|
EcNamedCurve::P521 => {
|
|
// 1-2.
|
|
let point = p521::EncodedPoint::from_bytes(&data)
|
|
.map_err(|_| data_error("invalid P-521 elliptic curve point"))?;
|
|
// 3.
|
|
if point.is_identity() {
|
|
return Err(data_error("invalid P-521 elliptic curve point"));
|
|
}
|
|
}
|
|
};
|
|
Ok(ImportKeyResult::Ec {
|
|
raw_data: RustRawKeyData::Public(data.to_vec().into()),
|
|
})
|
|
}
|
|
KeyData::Pkcs8(data) => {
|
|
// 2-7
|
|
// Deserialize PKCS8 - validate structure, extracts named_curve
|
|
let named_curve_alg = match named_curve {
|
|
EcNamedCurve::P256 | EcNamedCurve::P384 => {
|
|
let pk = PrivateKeyInfo::from_der(data.as_ref())
|
|
.map_err(|_| data_error("expected valid PKCS#8 data"))?;
|
|
pk.algorithm
|
|
.parameters
|
|
.ok_or_else(|| data_error("malformed parameters"))?
|
|
.try_into()
|
|
.unwrap()
|
|
}
|
|
EcNamedCurve::P521 => {
|
|
return Err(data_error("Unsupported named curve"))
|
|
}
|
|
};
|
|
|
|
// 8-9.
|
|
let pk_named_curve = match named_curve_alg {
|
|
// id-secp256r1
|
|
ID_SECP256R1_OID => Some(EcNamedCurve::P256),
|
|
// id-secp384r1
|
|
ID_SECP384R1_OID => Some(EcNamedCurve::P384),
|
|
// id-secp521r1
|
|
ID_SECP521R1_OID => Some(EcNamedCurve::P521),
|
|
_ => None,
|
|
};
|
|
|
|
// 10.
|
|
if let Some(pk_named_curve) = pk_named_curve {
|
|
let signing_alg = match pk_named_curve {
|
|
EcNamedCurve::P256 => CryptoNamedCurve::P256.into(),
|
|
EcNamedCurve::P384 => CryptoNamedCurve::P384.into(),
|
|
EcNamedCurve::P521 => {
|
|
return Err(data_error("Unsupported named curve"))
|
|
}
|
|
};
|
|
|
|
let rng = ring::rand::SystemRandom::new();
|
|
// deserialize pkcs8 using ring crate, to VALIDATE public key
|
|
let _private_key = EcdsaKeyPair::from_pkcs8(signing_alg, &data, &rng)?;
|
|
|
|
// 11.
|
|
if named_curve != pk_named_curve {
|
|
return Err(data_error("curve mismatch"));
|
|
}
|
|
} else {
|
|
return Err(data_error("Unsupported named curve"));
|
|
}
|
|
|
|
Ok(ImportKeyResult::Ec {
|
|
raw_data: RustRawKeyData::Private(data.to_vec().into()),
|
|
})
|
|
}
|
|
KeyData::Spki(data) => {
|
|
// 2-3.
|
|
let pk_info = spki::SubjectPublicKeyInfoRef::try_from(&*data)
|
|
.map_err(|e| data_error(e.to_string()))?;
|
|
|
|
// 4.
|
|
let alg = pk_info.algorithm.oid;
|
|
// id-ecPublicKey
|
|
if alg != elliptic_curve::ALGORITHM_OID {
|
|
return Err(data_error("unsupported algorithm"));
|
|
}
|
|
|
|
// 5-7.
|
|
let params = ECParametersSpki::try_from(
|
|
pk_info
|
|
.algorithm
|
|
.parameters
|
|
.ok_or_else(|| data_error("malformed parameters"))?,
|
|
)
|
|
.map_err(|_| data_error("malformed parameters"))?;
|
|
|
|
// 8-9.
|
|
let named_curve_alg = params.named_curve_alg;
|
|
let pk_named_curve = match named_curve_alg {
|
|
// id-secp256r1
|
|
ID_SECP256R1_OID => Some(EcNamedCurve::P256),
|
|
// id-secp384r1
|
|
ID_SECP384R1_OID => Some(EcNamedCurve::P384),
|
|
// id-secp521r1
|
|
ID_SECP521R1_OID => Some(EcNamedCurve::P521),
|
|
_ => None,
|
|
};
|
|
|
|
// 10.
|
|
let encoded_key;
|
|
|
|
if let Some(pk_named_curve) = pk_named_curve {
|
|
let pk = pk_info.subject_public_key;
|
|
|
|
encoded_key = pk.raw_bytes().to_vec();
|
|
|
|
let bytes_consumed = match named_curve {
|
|
EcNamedCurve::P256 => {
|
|
let point =
|
|
p256::EncodedPoint::from_bytes(&*encoded_key).map_err(|_| {
|
|
data_error("invalid P-256 elliptic curve SPKI data")
|
|
})?;
|
|
if point.is_identity() {
|
|
return Err(data_error("invalid P-256 elliptic curve point"));
|
|
}
|
|
|
|
point.as_bytes().len()
|
|
}
|
|
EcNamedCurve::P384 => {
|
|
let point =
|
|
p384::EncodedPoint::from_bytes(&*encoded_key).map_err(|_| {
|
|
data_error("invalid P-384 elliptic curve SPKI data")
|
|
})?;
|
|
|
|
if point.is_identity() {
|
|
return Err(data_error("invalid P-384 elliptic curve point"));
|
|
}
|
|
|
|
point.as_bytes().len()
|
|
}
|
|
EcNamedCurve::P521 => {
|
|
let point =
|
|
p521::EncodedPoint::from_bytes(&*encoded_key).map_err(|_| {
|
|
data_error("invalid P-521 elliptic curve SPKI data")
|
|
})?;
|
|
|
|
if point.is_identity() {
|
|
return Err(data_error("invalid P-521 elliptic curve point"));
|
|
}
|
|
|
|
point.as_bytes().len()
|
|
}
|
|
};
|
|
|
|
if bytes_consumed != pk_info.subject_public_key.raw_bytes().len() {
|
|
return Err(data_error("public key is invalid (too long)"));
|
|
}
|
|
|
|
// 11.
|
|
if named_curve != pk_named_curve {
|
|
return Err(data_error("curve mismatch"));
|
|
}
|
|
} else {
|
|
return Err(data_error("Unsupported named curve"));
|
|
}
|
|
|
|
Ok(ImportKeyResult::Ec {
|
|
raw_data: RustRawKeyData::Public(encoded_key.into()),
|
|
})
|
|
}
|
|
KeyData::JwkPublicEc { .. } | KeyData::JwkPrivateEc { .. } => {
|
|
import_key_ec_jwk(key_data, named_curve)
|
|
}
|
|
_ => Err(unsupported_format()),
|
|
}
|
|
}
|
|
|
|
fn import_key_aes(key_data: KeyData) -> Result<ImportKeyResult, AnyError> {
|
|
Ok(match key_data {
|
|
KeyData::JwkSecret { k } => {
|
|
let data = BASE64_URL_SAFE_FORGIVING
|
|
.decode(k)
|
|
.map_err(|_| data_error("invalid key data"))?;
|
|
ImportKeyResult::Hmac {
|
|
raw_data: RustRawKeyData::Secret(data.into()),
|
|
}
|
|
}
|
|
_ => return Err(unsupported_format()),
|
|
})
|
|
}
|
|
|
|
fn import_key_hmac(key_data: KeyData) -> Result<ImportKeyResult, AnyError> {
|
|
Ok(match key_data {
|
|
KeyData::JwkSecret { k } => {
|
|
let data = BASE64_URL_SAFE_FORGIVING
|
|
.decode(k)
|
|
.map_err(|_| data_error("invalid key data"))?;
|
|
ImportKeyResult::Hmac {
|
|
raw_data: RustRawKeyData::Secret(data.into()),
|
|
}
|
|
}
|
|
_ => return Err(unsupported_format()),
|
|
})
|
|
}
|