#![allow(unused_variables)]
use std::time::SystemTime;
use std::convert::TryInto;
use crate::{Error, Result};
use crate::crypto::asymmetric::KeyPair;
use crate::crypto::backend::interface::Asymmetric;
use crate::crypto::mem::Protected;
use crate::crypto::mpi::{self, MPI, ProtectedMPI};
use crate::crypto::SessionKey;
use crate::crypto::{pad, pad_at_least, pad_truncating};
use crate::packet::key::{Key4, SecretParts};
use crate::packet::{key, Key};
use crate::types::PublicKeyAlgorithm;
use crate::types::{Curve, HashAlgorithm};
use num_bigint_dig::{traits::ModInverse, BigInt, BigUint};
use win_crypto_ng as cng;
impl TryFrom<&Protected> for Box<ed25519_dalek::SigningKey> {
type Error = anyhow::Error;
fn try_from(value: &Protected) -> Result<Self> {
if value.len() != ed25519_dalek::SECRET_KEY_LENGTH {
return Err(crate::Error::InvalidArgument(
"Bad Ed25519 secret length".into()).into());
}
Ok(Box::new(ed25519_dalek::SigningKey::from_bytes(
value.as_ref().try_into().map_err(
|e: std::array::TryFromSliceError| {
Error::InvalidKey(e.to_string())
})?)))
}
}
impl Asymmetric for super::Backend {
fn supports_algo(algo: PublicKeyAlgorithm) -> bool {
use PublicKeyAlgorithm::*;
#[allow(deprecated)]
match algo {
X25519 | Ed25519 |
RSAEncryptSign | RSAEncrypt | RSASign | DSA | ECDH | ECDSA | EdDSA
=> true,
X448 | Ed448 |
ElGamalEncrypt | ElGamalEncryptSign | Private(_) | Unknown(_)
=> false,
}
}
fn supports_curve(curve: &Curve) -> bool {
use Curve::*;
match curve {
NistP256 | NistP384 | NistP521 | Ed25519 | Cv25519
=> true,
BrainpoolP256 | BrainpoolP384 | BrainpoolP512 | Unknown(_)
=> false,
}
}
fn x25519_generate_key() -> Result<(Protected, [u8; 32])> {
use cng::asymmetric::{Ecdh, AsymmetricKey, Export};
use cng::asymmetric::ecc::Curve25519;
let pair =
AsymmetricKey::builder(Ecdh(Curve25519)).build()?.export()?;
let mut public = [0u8; 32];
public.copy_from_slice(pair.x());
let mut clamped_secret = pair.d().into();
Self::x25519_clamp_secret(&mut clamped_secret);
Ok((clamped_secret, public))
}
fn x25519_derive_public(secret: &Protected) -> Result<[u8; 32]> {
use cng::asymmetric::{AsymmetricAlgorithm, AsymmetricAlgorithmId, Ecdh,
Private, AsymmetricKey, Export};
use cng::asymmetric::ecc::{Curve25519, NamedCurve};
let provider = AsymmetricAlgorithm::open(
AsymmetricAlgorithmId::Ecdh(NamedCurve::Curve25519)
)?;
let mut clamped_secret = secret.clone();
Self::x25519_clamp_secret(&mut clamped_secret);
let key = AsymmetricKey::<Ecdh<Curve25519>, Private>::import_from_parts(
&provider,
&clamped_secret,
)?;
Ok(<[u8; 32]>::try_from(&key.export()?.x()[..])?)
}
fn x25519_shared_point(secret: &Protected, public: &[u8; 32])
-> Result<Protected> {
use cng::asymmetric::{Ecdh, AsymmetricKey, Public, Private,
AsymmetricAlgorithm, AsymmetricAlgorithmId};
use cng::asymmetric::agreement::secret_agreement;
use cng::asymmetric::ecc::{NamedCurve, Curve25519};
let provider =
AsymmetricAlgorithm::open(
AsymmetricAlgorithmId::Ecdh(NamedCurve::Curve25519))?;
let public =
AsymmetricKey::<Ecdh<Curve25519>, Public>::import_from_parts(
&provider,
public,
)?;
let mut clamped_secret = secret.clone();
Self::x25519_clamp_secret(&mut clamped_secret);
let secret =
AsymmetricKey::<Ecdh<Curve25519>, Private>::import_from_parts(
&provider,
&clamped_secret,
)?;
let shared = secret_agreement(&secret, &public)?;
let mut shared = Protected::from(shared.derive_raw()?);
shared.reverse();
Ok(shared)
}
fn ed25519_generate_key() -> Result<(Protected, [u8; 32])> {
let mut rng = cng::random::RandomNumberGenerator::system_preferred();
let pair = ed25519_dalek::SigningKey::generate(&mut rng);
Ok((pair.to_bytes().into(), pair.verifying_key().to_bytes()))
}
fn ed25519_derive_public(secret: &Protected) -> Result<[u8; 32]> {
use ed25519_dalek::SigningKey;
let secret: Box<SigningKey> = secret.try_into()?;
let public = secret.verifying_key();
Ok(public.to_bytes())
}
fn ed25519_sign(secret: &Protected, _public: &[u8; 32], digest: &[u8])
-> Result<[u8; 64]> {
use ed25519_dalek::{SigningKey, Signer};
let secret: Box<SigningKey> = secret.try_into()?;
Ok(secret.sign(digest).to_bytes().try_into()?)
}
fn ed25519_verify(public: &[u8; 32], digest: &[u8], signature: &[u8; 64])
-> Result<bool> {
use ed25519_dalek::{VerifyingKey, Verifier, Signature};
let public = VerifyingKey::from_bytes(public).map_err(|e| {
Error::InvalidKey(e.to_string())
})?;
let signature = signature.as_ref().try_into().map_err(|e: std::array::TryFromSliceError| {
Error::InvalidArgument(e.to_string())
})?;
let signature = Signature::from_bytes(signature);
Ok(public.verify(digest, &signature).is_ok())
}
fn dsa_generate_key(p_bits: usize)
-> Result<(MPI, MPI, MPI, MPI, ProtectedMPI)>
{
let _ = p_bits;
#[allow(deprecated)]
Err(Error::UnsupportedPublicKeyAlgorithm(
PublicKeyAlgorithm::DSA).into())
}
fn dsa_sign(x: &ProtectedMPI,
p: &MPI, q: &MPI, g: &MPI, y: &MPI,
digest: &[u8])
-> Result<(MPI, MPI)>
{
use cng::key_blob::{DsaKeyPrivateV2Payload, DsaKeyPrivateV2Blob};
use cng::key_blob::{DsaKeyPrivatePayload, DsaKeyPrivateBlob};
use cng::asymmetric::{
AsymmetricAlgorithm,
AsymmetricAlgorithmId,
AsymmetricKey,
Dsa,
DsaPrivateBlob,
Private,
signature::Signer,
};
use cng::helpers::Blob;
let y = y.value_padded(p.value().len())
.map_err(|e| Error::InvalidKey(e.to_string()))?;
if y.len() > 3072 / 8 {
return Err(Error::InvalidOperation(
"DSA keys are supported up to 3072-bits".to_string()).into()
);
}
enum Version { V1, V2 }
let version = if y.len() <= 128 { Version::V1 } else { Version::V2 };
let blob: DsaPrivateBlob = match version {
Version::V1 => {
let mut group = [0; 20];
if let Ok(v) = q.value_padded(group.len()) {
group[..].copy_from_slice(&v);
} else {
return Err(Error::InvalidOperation(
"DSA keys' group parameter exceeds 160 bits"
.to_string()).into());
}
DsaPrivateBlob::V1(Blob::<DsaKeyPrivateBlob>::clone_from_parts(
&winapi::shared::bcrypt::BCRYPT_DSA_KEY_BLOB {
dwMagic: winapi::shared::bcrypt::BCRYPT_DSA_PUBLIC_MAGIC,
cbKey: y.len() as u32,
Count: [0; 4], Seed: [0; 20], q: group,
},
&DsaKeyPrivatePayload {
modulus: p.value(),
generator: g.value(),
public: &y,
priv_exp: x.value(),
},
))
},
Version::V2 => {
let hash = match q.value().len() {
20 => 0,
32 => 1,
64 => 2,
_ => return Err(Error::InvalidOperation(
"CNG accepts DSA q with length of either length of 20, 32 or 64".into())
.into()),
};
let (count, seed) = ([0x0; 4], vec![0x0; q.value().len()]);
let group_size = std::cmp::min(q.value().len(), 32);
let key_size = y.len();
DsaPrivateBlob::V2(Blob::<DsaKeyPrivateV2Blob>::clone_from_parts(
&winapi::shared::bcrypt::BCRYPT_DSA_KEY_BLOB_V2 {
dwMagic: winapi::shared::bcrypt::BCRYPT_DSA_PRIVATE_MAGIC_V2,
Count: count,
cbGroupSize: group_size as u32,
cbKey: key_size as u32,
cbSeedLength: seed.len() as u32,
hashAlgorithm: hash,
standardVersion: 1,
},
&DsaKeyPrivateV2Payload {
seed: &seed,
group: &q.value_padded(group_size)?,
modulus: &p.value_padded(key_size)?,
generator: &g.value_padded(key_size)?,
public: &y,
priv_exp: &x.value_padded(group_size),
},
))
},
};
use win_crypto_ng::asymmetric::{Import};
let provider = AsymmetricAlgorithm::open(AsymmetricAlgorithmId::Dsa)?;
let pair = AsymmetricKey::<Dsa, Private>::import(
Dsa,
&provider,
blob
)?;
let digest = pad_truncating(&digest, q.value().len());
assert_eq!(q.value().len(), digest.len());
let sig = pair.sign(&digest, None)?;
let (r, s) = sig.split_at(sig.len() / 2);
Ok((mpi::MPI::new(r), mpi::MPI::new(s)))
}
fn dsa_verify(p: &MPI, q: &MPI, g: &MPI, y: &MPI,
digest: &[u8],
r: &MPI, s: &MPI)
-> Result<bool>
{
use cng::key_blob::{DsaKeyPublicPayload, DsaKeyPublicBlob};
use cng::key_blob::{DsaKeyPublicV2Payload, DsaKeyPublicV2Blob};
use cng::asymmetric::{
AsymmetricAlgorithm,
AsymmetricAlgorithmId,
AsymmetricKey,
Dsa,
DsaPublicBlob,
Public,
signature::Verifier,
};
use cng::helpers::Blob;
let y = y.value_padded(p.value().len())
.map_err(|e| Error::InvalidKey(e.to_string()))?;
if y.len() > 3072 / 8 {
return Err(Error::InvalidOperation(
"DSA keys are supported up to 3072-bits".to_string()).into()
);
}
let field_sz = q.value().len();
let mut signature = vec![0u8; 2 * field_sz];
fn bad(e: impl ToString) -> anyhow::Error {
Error::BadSignature(e.to_string()).into()
}
signature[..field_sz].copy_from_slice(
&r.value_padded(field_sz).map_err(bad)?);
signature[field_sz..].copy_from_slice(
&s.value_padded(field_sz).map_err(bad)?);
enum Version { V1, V2 }
let version = if y.len() <= 128 { Version::V1 } else { Version::V2 };
let blob: DsaPublicBlob = match version {
Version::V1 => {
let mut group = [0; 20];
if let Ok(v) = q.value_padded(group.len()) {
group[..].copy_from_slice(&v);
} else {
return Err(Error::InvalidOperation(
"DSA keys' group parameter exceeds 160 bits"
.to_string()).into());
}
DsaPublicBlob::V1(Blob::<DsaKeyPublicBlob>::clone_from_parts(
&winapi::shared::bcrypt::BCRYPT_DSA_KEY_BLOB {
dwMagic: winapi::shared::bcrypt::BCRYPT_DSA_PUBLIC_MAGIC,
cbKey: y.len() as u32,
Count: [0; 4], Seed: [0; 20], q: group,
},
&DsaKeyPublicPayload {
modulus: p.value(),
generator: g.value(),
public: &y,
},
))
},
Version::V2 => {
let hash = match q.value().len() {
20 => 0,
32 => 1,
64 => 2,
_ => return Err(Error::InvalidOperation(
"CNG accepts DSA q with length of either length of 20, 32 or 64".into())
.into()),
};
let (count, seed) = ([0x0; 4], vec![0x0; q.value().len()]);
DsaPublicBlob::V2(Blob::<DsaKeyPublicV2Blob>::clone_from_parts(
&winapi::shared::bcrypt::BCRYPT_DSA_KEY_BLOB_V2 {
dwMagic: winapi::shared::bcrypt::BCRYPT_DSA_PUBLIC_MAGIC_V2,
Count: count,
cbGroupSize: q.value().len() as u32,
cbKey: y.len() as u32,
cbSeedLength: seed.len() as u32,
hashAlgorithm: hash,
standardVersion: 1,
},
&DsaKeyPublicV2Payload {
seed: &seed,
group: q.value(),
modulus: p.value(),
generator: g.value(),
public: &y,
},
))
},
};
use win_crypto_ng::asymmetric::Import;
let provider = AsymmetricAlgorithm::open(AsymmetricAlgorithmId::Dsa)?;
let key = AsymmetricKey::<Dsa, Public>::import(
Dsa,
&provider,
blob
)?;
let digest = pad_truncating(&digest, q.value().len());
assert_eq!(q.value().len(), digest.len());
Ok(key.verify(&digest, &signature, None).map(|_| true)?)
}
}
impl KeyPair {
pub(crate) fn sign_backend(&self,
secret: &mpi::SecretKeyMaterial,
hash_algo: HashAlgorithm,
digest: &[u8])
-> Result<mpi::Signature>
{
use cng::asymmetric::{AsymmetricAlgorithm, AsymmetricAlgorithmId};
use cng::asymmetric::{AsymmetricKey, Private, Rsa};
use cng::asymmetric::signature::{Signer, SignaturePadding};
use cng::key_blob::RsaKeyPrivatePayload;
use cng::key_blob::EccKeyPrivatePayload;
use cng::asymmetric::ecc::NamedCurve;
#[allow(deprecated)]
Ok(match (self.public().pk_algo(), self.public().mpis(), secret) {
(PublicKeyAlgorithm::RSAEncryptSign,
&mpi::PublicKey::RSA { ref e, ref n },
&mpi::SecretKeyMaterial::RSA { ref p, ref q, ref d, .. }) |
(PublicKeyAlgorithm::RSASign,
&mpi::PublicKey::RSA { ref e, ref n },
&mpi::SecretKeyMaterial::RSA { ref p, ref q, ref d, .. }) => {
let provider = AsymmetricAlgorithm::open(AsymmetricAlgorithmId::Rsa)?;
let key = AsymmetricKey::<Rsa, Private>::import_from_parts(
&provider,
&RsaKeyPrivatePayload {
modulus: n.value(),
pub_exp: e.value(),
prime1: p.value(),
prime2: q.value(),
}
)?;
let hash = hash_algo.try_into()?;
let padding = SignaturePadding::pkcs1(hash);
let sig = key.sign(digest, Some(padding))?;
mpi::Signature::RSA { s: mpi::MPI::new(&*sig) }
},
(PublicKeyAlgorithm::ECDSA,
mpi::PublicKey::ECDSA { curve, q },
mpi::SecretKeyMaterial::ECDSA { scalar }) =>
{
let (x, y) = q.decode_point(curve)?;
let curve_bytes = curve.field_size()?;
let secret = scalar.value_padded(curve_bytes);
use cng::asymmetric::{ecc::{NistP256, NistP384, NistP521}, Ecdsa};
let sig = match curve {
Curve::NistP256 => {
let provider = AsymmetricAlgorithm::open(
AsymmetricAlgorithmId::Ecdsa(NamedCurve::NistP256)
)?;
let key = AsymmetricKey::<Ecdsa<NistP256>, Private>::import_from_parts(
&provider,
&EccKeyPrivatePayload { x, y, d: &secret }
)?;
key.sign(digest, None)?
},
Curve::NistP384 => {
let provider = AsymmetricAlgorithm::open(
AsymmetricAlgorithmId::Ecdsa(NamedCurve::NistP384)
)?;
let key = AsymmetricKey::<Ecdsa<NistP384>, Private>::import_from_parts(
&provider,
&EccKeyPrivatePayload { x, y, d: &secret }
)?;
key.sign(digest, None)?
},
Curve::NistP521 => {
let provider = AsymmetricAlgorithm::open(
AsymmetricAlgorithmId::Ecdsa(NamedCurve::NistP521)
)?;
let key = AsymmetricKey::<Ecdsa<NistP521>, Private>::import_from_parts(
&provider,
&EccKeyPrivatePayload { x, y, d: &secret }
)?;
key.sign(digest, None)?
},
_ => return Err(
Error::UnsupportedEllipticCurve(curve.clone()).into()),
};
let (r, s) = sig.split_at(sig.len() / 2);
mpi::Signature::ECDSA {
r: mpi::MPI::new(r),
s: mpi::MPI::new(s),
}
},
(pk_algo, _, _) => Err(Error::InvalidOperation(format!(
"unsupported combination of algorithm {:?}, key {:?}, \
and secret key {:?}",
pk_algo, self.public(), self.secret())))?,
})
}
}
impl KeyPair {
pub(crate) fn decrypt_backend(
&self,
secret: &mpi::SecretKeyMaterial,
ciphertext: &mpi::Ciphertext,
plaintext_len: Option<usize>,
) -> Result<SessionKey> {
use crate::PublicKeyAlgorithm::*;
#[allow(deprecated)]
Ok(match (self.public().mpis(), secret, ciphertext) {
(mpi::PublicKey::RSA { ref e, ref n },
mpi::SecretKeyMaterial::RSA { ref p, ref q, ref d, .. },
mpi::Ciphertext::RSA { ref c }) => {
use cng::asymmetric::{AsymmetricAlgorithm, AsymmetricAlgorithmId};
use cng::asymmetric::{AsymmetricKey, Private, Rsa};
use cng::asymmetric::EncryptionPadding;
use cng::key_blob::RsaKeyPrivatePayload;
let provider = AsymmetricAlgorithm::open(AsymmetricAlgorithmId::Rsa)?;
let key = AsymmetricKey::<Rsa, Private>::import_from_parts(
&provider,
&RsaKeyPrivatePayload {
modulus: n.value(),
pub_exp: e.value(),
prime1: p.value(),
prime2: q.value(),
}
)?;
let pad_to = round_up_to_multiple_of(c.value().len(), 8);
assert!(pad_to >= c.value().len());
let c = c.value_padded(pad_to).expect("we don't truncate");
let decrypted =
key.decrypt(Some(EncryptionPadding::Pkcs1), &c)?;
SessionKey::from(decrypted)
}
(mpi::PublicKey::ElGamal { .. },
mpi::SecretKeyMaterial::ElGamal { .. },
mpi::Ciphertext::ElGamal { .. }) =>
return Err(
Error::UnsupportedPublicKeyAlgorithm(ElGamalEncrypt).into()),
(mpi::PublicKey::ECDH{ .. },
mpi::SecretKeyMaterial::ECDH { .. },
mpi::Ciphertext::ECDH { .. }) =>
crate::crypto::ecdh::decrypt(self.public(), secret, ciphertext,
plaintext_len)?,
(public, secret, ciphertext) =>
return Err(Error::InvalidOperation(format!(
"unsupported combination of key pair {:?}/{:?} \
and ciphertext {:?}",
public, secret, ciphertext)).into()),
})
}
}
impl<P: key::KeyParts, R: key::KeyRole> Key<P, R> {
pub(crate) fn encrypt_backend(&self, data: &SessionKey) -> Result<mpi::Ciphertext> {
use cng::asymmetric::{AsymmetricAlgorithm, AsymmetricAlgorithmId};
use cng::asymmetric::{AsymmetricKey, Public, Rsa};
use cng::key_blob::RsaKeyPublicPayload;
use PublicKeyAlgorithm::*;
#[allow(deprecated)]
match self.pk_algo() {
RSAEncryptSign | RSAEncrypt => {
match self.mpis() {
mpi::PublicKey::RSA { e, n } => {
let ciphertext_len = n.value().len();
if data.len() + 11 > ciphertext_len {
return Err(Error::InvalidArgument(
"Plaintext data too large".into()).into());
}
let provider = AsymmetricAlgorithm::open(AsymmetricAlgorithmId::Rsa)?;
let key = AsymmetricKey::<Rsa, Public>::import_from_parts(
&provider,
&RsaKeyPublicPayload {
modulus: n.value(),
pub_exp: e.value(),
}
)?;
let padding = win_crypto_ng::asymmetric::EncryptionPadding::Pkcs1;
let ciphertext = key.encrypt(Some(padding), data)?;
Ok(mpi::Ciphertext::RSA {
c: mpi::MPI::new(ciphertext.as_ref()),
})
},
pk => {
Err(Error::MalformedPacket(
format!(
"Key: Expected RSA public key, got {:?}",
pk)).into())
},
}
},
ECDH => crate::crypto::ecdh::encrypt(self.parts_as_public(), data),
RSASign | DSA | ECDSA | EdDSA | Ed25519 | Ed448 =>
Err(Error::InvalidOperation(
format!("{} is not an encryption algorithm", self.pk_algo())
).into()),
ElGamalEncrypt | ElGamalEncryptSign |
X25519 | X448 |
Private(_) | Unknown(_) =>
Err(Error::UnsupportedPublicKeyAlgorithm(self.pk_algo()).into()),
}
}
pub(crate) fn verify_backend(&self, sig: &mpi::Signature, hash_algo: HashAlgorithm,
digest: &[u8]) -> Result<()> {
use cng::asymmetric::{AsymmetricAlgorithm, AsymmetricAlgorithmId};
use cng::asymmetric::{AsymmetricKey, Public, Rsa};
use cng::asymmetric::ecc::NamedCurve;
use cng::asymmetric::signature::{Verifier, SignaturePadding};
use cng::key_blob::RsaKeyPublicPayload;
fn bad(e: impl ToString) -> anyhow::Error {
Error::BadSignature(e.to_string()).into()
}
let ok = match (self.mpis(), sig) {
(mpi::PublicKey::RSA { e, n }, mpi::Signature::RSA { s }) => {
let s = pad(s.value(), n.value().len()).map_err(bad)?;
let n = pad_at_least(n.value(), 512);
let provider = AsymmetricAlgorithm::open(AsymmetricAlgorithmId::Rsa)?;
let key = AsymmetricKey::<Rsa, Public>::import_from_parts(
&provider,
&RsaKeyPublicPayload {
modulus: &n,
pub_exp: e.value(),
}
)?;
let hash = hash_algo.try_into()?;
let padding = SignaturePadding::pkcs1(hash);
key.verify(digest, &s, Some(padding)).map(|_| true)?
},
(mpi::PublicKey::ECDSA { curve, q }, mpi::Signature::ECDSA { s, r }) =>
{
let (x, y) = q.decode_point(curve)?;
let field_sz = x.len();
let mut signature = vec![0u8; 2 * field_sz];
signature[..field_sz].copy_from_slice(
&r.value_padded(field_sz).map_err(bad)?);
signature[field_sz..].copy_from_slice(
&s.value_padded(field_sz).map_err(bad)?);
use cng::key_blob::EccKeyPublicPayload;
use cng::asymmetric::{ecc::{NistP256, NistP384, NistP521}, Ecdsa};
match curve {
Curve::NistP256 => {
let provider = AsymmetricAlgorithm::open(
AsymmetricAlgorithmId::Ecdsa(NamedCurve::NistP256)
)?;
let key = AsymmetricKey::<Ecdsa<NistP256>, Public>::import_from_parts(
&provider,
&EccKeyPublicPayload { x, y }
)?;
key.verify(digest, &signature, None).map(|_| true)?
},
Curve::NistP384 => {
let provider = AsymmetricAlgorithm::open(
AsymmetricAlgorithmId::Ecdsa(NamedCurve::NistP384)
)?;
let key = AsymmetricKey::<Ecdsa<NistP384>, Public>::import_from_parts(
&provider,
&EccKeyPublicPayload { x, y }
)?;
key.verify(digest, &signature, None).map(|_| true)?
},
Curve::NistP521 => {
let provider = AsymmetricAlgorithm::open(
AsymmetricAlgorithmId::Ecdsa(NamedCurve::NistP521)
)?;
let key = AsymmetricKey::<Ecdsa<NistP521>, Public>::import_from_parts(
&provider,
&EccKeyPublicPayload { x, y }
)?;
key.verify(digest, &signature, None).map(|_| true)?
},
_ => return Err(
Error::UnsupportedEllipticCurve(curve.clone()).into()),
}
},
_ => return Err(Error::MalformedPacket(format!(
"unsupported combination of key {} and signature {:?}.",
self.pk_algo(), sig)).into()),
};
if ok {
Ok(())
} else {
Err(Error::ManipulatedMessage.into())
}
}
}
impl<R> Key4<SecretParts, R>
where
R: key::KeyRole,
{
pub fn import_secret_rsa<T>(d: &[u8], p: &[u8], q: &[u8], ctime: T) -> Result<Self>
where
T: Into<Option<SystemTime>>,
{
let (p, q) = crate::crypto::rsa_sort_raw_pq(p, q);
let big_p = BigUint::from_bytes_be(p);
let big_q = BigUint::from_bytes_be(q);
let n = big_p.clone() * big_q.clone();
let big_d = BigUint::from_bytes_be(d);
let big_phi = (big_p.clone() - 1u32) * (big_q.clone() - 1u32);
let e = big_d.mod_inverse(big_phi) .and_then(|x: BigInt| x.to_biguint())
.ok_or_else(|| Error::MalformedMPI("RSA: `d` and `(p-1)(q-1)` aren't coprime".into()))?;
let u: BigUint = big_p.mod_inverse(big_q) .and_then(|x: BigInt| x.to_biguint())
.ok_or_else(|| Error::MalformedMPI("RSA: `p` and `q` aren't coprime".into()))?;
Self::with_secret(
ctime.into().unwrap_or_else(crate::now),
PublicKeyAlgorithm::RSAEncryptSign,
mpi::PublicKey::RSA {
e: mpi::MPI::new(&e.to_bytes_be()),
n: mpi::MPI::new(&n.to_bytes_be()),
},
mpi::SecretKeyMaterial::RSA {
d: d.into(),
p: p.into(),
q: q.into(),
u: u.to_bytes_be().into(),
}.into()
)
}
pub fn generate_rsa(bits: usize) -> Result<Self> {
use win_crypto_ng::asymmetric::{AsymmetricKey, Rsa};
let blob = AsymmetricKey::builder(Rsa)
.key_bits(bits as u32)
.build()?
.export_full()?;
let public = mpi::PublicKey::RSA {
e: mpi::MPI::new(blob.pub_exp()).into(),
n: mpi::MPI::new(blob.modulus()).into(),
};
let p = mpi::ProtectedMPI::from(blob.prime1());
let q = mpi::ProtectedMPI::from(blob.prime2());
let (p, q) = rsa_sort_pq(p, q);
let big_p = BigUint::from_bytes_be(p.value());
let big_q = BigUint::from_bytes_be(q.value());
let u = big_p.mod_inverse(big_q) .and_then(|x: BigInt| x.to_biguint())
.expect("CNG to generate a valid RSA key (where p, q are coprime)");
let private = mpi::SecretKeyMaterial::RSA {
p: p,
q: q,
d: blob.priv_exp().into(),
u: u.to_bytes_be().into(),
};
Self::with_secret(
crate::now(),
PublicKeyAlgorithm::RSAEncryptSign,
public,
private.into()
)
}
pub(crate) fn generate_ecc_backend(for_signing: bool, curve: Curve)
-> Result<(PublicKeyAlgorithm,
mpi::PublicKey,
mpi::SecretKeyMaterial)>
{
use cng::asymmetric::{ecc, Export};
use cng::asymmetric::{AsymmetricKey, AsymmetricAlgorithmId};
match (curve.clone(), for_signing) {
(Curve::Ed25519, true) =>
unreachable!("handled in Key4::generate_ecc"),
(Curve::Cv25519, false) =>
unreachable!("handled in Key4::generate_ecc"),
(Curve::NistP256, ..) | (Curve::NistP384, ..) | (Curve::NistP521, ..) => {
let cng_curve = match curve {
Curve::NistP256 => ecc::NamedCurve::NistP256,
Curve::NistP384 => ecc::NamedCurve::NistP384,
Curve::NistP521 => ecc::NamedCurve::NistP521,
_ => unreachable!()
};
let ecc_algo = if for_signing {
AsymmetricAlgorithmId::Ecdsa(cng_curve)
} else {
AsymmetricAlgorithmId::Ecdh(cng_curve)
};
let blob = AsymmetricKey::builder(ecc_algo).build()?.export()?;
let blob = match blob.try_into::<cng::key_blob::EccKeyPrivateBlob>() {
Ok(blob) => blob,
Err(..) => unreachable!()
};
let field_sz = cng_curve.key_bits() as usize;
let q = mpi::MPI::new_point(blob.x(), blob.y(), field_sz);
let scalar = mpi::ProtectedMPI::from(blob.d());
if for_signing {
Ok((
PublicKeyAlgorithm::ECDSA,
mpi::PublicKey::ECDSA { curve, q },
mpi::SecretKeyMaterial::ECDSA { scalar },
))
} else {
let hash =
crate::crypto::ecdh::default_ecdh_kdf_hash(&curve);
let sym =
crate::crypto::ecdh::default_ecdh_kek_cipher(&curve);
Ok((
PublicKeyAlgorithm::ECDH,
mpi::PublicKey::ECDH { curve, q, hash, sym },
mpi::SecretKeyMaterial::ECDH { scalar },
))
}
},
_ => Err(Error::UnsupportedEllipticCurve(curve).into()),
}
}
}
fn round_up_to_multiple_of(n: usize, m: usize) -> usize {
((n + m - 1) / m) * m
}
fn rsa_sort_pq(p: mpi::ProtectedMPI, q: mpi::ProtectedMPI)
-> (mpi::ProtectedMPI, mpi::ProtectedMPI)
{
if p < q {
(p, q)
} else {
(q, p)
}
}
#[cfg(test)]
mod tests {
quickcheck! {
fn round_up_to_multiple_of(n: usize, m: usize) -> bool {
if n.checked_add(m).is_none() {
return true;
}
if m == 0 {
return true;
}
let rounded_up = super::round_up_to_multiple_of(n, m);
assert!(rounded_up >= n);
assert!(rounded_up - n < m);
assert_eq!(rounded_up % m, 0);
true
}
}
}