use elliptic_curve::Curve;
use elliptic_curve::array::typenum::Unsigned;
use js::context::JSContext;
use p256::NistP256;
use p256::ecdh::diffie_hellman as p256_diffie_hellman;
use p384::NistP384;
use p384::ecdh::diffie_hellman as p384_diffie_hellman;
use p521::NistP521;
use p521::ecdh::diffie_hellman as p521_diffie_hellman;
use crate::dom::bindings::codegen::Bindings::CryptoKeyBinding::{
CryptoKeyMethods, CryptoKeyPair, KeyType, KeyUsage,
};
use crate::dom::bindings::codegen::Bindings::SubtleCryptoBinding::KeyFormat;
use crate::dom::bindings::error::Error;
use crate::dom::bindings::root::DomRoot;
use crate::dom::cryptokey::{CryptoKey, Handle};
use crate::dom::globalscope::GlobalScope;
use crate::dom::subtlecrypto::ec_common::EcAlgorithm;
use crate::dom::subtlecrypto::{
ExportedKey, KeyAlgorithmAndDerivatives, NAMED_CURVE_P256, NAMED_CURVE_P384, NAMED_CURVE_P521,
SubtleEcKeyGenParams, SubtleEcKeyImportParams, SubtleEcdhKeyDeriveParams, ec_common,
};
pub(crate) fn generate_key(
cx: &mut JSContext,
global: &GlobalScope,
normalized_algorithm: &SubtleEcKeyGenParams,
extractable: bool,
usages: Vec<KeyUsage>,
) -> Result<CryptoKeyPair, Error> {
ec_common::generate_key(
EcAlgorithm::Ecdh,
cx,
global,
normalized_algorithm,
extractable,
usages,
)
}
pub(crate) fn derive_bits(
normalized_algorithm: &SubtleEcdhKeyDeriveParams,
key: &CryptoKey,
length: Option<u32>,
) -> Result<Vec<u8>, Error> {
if key.Type() != KeyType::Private {
return Err(Error::InvalidAccess(Some(
"[[type]] internal slot of key is not \"private\"".to_string(),
)));
}
let public_key = normalized_algorithm.public.root();
if public_key.Type() != KeyType::Public {
return Err(Error::InvalidAccess(Some(
"[[type]] internal slot of key is not \"public\"".to_string(),
)));
}
if public_key.algorithm().name() != key.algorithm().name() {
return Err(Error::InvalidAccess(Some(
"public key [[algorithm]] internal slot name does not match that of private key"
.to_string(),
)));
}
let (
KeyAlgorithmAndDerivatives::EcKeyAlgorithm(public_key_algorithm),
KeyAlgorithmAndDerivatives::EcKeyAlgorithm(key_algorithm),
) = (public_key.algorithm(), key.algorithm())
else {
return Err(Error::Operation(Some("Public or private key's [[algorithm]] internal slot is not an elliptic curve algorithm".to_string())));
};
if public_key_algorithm.named_curve != key_algorithm.named_curve {
return Err(Error::InvalidAccess(Some(
"Public and private keys' [[algorithm]] internal slots namedCurves do not match"
.to_string(),
)));
}
let secret = match key_algorithm.named_curve.as_str() {
NAMED_CURVE_P256 => {
let Handle::P256PrivateKey(private_key) = key.handle() else {
return Err(Error::Operation(Some(
"Private key is not a P-256 private key".to_string(),
)));
};
let Handle::P256PublicKey(public_key) = public_key.handle() else {
return Err(Error::Operation(Some(
"Public key is not a P-256 public key".to_string(),
)));
};
p256_diffie_hellman(private_key.to_nonzero_scalar(), public_key.as_affine())
.raw_secret_bytes()
.to_vec()
},
NAMED_CURVE_P384 => {
let Handle::P384PrivateKey(private_key) = key.handle() else {
return Err(Error::Operation(Some(
"Private key is not a P-384 private key".to_string(),
)));
};
let Handle::P384PublicKey(public_key) = public_key.handle() else {
return Err(Error::Operation(Some(
"Public key is not a P384 public key".to_string(),
)));
};
p384_diffie_hellman(private_key.to_nonzero_scalar(), public_key.as_affine())
.raw_secret_bytes()
.to_vec()
},
NAMED_CURVE_P521 => {
let Handle::P521PrivateKey(private_key) = key.handle() else {
return Err(Error::Operation(Some(
"Private key is not a P-521 private key".to_string(),
)));
};
let Handle::P521PublicKey(public_key) = public_key.handle() else {
return Err(Error::Operation(Some(
"Public key is not a P-521 public key".to_string(),
)));
};
p521_diffie_hellman(private_key.to_nonzero_scalar(), public_key.as_affine())
.raw_secret_bytes()
.to_vec()
},
_ => {
return Err(Error::NotSupported(Some(format!(
"Unsupported namedCurve: {}",
key_algorithm.named_curve
))));
},
};
match length {
None => Ok(secret),
Some(length) => {
if secret.len() * 8 < length as usize {
Err(Error::Operation(Some(
"Derived secret is too short".to_string(),
)))
} else {
let mut secret = secret[..length.div_ceil(8) as usize].to_vec();
if length % 8 != 0 {
let mask = u8::MAX << (8 - length % 8);
if let Some(last_byte) = secret.last_mut() {
*last_byte &= mask;
}
}
Ok(secret)
}
},
}
}
pub(crate) fn import_key(
cx: &mut JSContext,
global: &GlobalScope,
normalized_algorithm: &SubtleEcKeyImportParams,
format: KeyFormat,
key_data: &[u8],
extractable: bool,
usages: Vec<KeyUsage>,
) -> Result<DomRoot<CryptoKey>, Error> {
ec_common::import_key(
EcAlgorithm::Ecdh,
cx,
global,
normalized_algorithm,
format,
key_data,
extractable,
usages,
)
}
pub(crate) fn export_key(format: KeyFormat, key: &CryptoKey) -> Result<ExportedKey, Error> {
ec_common::export_key(format, key)
}
pub(crate) fn get_public_key(
cx: &mut JSContext,
global: &GlobalScope,
key: &CryptoKey,
algorithm: &KeyAlgorithmAndDerivatives,
usages: Vec<KeyUsage>,
) -> Result<DomRoot<CryptoKey>, Error> {
ec_common::get_public_key(cx, global, key, algorithm, usages)
}
pub(crate) fn secret_length(
normalized_algorithm: &SubtleEcdhKeyDeriveParams,
) -> Result<u32, Error> {
let public_key = normalized_algorithm.public.root();
let KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm) = public_key.algorithm() else {
return Err(Error::Operation(Some(
"The key is not an elliptic curve algorithm key".to_string(),
)));
};
let secret_length_in_bits = match algorithm.named_curve.as_str() {
NAMED_CURVE_P256 => <NistP256 as Curve>::FieldBytesSize::to_u32(),
NAMED_CURVE_P384 => <NistP384 as Curve>::FieldBytesSize::to_u32(),
NAMED_CURVE_P521 => <NistP521 as Curve>::FieldBytesSize::to_u32(),
named_curve => {
return Err(Error::NotSupported(Some(format!(
"Unsupported namedCurve: {}",
named_curve
))));
},
};
Ok(secret_length_in_bits)
}