Refactor padding modes into submodules (#312)
The padding mode modules have gotten quite large. This commit refactors types into respective submodules, with the toplevel module defining the same-named padding schemes.
This commit is contained in:
parent
d9968bc0c9
commit
f5918ad3bf
@ -53,7 +53,7 @@ impl PartialEq for RsaPrivateKey {
|
||||
}
|
||||
|
||||
impl Hash for RsaPrivateKey {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) -> () {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
// Domain separator for RSA private keys
|
||||
state.write(b"RsaPrivateKey");
|
||||
Hash::hash(&self.pubkey_components, state);
|
||||
|
@ -239,6 +239,7 @@ pub use pkcs8;
|
||||
pub use sha2;
|
||||
|
||||
pub use crate::{
|
||||
errors::{Error, Result},
|
||||
key::{RsaPrivateKey, RsaPublicKey},
|
||||
oaep::Oaep,
|
||||
pkcs1v15::{Pkcs1v15Encrypt, Pkcs1v15Sign},
|
||||
|
152
src/oaep.rs
152
src/oaep.rs
@ -4,26 +4,27 @@
|
||||
//!
|
||||
//! See [code example in the toplevel rustdoc](../index.html#oaep-encryption).
|
||||
|
||||
mod decrypting_key;
|
||||
mod encrypting_key;
|
||||
|
||||
pub use self::{decrypting_key::DecryptingKey, encrypting_key::EncryptingKey};
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::{String, ToString};
|
||||
use alloc::vec::Vec;
|
||||
use core::fmt;
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use digest::{Digest, DynDigest, FixedOutputReset};
|
||||
use num_bigint::BigUint;
|
||||
use rand_core::CryptoRngCore;
|
||||
use zeroize::{ZeroizeOnDrop, Zeroizing};
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
use crate::algorithms::oaep::*;
|
||||
use crate::algorithms::pad::{uint_to_be_pad, uint_to_zeroizing_be_pad};
|
||||
use crate::algorithms::rsa::{rsa_decrypt_and_check, rsa_encrypt};
|
||||
use crate::dummy_rng::DummyRng;
|
||||
use crate::errors::{Error, Result};
|
||||
use crate::key::{self, RsaPrivateKey, RsaPublicKey};
|
||||
use crate::traits::PaddingScheme;
|
||||
use crate::traits::{Decryptor, RandomizedDecryptor, RandomizedEncryptor};
|
||||
use crate::traits::PublicKeyParts;
|
||||
use crate::traits::{PaddingScheme, PublicKeyParts};
|
||||
|
||||
/// Encryption and Decryption using [OAEP padding](https://datatracker.ietf.org/doc/html/rfc8017#section-7.1).
|
||||
///
|
||||
@ -282,149 +283,12 @@ fn decrypt_digest<R: CryptoRngCore + ?Sized, D: Digest, MGD: Digest + FixedOutpu
|
||||
oaep_decrypt_digest::<D, MGD>(&mut em, label, priv_key.size())
|
||||
}
|
||||
|
||||
/// Encryption key for PKCS#1 v1.5 encryption as described in [RFC8017 § 7.1].
|
||||
///
|
||||
/// [RFC8017 § 7.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.1
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EncryptingKey<D, MGD = D>
|
||||
where
|
||||
D: Digest,
|
||||
MGD: Digest + FixedOutputReset,
|
||||
{
|
||||
inner: RsaPublicKey,
|
||||
label: Option<String>,
|
||||
phantom: PhantomData<D>,
|
||||
mg_phantom: PhantomData<MGD>,
|
||||
}
|
||||
|
||||
impl<D, MGD> EncryptingKey<D, MGD>
|
||||
where
|
||||
D: Digest,
|
||||
MGD: Digest + FixedOutputReset,
|
||||
{
|
||||
/// Create a new verifying key from an RSA public key.
|
||||
pub fn new(key: RsaPublicKey) -> Self {
|
||||
Self {
|
||||
inner: key,
|
||||
label: None,
|
||||
phantom: Default::default(),
|
||||
mg_phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new verifying key from an RSA public key using provided label
|
||||
pub fn new_with_label<S: AsRef<str>>(key: RsaPublicKey, label: S) -> Self {
|
||||
Self {
|
||||
inner: key,
|
||||
label: Some(label.as_ref().to_string()),
|
||||
phantom: Default::default(),
|
||||
mg_phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D, MGD> RandomizedEncryptor for EncryptingKey<D, MGD>
|
||||
where
|
||||
D: Digest,
|
||||
MGD: Digest + FixedOutputReset,
|
||||
{
|
||||
fn encrypt_with_rng<R: CryptoRngCore + ?Sized>(
|
||||
&self,
|
||||
rng: &mut R,
|
||||
msg: &[u8],
|
||||
) -> Result<Vec<u8>> {
|
||||
encrypt_digest::<_, D, MGD>(rng, &self.inner, msg, self.label.as_ref().cloned())
|
||||
}
|
||||
}
|
||||
|
||||
/// Decryption key for PKCS#1 v1.5 decryption as described in [RFC8017 § 7.1].
|
||||
///
|
||||
/// [RFC8017 § 7.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.1
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DecryptingKey<D, MGD = D>
|
||||
where
|
||||
D: Digest,
|
||||
MGD: Digest + FixedOutputReset,
|
||||
{
|
||||
inner: RsaPrivateKey,
|
||||
label: Option<String>,
|
||||
phantom: PhantomData<D>,
|
||||
mg_phantom: PhantomData<MGD>,
|
||||
}
|
||||
|
||||
impl<D, MGD> DecryptingKey<D, MGD>
|
||||
where
|
||||
D: Digest,
|
||||
MGD: Digest + FixedOutputReset,
|
||||
{
|
||||
/// Create a new verifying key from an RSA public key.
|
||||
pub fn new(key: RsaPrivateKey) -> Self {
|
||||
Self {
|
||||
inner: key,
|
||||
label: None,
|
||||
phantom: Default::default(),
|
||||
mg_phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new verifying key from an RSA public key using provided label
|
||||
pub fn new_with_label<S: AsRef<str>>(key: RsaPrivateKey, label: S) -> Self {
|
||||
Self {
|
||||
inner: key,
|
||||
label: Some(label.as_ref().to_string()),
|
||||
phantom: Default::default(),
|
||||
mg_phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D, MGD> Decryptor for DecryptingKey<D, MGD>
|
||||
where
|
||||
D: Digest,
|
||||
MGD: Digest + FixedOutputReset,
|
||||
{
|
||||
fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>> {
|
||||
decrypt_digest::<DummyRng, D, MGD>(
|
||||
None,
|
||||
&self.inner,
|
||||
ciphertext,
|
||||
self.label.as_ref().cloned(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D, MGD> RandomizedDecryptor for DecryptingKey<D, MGD>
|
||||
where
|
||||
D: Digest,
|
||||
MGD: Digest + FixedOutputReset,
|
||||
{
|
||||
fn decrypt_with_rng<R: CryptoRngCore + ?Sized>(
|
||||
&self,
|
||||
rng: &mut R,
|
||||
ciphertext: &[u8],
|
||||
) -> Result<Vec<u8>> {
|
||||
decrypt_digest::<_, D, MGD>(
|
||||
Some(rng),
|
||||
&self.inner,
|
||||
ciphertext,
|
||||
self.label.as_ref().cloned(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D, MGD> ZeroizeOnDrop for DecryptingKey<D, MGD>
|
||||
where
|
||||
D: Digest,
|
||||
MGD: Digest + FixedOutputReset,
|
||||
{
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::key::{RsaPrivateKey, RsaPublicKey};
|
||||
use crate::oaep::{DecryptingKey, EncryptingKey, Oaep};
|
||||
use crate::traits::{Decryptor, RandomizedDecryptor, RandomizedEncryptor};
|
||||
use crate::traits::PublicKeyParts;
|
||||
use crate::traits::{Decryptor, RandomizedDecryptor, RandomizedEncryptor};
|
||||
|
||||
use alloc::string::String;
|
||||
use digest::{Digest, DynDigest, FixedOutputReset};
|
||||
|
96
src/oaep/decrypting_key.rs
Normal file
96
src/oaep/decrypting_key.rs
Normal file
@ -0,0 +1,96 @@
|
||||
use super::decrypt_digest;
|
||||
use crate::{
|
||||
dummy_rng::DummyRng,
|
||||
traits::{Decryptor, RandomizedDecryptor},
|
||||
Result, RsaPrivateKey,
|
||||
};
|
||||
use alloc::{
|
||||
string::{String, ToString},
|
||||
vec::Vec,
|
||||
};
|
||||
use core::marker::PhantomData;
|
||||
use digest::{Digest, FixedOutputReset};
|
||||
use rand_core::CryptoRngCore;
|
||||
use zeroize::ZeroizeOnDrop;
|
||||
|
||||
/// Decryption key for PKCS#1 v1.5 decryption as described in [RFC8017 § 7.1].
|
||||
///
|
||||
/// [RFC8017 § 7.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.1
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DecryptingKey<D, MGD = D>
|
||||
where
|
||||
D: Digest,
|
||||
MGD: Digest + FixedOutputReset,
|
||||
{
|
||||
inner: RsaPrivateKey,
|
||||
label: Option<String>,
|
||||
phantom: PhantomData<D>,
|
||||
mg_phantom: PhantomData<MGD>,
|
||||
}
|
||||
|
||||
impl<D, MGD> DecryptingKey<D, MGD>
|
||||
where
|
||||
D: Digest,
|
||||
MGD: Digest + FixedOutputReset,
|
||||
{
|
||||
/// Create a new verifying key from an RSA public key.
|
||||
pub fn new(key: RsaPrivateKey) -> Self {
|
||||
Self {
|
||||
inner: key,
|
||||
label: None,
|
||||
phantom: Default::default(),
|
||||
mg_phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new verifying key from an RSA public key using provided label
|
||||
pub fn new_with_label<S: AsRef<str>>(key: RsaPrivateKey, label: S) -> Self {
|
||||
Self {
|
||||
inner: key,
|
||||
label: Some(label.as_ref().to_string()),
|
||||
phantom: Default::default(),
|
||||
mg_phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D, MGD> Decryptor for DecryptingKey<D, MGD>
|
||||
where
|
||||
D: Digest,
|
||||
MGD: Digest + FixedOutputReset,
|
||||
{
|
||||
fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>> {
|
||||
decrypt_digest::<DummyRng, D, MGD>(
|
||||
None,
|
||||
&self.inner,
|
||||
ciphertext,
|
||||
self.label.as_ref().cloned(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D, MGD> RandomizedDecryptor for DecryptingKey<D, MGD>
|
||||
where
|
||||
D: Digest,
|
||||
MGD: Digest + FixedOutputReset,
|
||||
{
|
||||
fn decrypt_with_rng<R: CryptoRngCore + ?Sized>(
|
||||
&self,
|
||||
rng: &mut R,
|
||||
ciphertext: &[u8],
|
||||
) -> Result<Vec<u8>> {
|
||||
decrypt_digest::<_, D, MGD>(
|
||||
Some(rng),
|
||||
&self.inner,
|
||||
ciphertext,
|
||||
self.label.as_ref().cloned(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D, MGD> ZeroizeOnDrop for DecryptingKey<D, MGD>
|
||||
where
|
||||
D: Digest,
|
||||
MGD: Digest + FixedOutputReset,
|
||||
{
|
||||
}
|
64
src/oaep/encrypting_key.rs
Normal file
64
src/oaep/encrypting_key.rs
Normal file
@ -0,0 +1,64 @@
|
||||
use super::encrypt_digest;
|
||||
use crate::{traits::RandomizedEncryptor, Result, RsaPublicKey};
|
||||
use alloc::{
|
||||
string::{String, ToString},
|
||||
vec::Vec,
|
||||
};
|
||||
use core::marker::PhantomData;
|
||||
use digest::{Digest, FixedOutputReset};
|
||||
use rand_core::CryptoRngCore;
|
||||
|
||||
/// Encryption key for PKCS#1 v1.5 encryption as described in [RFC8017 § 7.1].
|
||||
///
|
||||
/// [RFC8017 § 7.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.1
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EncryptingKey<D, MGD = D>
|
||||
where
|
||||
D: Digest,
|
||||
MGD: Digest + FixedOutputReset,
|
||||
{
|
||||
inner: RsaPublicKey,
|
||||
label: Option<String>,
|
||||
phantom: PhantomData<D>,
|
||||
mg_phantom: PhantomData<MGD>,
|
||||
}
|
||||
|
||||
impl<D, MGD> EncryptingKey<D, MGD>
|
||||
where
|
||||
D: Digest,
|
||||
MGD: Digest + FixedOutputReset,
|
||||
{
|
||||
/// Create a new verifying key from an RSA public key.
|
||||
pub fn new(key: RsaPublicKey) -> Self {
|
||||
Self {
|
||||
inner: key,
|
||||
label: None,
|
||||
phantom: Default::default(),
|
||||
mg_phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new verifying key from an RSA public key using provided label
|
||||
pub fn new_with_label<S: AsRef<str>>(key: RsaPublicKey, label: S) -> Self {
|
||||
Self {
|
||||
inner: key,
|
||||
label: Some(label.as_ref().to_string()),
|
||||
phantom: Default::default(),
|
||||
mg_phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D, MGD> RandomizedEncryptor for EncryptingKey<D, MGD>
|
||||
where
|
||||
D: Digest,
|
||||
MGD: Digest + FixedOutputReset,
|
||||
{
|
||||
fn encrypt_with_rng<R: CryptoRngCore + ?Sized>(
|
||||
&self,
|
||||
rng: &mut R,
|
||||
msg: &[u8],
|
||||
) -> Result<Vec<u8>> {
|
||||
encrypt_digest::<_, D, MGD>(rng, &self.inner, msg, self.label.as_ref().cloned())
|
||||
}
|
||||
}
|
562
src/pkcs1v15.rs
562
src/pkcs1v15.rs
@ -6,36 +6,31 @@
|
||||
//!
|
||||
//! [RFC8017 § 8.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.2
|
||||
|
||||
use alloc::{boxed::Box, string::ToString, vec::Vec};
|
||||
use core::fmt::{Debug, Display, Formatter, LowerHex, UpperHex};
|
||||
use core::marker::PhantomData;
|
||||
mod decrypting_key;
|
||||
mod encrypting_key;
|
||||
mod signature;
|
||||
mod signing_key;
|
||||
mod verifying_key;
|
||||
|
||||
pub use self::{
|
||||
decrypting_key::DecryptingKey, encrypting_key::EncryptingKey, signature::Signature,
|
||||
signing_key::SigningKey, verifying_key::VerifyingKey,
|
||||
};
|
||||
|
||||
use alloc::{boxed::Box, vec::Vec};
|
||||
use core::fmt::Debug;
|
||||
use digest::Digest;
|
||||
use num_bigint::BigUint;
|
||||
use pkcs8::{
|
||||
spki::{
|
||||
der::AnyRef, AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier,
|
||||
SignatureAlgorithmIdentifier,
|
||||
},
|
||||
AssociatedOid, Document, EncodePrivateKey, EncodePublicKey, SecretDocument,
|
||||
};
|
||||
use pkcs8::AssociatedOid;
|
||||
use rand_core::CryptoRngCore;
|
||||
use signature::{
|
||||
hazmat::{PrehashSigner, PrehashVerifier},
|
||||
DigestSigner, DigestVerifier, Keypair, RandomizedDigestSigner, RandomizedSigner,
|
||||
SignatureEncoding, Signer, Verifier,
|
||||
};
|
||||
use zeroize::{ZeroizeOnDrop, Zeroizing};
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
use crate::algorithms::pad::{uint_to_be_pad, uint_to_zeroizing_be_pad};
|
||||
use crate::algorithms::pkcs1v15::*;
|
||||
use crate::algorithms::rsa::{rsa_decrypt_and_check, rsa_encrypt};
|
||||
use crate::dummy_rng::DummyRng;
|
||||
use crate::errors::{Error, Result};
|
||||
use crate::key;
|
||||
use crate::traits::{PaddingScheme, SignatureScheme};
|
||||
use crate::traits::PublicKeyParts;
|
||||
use crate::traits::{Decryptor, EncryptingKeypair, RandomizedDecryptor, RandomizedEncryptor};
|
||||
use crate::{RsaPrivateKey, RsaPublicKey};
|
||||
use crate::key::{self, RsaPrivateKey, RsaPublicKey};
|
||||
use crate::traits::{PaddingScheme, PublicKeyParts, SignatureScheme};
|
||||
|
||||
/// Encryption using PKCS#1 v1.5 padding.
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||
@ -138,62 +133,6 @@ impl SignatureScheme for Pkcs1v15Sign {
|
||||
}
|
||||
}
|
||||
|
||||
/// PKCS#1 v1.5 signatures as described in [RFC8017 § 8.2].
|
||||
///
|
||||
/// [RFC8017 § 8.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.2
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct Signature {
|
||||
inner: BigUint,
|
||||
len: usize,
|
||||
}
|
||||
|
||||
impl SignatureEncoding for Signature {
|
||||
type Repr = Box<[u8]>;
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for Signature {
|
||||
type Error = signature::Error;
|
||||
|
||||
fn try_from(bytes: &[u8]) -> signature::Result<Self> {
|
||||
Ok(Self {
|
||||
inner: BigUint::from_bytes_be(bytes),
|
||||
len: bytes.len(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Signature> for Box<[u8]> {
|
||||
fn from(signature: Signature) -> Box<[u8]> {
|
||||
signature.inner.to_bytes_be().into_boxed_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Signature {
|
||||
fn fmt(&self, fmt: &mut Formatter<'_>) -> core::result::Result<(), core::fmt::Error> {
|
||||
fmt.debug_tuple("Signature")
|
||||
.field(&self.to_string())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl LowerHex for Signature {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "{:x}", &self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl UpperHex for Signature {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "{:X}", &self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Signature {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "{:X}", self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Encrypts the given message with RSA and the padding
|
||||
/// scheme from PKCS#1 v1.5. The message must be no longer than the
|
||||
/// length of the public modulus minus 11 bytes.
|
||||
@ -279,462 +218,6 @@ fn verify(
|
||||
pkcs1v15_sign_unpad(prefix, hashed, &em, pub_key.size())
|
||||
}
|
||||
|
||||
/// Signing key for PKCS#1 v1.5 signatures as described in [RFC8017 § 8.2].
|
||||
///
|
||||
/// [RFC8017 § 8.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.2
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
inner: RsaPrivateKey,
|
||||
prefix: Vec<u8>,
|
||||
phantom: PhantomData<D>,
|
||||
}
|
||||
|
||||
impl<D> SigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
/// Create a new signing key from the give RSA private key with an empty prefix.
|
||||
///
|
||||
/// ## Note: unprefixed signatures are uncommon
|
||||
///
|
||||
/// In most cases you'll want to use [`SigningKey::new`].
|
||||
pub fn new_unprefixed(key: RsaPrivateKey) -> Self {
|
||||
Self {
|
||||
inner: key,
|
||||
prefix: Vec::new(),
|
||||
phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a new signing key with an empty prefix.
|
||||
pub fn random_unprefixed<R: CryptoRngCore + ?Sized>(
|
||||
rng: &mut R,
|
||||
bit_size: usize,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
inner: RsaPrivateKey::new(rng, bit_size)?,
|
||||
prefix: Vec::new(),
|
||||
phantom: Default::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> AssociatedAlgorithmIdentifier for SigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
type Params = AnyRef<'static>;
|
||||
|
||||
const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID;
|
||||
}
|
||||
|
||||
impl<D> SignatureAlgorithmIdentifier for SigningKey<D>
|
||||
where
|
||||
D: Digest + oid::RsaSignatureAssociatedOid,
|
||||
{
|
||||
type Params = AnyRef<'static>;
|
||||
|
||||
const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> =
|
||||
AlgorithmIdentifierRef {
|
||||
oid: D::OID,
|
||||
parameters: Some(AnyRef::NULL),
|
||||
};
|
||||
}
|
||||
|
||||
impl<D> From<RsaPrivateKey> for SigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn from(key: RsaPrivateKey) -> Self {
|
||||
Self::new_unprefixed(key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> From<SigningKey<D>> for RsaPrivateKey
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn from(key: SigningKey<D>) -> Self {
|
||||
key.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> SigningKey<D>
|
||||
where
|
||||
D: Digest + AssociatedOid,
|
||||
{
|
||||
/// Create a new signing key with a prefix for the digest `D`.
|
||||
pub fn new(key: RsaPrivateKey) -> Self {
|
||||
Self {
|
||||
inner: key,
|
||||
prefix: pkcs1v15_generate_prefix::<D>(),
|
||||
phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a new signing key with a prefix for the digest `D`.
|
||||
pub fn random<R: CryptoRngCore + ?Sized>(rng: &mut R, bit_size: usize) -> Result<Self> {
|
||||
Ok(Self {
|
||||
inner: RsaPrivateKey::new(rng, bit_size)?,
|
||||
prefix: pkcs1v15_generate_prefix::<D>(),
|
||||
phantom: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a new signing key with a prefix for the digest `D`.
|
||||
#[deprecated(since = "0.9.0", note = "use SigningKey::new instead")]
|
||||
pub fn new_with_prefix(key: RsaPrivateKey) -> Self {
|
||||
Self::new(key)
|
||||
}
|
||||
|
||||
/// Generate a new signing key with a prefix for the digest `D`.
|
||||
#[deprecated(since = "0.9.0", note = "use SigningKey::random instead")]
|
||||
pub fn random_with_prefix<R: CryptoRngCore + ?Sized>(
|
||||
rng: &mut R,
|
||||
bit_size: usize,
|
||||
) -> Result<Self> {
|
||||
Self::random(rng, bit_size)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> AsRef<RsaPrivateKey> for SigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn as_ref(&self) -> &RsaPrivateKey {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> EncodePrivateKey for SigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn to_pkcs8_der(&self) -> pkcs8::Result<SecretDocument> {
|
||||
self.inner.to_pkcs8_der()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> ZeroizeOnDrop for SigningKey<D> where D: Digest {}
|
||||
|
||||
impl<D> Signer<Signature> for SigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn try_sign(&self, msg: &[u8]) -> signature::Result<Signature> {
|
||||
sign::<DummyRng>(None, &self.inner, &self.prefix, &D::digest(msg))?
|
||||
.as_slice()
|
||||
.try_into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> RandomizedSigner<Signature> for SigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn try_sign_with_rng(
|
||||
&self,
|
||||
rng: &mut impl CryptoRngCore,
|
||||
msg: &[u8],
|
||||
) -> signature::Result<Signature> {
|
||||
sign(Some(rng), &self.inner, &self.prefix, &D::digest(msg))?
|
||||
.as_slice()
|
||||
.try_into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> DigestSigner<D, Signature> for SigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn try_sign_digest(&self, digest: D) -> signature::Result<Signature> {
|
||||
sign::<DummyRng>(None, &self.inner, &self.prefix, &digest.finalize())?
|
||||
.as_slice()
|
||||
.try_into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> RandomizedDigestSigner<D, Signature> for SigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn try_sign_digest_with_rng(
|
||||
&self,
|
||||
rng: &mut impl CryptoRngCore,
|
||||
digest: D,
|
||||
) -> signature::Result<Signature> {
|
||||
sign(Some(rng), &self.inner, &self.prefix, &digest.finalize())?
|
||||
.as_slice()
|
||||
.try_into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> PrehashSigner<Signature> for SigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn sign_prehash(&self, prehash: &[u8]) -> signature::Result<Signature> {
|
||||
sign::<DummyRng>(None, &self.inner, &self.prefix, prehash)?
|
||||
.as_slice()
|
||||
.try_into()
|
||||
}
|
||||
}
|
||||
|
||||
/// Verifying key for PKCS#1 v1.5 signatures as described in [RFC8017 § 8.2].
|
||||
///
|
||||
/// [RFC8017 § 8.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.2
|
||||
#[derive(Debug)]
|
||||
pub struct VerifyingKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
inner: RsaPublicKey,
|
||||
prefix: Vec<u8>,
|
||||
phantom: PhantomData<D>,
|
||||
}
|
||||
|
||||
/* Implemented manually so we don't have to bind D with Clone */
|
||||
impl<D> Clone for VerifyingKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
inner: self.inner.clone(),
|
||||
prefix: self.prefix.clone(),
|
||||
phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> VerifyingKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
/// Create a new verifying key from an RSA public key with an empty prefix.
|
||||
///
|
||||
/// ## Note: unprefixed signatures are uncommon
|
||||
///
|
||||
/// In most cases you'll want to use [`VerifyingKey::new`] instead.
|
||||
pub fn new_unprefixed(key: RsaPublicKey) -> Self {
|
||||
Self {
|
||||
inner: key,
|
||||
prefix: Vec::new(),
|
||||
phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> AssociatedAlgorithmIdentifier for VerifyingKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
type Params = AnyRef<'static>;
|
||||
|
||||
const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID;
|
||||
}
|
||||
|
||||
impl<D> SignatureAlgorithmIdentifier for VerifyingKey<D>
|
||||
where
|
||||
D: Digest + oid::RsaSignatureAssociatedOid,
|
||||
{
|
||||
type Params = AnyRef<'static>;
|
||||
|
||||
const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> =
|
||||
AlgorithmIdentifierRef {
|
||||
oid: D::OID,
|
||||
parameters: Some(AnyRef::NULL),
|
||||
};
|
||||
}
|
||||
|
||||
impl<D> From<RsaPublicKey> for VerifyingKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn from(key: RsaPublicKey) -> Self {
|
||||
Self::new_unprefixed(key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> From<VerifyingKey<D>> for RsaPublicKey
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn from(key: VerifyingKey<D>) -> Self {
|
||||
key.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> VerifyingKey<D>
|
||||
where
|
||||
D: Digest + AssociatedOid,
|
||||
{
|
||||
/// Create a new verifying key with a prefix for the digest `D`.
|
||||
pub fn new(key: RsaPublicKey) -> Self {
|
||||
Self {
|
||||
inner: key,
|
||||
prefix: pkcs1v15_generate_prefix::<D>(),
|
||||
phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new verifying key with a prefix for the digest `D`.
|
||||
#[deprecated(since = "0.9.0", note = "use VerifyingKey::new instead")]
|
||||
pub fn new_with_prefix(key: RsaPublicKey) -> Self {
|
||||
Self::new(key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> AsRef<RsaPublicKey> for VerifyingKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn as_ref(&self) -> &RsaPublicKey {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> Keypair for SigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
type VerifyingKey = VerifyingKey<D>;
|
||||
fn verifying_key(&self) -> Self::VerifyingKey {
|
||||
VerifyingKey {
|
||||
inner: self.inner.to_public_key(),
|
||||
prefix: self.prefix.clone(),
|
||||
phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> Verifier<Signature> for VerifyingKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn verify(&self, msg: &[u8], signature: &Signature) -> signature::Result<()> {
|
||||
verify(
|
||||
&self.inner,
|
||||
&self.prefix.clone(),
|
||||
&D::digest(msg),
|
||||
&signature.inner,
|
||||
signature.len,
|
||||
)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> DigestVerifier<D, Signature> for VerifyingKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn verify_digest(&self, digest: D, signature: &Signature) -> signature::Result<()> {
|
||||
verify(
|
||||
&self.inner,
|
||||
&self.prefix,
|
||||
&digest.finalize(),
|
||||
&signature.inner,
|
||||
signature.len,
|
||||
)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> PrehashVerifier<Signature> for VerifyingKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn verify_prehash(&self, prehash: &[u8], signature: &Signature) -> signature::Result<()> {
|
||||
verify(
|
||||
&self.inner,
|
||||
&self.prefix,
|
||||
prehash,
|
||||
&signature.inner,
|
||||
signature.len,
|
||||
)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> EncodePublicKey for VerifyingKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn to_public_key_der(&self) -> pkcs8::spki::Result<Document> {
|
||||
self.inner.to_public_key_der()
|
||||
}
|
||||
}
|
||||
|
||||
/// Encryption key for PKCS#1 v1.5 encryption as described in [RFC8017 § 7.2].
|
||||
///
|
||||
/// [RFC8017 § 7.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.2
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EncryptingKey {
|
||||
inner: RsaPublicKey,
|
||||
}
|
||||
|
||||
impl EncryptingKey {
|
||||
/// Create a new verifying key from an RSA public key.
|
||||
pub fn new(key: RsaPublicKey) -> Self {
|
||||
Self { inner: key }
|
||||
}
|
||||
}
|
||||
|
||||
impl RandomizedEncryptor for EncryptingKey {
|
||||
fn encrypt_with_rng<R: CryptoRngCore + ?Sized>(
|
||||
&self,
|
||||
rng: &mut R,
|
||||
msg: &[u8],
|
||||
) -> Result<Vec<u8>> {
|
||||
encrypt(rng, &self.inner, msg)
|
||||
}
|
||||
}
|
||||
|
||||
/// Decryption key for PKCS#1 v1.5 decryption as described in [RFC8017 § 7.2].
|
||||
///
|
||||
/// [RFC8017 § 7.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.2
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DecryptingKey {
|
||||
inner: RsaPrivateKey,
|
||||
}
|
||||
|
||||
impl DecryptingKey {
|
||||
/// Create a new verifying key from an RSA public key.
|
||||
pub fn new(key: RsaPrivateKey) -> Self {
|
||||
Self { inner: key }
|
||||
}
|
||||
}
|
||||
|
||||
impl Decryptor for DecryptingKey {
|
||||
fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>> {
|
||||
decrypt::<DummyRng>(None, &self.inner, ciphertext)
|
||||
}
|
||||
}
|
||||
|
||||
impl RandomizedDecryptor for DecryptingKey {
|
||||
fn decrypt_with_rng<R: CryptoRngCore + ?Sized>(
|
||||
&self,
|
||||
rng: &mut R,
|
||||
ciphertext: &[u8],
|
||||
) -> Result<Vec<u8>> {
|
||||
decrypt(Some(rng), &self.inner, ciphertext)
|
||||
}
|
||||
}
|
||||
|
||||
impl EncryptingKeypair for DecryptingKey {
|
||||
type EncryptingKey = EncryptingKey;
|
||||
fn encrypting_key(&self) -> EncryptingKey {
|
||||
EncryptingKey {
|
||||
inner: self.inner.clone().into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ZeroizeOnDrop for DecryptingKey {}
|
||||
|
||||
mod oid {
|
||||
use const_oid::ObjectIdentifier;
|
||||
|
||||
@ -778,6 +261,11 @@ mod oid {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use ::signature::{
|
||||
hazmat::{PrehashSigner, PrehashVerifier},
|
||||
DigestSigner, DigestVerifier, Keypair, RandomizedDigestSigner, RandomizedSigner,
|
||||
SignatureEncoding, Signer, Verifier,
|
||||
};
|
||||
use base64ct::{Base64, Encoding};
|
||||
use hex_literal::hex;
|
||||
use num_bigint::BigUint;
|
||||
@ -790,9 +278,11 @@ mod tests {
|
||||
use sha1::{Digest, Sha1};
|
||||
use sha2::Sha256;
|
||||
use sha3::Sha3_256;
|
||||
use signature::{RandomizedSigner, Signer, Verifier};
|
||||
|
||||
use crate::{traits::PublicKeyParts, RsaPrivateKey, RsaPublicKey};
|
||||
use crate::traits::{
|
||||
Decryptor, EncryptingKeypair, PublicKeyParts, RandomizedDecryptor, RandomizedEncryptor,
|
||||
};
|
||||
use crate::{RsaPrivateKey, RsaPublicKey};
|
||||
|
||||
fn get_private_key() -> RsaPrivateKey {
|
||||
// In order to generate new test vectors you'll need the PEM form of this key:
|
||||
|
51
src/pkcs1v15/decrypting_key.rs
Normal file
51
src/pkcs1v15/decrypting_key.rs
Normal file
@ -0,0 +1,51 @@
|
||||
use super::{decrypt, EncryptingKey};
|
||||
use crate::{
|
||||
dummy_rng::DummyRng,
|
||||
traits::{Decryptor, EncryptingKeypair, RandomizedDecryptor},
|
||||
Result, RsaPrivateKey,
|
||||
};
|
||||
use alloc::vec::Vec;
|
||||
use rand_core::CryptoRngCore;
|
||||
use zeroize::ZeroizeOnDrop;
|
||||
|
||||
/// Decryption key for PKCS#1 v1.5 decryption as described in [RFC8017 § 7.2].
|
||||
///
|
||||
/// [RFC8017 § 7.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.2
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DecryptingKey {
|
||||
inner: RsaPrivateKey,
|
||||
}
|
||||
|
||||
impl DecryptingKey {
|
||||
/// Create a new verifying key from an RSA public key.
|
||||
pub fn new(key: RsaPrivateKey) -> Self {
|
||||
Self { inner: key }
|
||||
}
|
||||
}
|
||||
|
||||
impl Decryptor for DecryptingKey {
|
||||
fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>> {
|
||||
decrypt::<DummyRng>(None, &self.inner, ciphertext)
|
||||
}
|
||||
}
|
||||
|
||||
impl RandomizedDecryptor for DecryptingKey {
|
||||
fn decrypt_with_rng<R: CryptoRngCore + ?Sized>(
|
||||
&self,
|
||||
rng: &mut R,
|
||||
ciphertext: &[u8],
|
||||
) -> Result<Vec<u8>> {
|
||||
decrypt(Some(rng), &self.inner, ciphertext)
|
||||
}
|
||||
}
|
||||
|
||||
impl EncryptingKeypair for DecryptingKey {
|
||||
type EncryptingKey = EncryptingKey;
|
||||
fn encrypting_key(&self) -> EncryptingKey {
|
||||
EncryptingKey {
|
||||
inner: self.inner.clone().into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ZeroizeOnDrop for DecryptingKey {}
|
29
src/pkcs1v15/encrypting_key.rs
Normal file
29
src/pkcs1v15/encrypting_key.rs
Normal file
@ -0,0 +1,29 @@
|
||||
use super::encrypt;
|
||||
use crate::{traits::RandomizedEncryptor, Result, RsaPublicKey};
|
||||
use alloc::vec::Vec;
|
||||
use rand_core::CryptoRngCore;
|
||||
|
||||
/// Encryption key for PKCS#1 v1.5 encryption as described in [RFC8017 § 7.2].
|
||||
///
|
||||
/// [RFC8017 § 7.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.2
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EncryptingKey {
|
||||
pub(super) inner: RsaPublicKey,
|
||||
}
|
||||
|
||||
impl EncryptingKey {
|
||||
/// Create a new verifying key from an RSA public key.
|
||||
pub fn new(key: RsaPublicKey) -> Self {
|
||||
Self { inner: key }
|
||||
}
|
||||
}
|
||||
|
||||
impl RandomizedEncryptor for EncryptingKey {
|
||||
fn encrypt_with_rng<R: CryptoRngCore + ?Sized>(
|
||||
&self,
|
||||
rng: &mut R,
|
||||
msg: &[u8],
|
||||
) -> Result<Vec<u8>> {
|
||||
encrypt(rng, &self.inner, msg)
|
||||
}
|
||||
}
|
65
src/pkcs1v15/signature.rs
Normal file
65
src/pkcs1v15/signature.rs
Normal file
@ -0,0 +1,65 @@
|
||||
pub use ::signature::{
|
||||
hazmat::{PrehashSigner, PrehashVerifier},
|
||||
DigestSigner, DigestVerifier, Error, Keypair, RandomizedDigestSigner, RandomizedSigner, Result,
|
||||
SignatureEncoding, Signer, Verifier,
|
||||
};
|
||||
|
||||
use alloc::{boxed::Box, string::ToString};
|
||||
use core::fmt::{Debug, Display, Formatter, LowerHex, UpperHex};
|
||||
use num_bigint::BigUint;
|
||||
|
||||
/// PKCS#1 v1.5 signatures as described in [RFC8017 § 8.2].
|
||||
///
|
||||
/// [RFC8017 § 8.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.2
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct Signature {
|
||||
pub(super) inner: BigUint,
|
||||
pub(super) len: usize,
|
||||
}
|
||||
|
||||
impl SignatureEncoding for Signature {
|
||||
type Repr = Box<[u8]>;
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for Signature {
|
||||
type Error = signature::Error;
|
||||
|
||||
fn try_from(bytes: &[u8]) -> signature::Result<Self> {
|
||||
Ok(Self {
|
||||
inner: BigUint::from_bytes_be(bytes),
|
||||
len: bytes.len(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Signature> for Box<[u8]> {
|
||||
fn from(signature: Signature) -> Box<[u8]> {
|
||||
signature.inner.to_bytes_be().into_boxed_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Signature {
|
||||
fn fmt(&self, fmt: &mut Formatter<'_>) -> core::result::Result<(), core::fmt::Error> {
|
||||
fmt.debug_tuple("Signature")
|
||||
.field(&self.to_string())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl LowerHex for Signature {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "{:x}", &self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl UpperHex for Signature {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "{:X}", &self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Signature {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "{:X}", self)
|
||||
}
|
||||
}
|
244
src/pkcs1v15/signing_key.rs
Normal file
244
src/pkcs1v15/signing_key.rs
Normal file
@ -0,0 +1,244 @@
|
||||
use super::{oid, pkcs1v15_generate_prefix, sign, Signature, VerifyingKey};
|
||||
use crate::{dummy_rng::DummyRng, Result, RsaPrivateKey};
|
||||
use alloc::vec::Vec;
|
||||
use core::marker::PhantomData;
|
||||
use digest::Digest;
|
||||
use pkcs8::{
|
||||
spki::{
|
||||
der::AnyRef, AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier,
|
||||
SignatureAlgorithmIdentifier,
|
||||
},
|
||||
AssociatedOid, EncodePrivateKey, SecretDocument,
|
||||
};
|
||||
use rand_core::CryptoRngCore;
|
||||
use signature::{
|
||||
hazmat::PrehashSigner, DigestSigner, Keypair, RandomizedDigestSigner, RandomizedSigner, Signer,
|
||||
};
|
||||
use zeroize::ZeroizeOnDrop;
|
||||
|
||||
/// Signing key for PKCS#1 v1.5 signatures as described in [RFC8017 § 8.2].
|
||||
///
|
||||
/// [RFC8017 § 8.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.2
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
inner: RsaPrivateKey,
|
||||
prefix: Vec<u8>,
|
||||
phantom: PhantomData<D>,
|
||||
}
|
||||
|
||||
impl<D> SigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
/// Create a new signing key from the give RSA private key with an empty prefix.
|
||||
///
|
||||
/// ## Note: unprefixed signatures are uncommon
|
||||
///
|
||||
/// In most cases you'll want to use [`SigningKey::new`].
|
||||
pub fn new_unprefixed(key: RsaPrivateKey) -> Self {
|
||||
Self {
|
||||
inner: key,
|
||||
prefix: Vec::new(),
|
||||
phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a new signing key with an empty prefix.
|
||||
pub fn random_unprefixed<R: CryptoRngCore + ?Sized>(
|
||||
rng: &mut R,
|
||||
bit_size: usize,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
inner: RsaPrivateKey::new(rng, bit_size)?,
|
||||
prefix: Vec::new(),
|
||||
phantom: Default::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> SigningKey<D>
|
||||
where
|
||||
D: Digest + AssociatedOid,
|
||||
{
|
||||
/// Create a new signing key with a prefix for the digest `D`.
|
||||
pub fn new(key: RsaPrivateKey) -> Self {
|
||||
Self {
|
||||
inner: key,
|
||||
prefix: pkcs1v15_generate_prefix::<D>(),
|
||||
phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a new signing key with a prefix for the digest `D`.
|
||||
pub fn random<R: CryptoRngCore + ?Sized>(rng: &mut R, bit_size: usize) -> Result<Self> {
|
||||
Ok(Self {
|
||||
inner: RsaPrivateKey::new(rng, bit_size)?,
|
||||
prefix: pkcs1v15_generate_prefix::<D>(),
|
||||
phantom: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a new signing key with a prefix for the digest `D`.
|
||||
#[deprecated(since = "0.9.0", note = "use SigningKey::new instead")]
|
||||
pub fn new_with_prefix(key: RsaPrivateKey) -> Self {
|
||||
Self::new(key)
|
||||
}
|
||||
|
||||
/// Generate a new signing key with a prefix for the digest `D`.
|
||||
#[deprecated(since = "0.9.0", note = "use SigningKey::random instead")]
|
||||
pub fn random_with_prefix<R: CryptoRngCore + ?Sized>(
|
||||
rng: &mut R,
|
||||
bit_size: usize,
|
||||
) -> Result<Self> {
|
||||
Self::random(rng, bit_size)
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// `*Signer` trait impls
|
||||
//
|
||||
|
||||
impl<D> DigestSigner<D, Signature> for SigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn try_sign_digest(&self, digest: D) -> signature::Result<Signature> {
|
||||
sign::<DummyRng>(None, &self.inner, &self.prefix, &digest.finalize())?
|
||||
.as_slice()
|
||||
.try_into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> PrehashSigner<Signature> for SigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn sign_prehash(&self, prehash: &[u8]) -> signature::Result<Signature> {
|
||||
sign::<DummyRng>(None, &self.inner, &self.prefix, prehash)?
|
||||
.as_slice()
|
||||
.try_into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> RandomizedDigestSigner<D, Signature> for SigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn try_sign_digest_with_rng(
|
||||
&self,
|
||||
rng: &mut impl CryptoRngCore,
|
||||
digest: D,
|
||||
) -> signature::Result<Signature> {
|
||||
sign(Some(rng), &self.inner, &self.prefix, &digest.finalize())?
|
||||
.as_slice()
|
||||
.try_into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> RandomizedSigner<Signature> for SigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn try_sign_with_rng(
|
||||
&self,
|
||||
rng: &mut impl CryptoRngCore,
|
||||
msg: &[u8],
|
||||
) -> signature::Result<Signature> {
|
||||
sign(Some(rng), &self.inner, &self.prefix, &D::digest(msg))?
|
||||
.as_slice()
|
||||
.try_into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> Signer<Signature> for SigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn try_sign(&self, msg: &[u8]) -> signature::Result<Signature> {
|
||||
sign::<DummyRng>(None, &self.inner, &self.prefix, &D::digest(msg))?
|
||||
.as_slice()
|
||||
.try_into()
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Other trait impls
|
||||
//
|
||||
|
||||
impl<D> AsRef<RsaPrivateKey> for SigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn as_ref(&self) -> &RsaPrivateKey {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> AssociatedAlgorithmIdentifier for SigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
type Params = AnyRef<'static>;
|
||||
|
||||
const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID;
|
||||
}
|
||||
|
||||
impl<D> EncodePrivateKey for SigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn to_pkcs8_der(&self) -> pkcs8::Result<SecretDocument> {
|
||||
self.inner.to_pkcs8_der()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> From<RsaPrivateKey> for SigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn from(key: RsaPrivateKey) -> Self {
|
||||
Self::new_unprefixed(key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> From<SigningKey<D>> for RsaPrivateKey
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn from(key: SigningKey<D>) -> Self {
|
||||
key.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> Keypair for SigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
type VerifyingKey = VerifyingKey<D>;
|
||||
|
||||
fn verifying_key(&self) -> Self::VerifyingKey {
|
||||
VerifyingKey {
|
||||
inner: self.inner.to_public_key(),
|
||||
prefix: self.prefix.clone(),
|
||||
phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> SignatureAlgorithmIdentifier for SigningKey<D>
|
||||
where
|
||||
D: Digest + oid::RsaSignatureAssociatedOid,
|
||||
{
|
||||
type Params = AnyRef<'static>;
|
||||
|
||||
const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> =
|
||||
AlgorithmIdentifierRef {
|
||||
oid: D::OID,
|
||||
parameters: Some(AnyRef::NULL),
|
||||
};
|
||||
}
|
||||
|
||||
impl<D> ZeroizeOnDrop for SigningKey<D> where D: Digest {}
|
192
src/pkcs1v15/verifying_key.rs
Normal file
192
src/pkcs1v15/verifying_key.rs
Normal file
@ -0,0 +1,192 @@
|
||||
use super::{oid, pkcs1v15_generate_prefix, verify, Signature};
|
||||
use crate::RsaPublicKey;
|
||||
use alloc::vec::Vec;
|
||||
use core::marker::PhantomData;
|
||||
use digest::Digest;
|
||||
use pkcs8::{
|
||||
spki::{
|
||||
der::AnyRef, AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier,
|
||||
SignatureAlgorithmIdentifier,
|
||||
},
|
||||
AssociatedOid, Document, EncodePublicKey,
|
||||
};
|
||||
use signature::{hazmat::PrehashVerifier, DigestVerifier, Verifier};
|
||||
|
||||
/// Verifying key for PKCS#1 v1.5 signatures as described in [RFC8017 § 8.2].
|
||||
///
|
||||
/// [RFC8017 § 8.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.2
|
||||
#[derive(Debug)]
|
||||
pub struct VerifyingKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
pub(super) inner: RsaPublicKey,
|
||||
pub(super) prefix: Vec<u8>,
|
||||
pub(super) phantom: PhantomData<D>,
|
||||
}
|
||||
|
||||
impl<D> VerifyingKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
/// Create a new verifying key from an RSA public key with an empty prefix.
|
||||
///
|
||||
/// ## Note: unprefixed signatures are uncommon
|
||||
///
|
||||
/// In most cases you'll want to use [`VerifyingKey::new`] instead.
|
||||
pub fn new_unprefixed(key: RsaPublicKey) -> Self {
|
||||
Self {
|
||||
inner: key,
|
||||
prefix: Vec::new(),
|
||||
phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> VerifyingKey<D>
|
||||
where
|
||||
D: Digest + AssociatedOid,
|
||||
{
|
||||
/// Create a new verifying key with a prefix for the digest `D`.
|
||||
pub fn new(key: RsaPublicKey) -> Self {
|
||||
Self {
|
||||
inner: key,
|
||||
prefix: pkcs1v15_generate_prefix::<D>(),
|
||||
phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new verifying key with a prefix for the digest `D`.
|
||||
#[deprecated(since = "0.9.0", note = "use VerifyingKey::new instead")]
|
||||
pub fn new_with_prefix(key: RsaPublicKey) -> Self {
|
||||
Self::new(key)
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// `*Verifier` trait impls
|
||||
//
|
||||
|
||||
impl<D> DigestVerifier<D, Signature> for VerifyingKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn verify_digest(&self, digest: D, signature: &Signature) -> signature::Result<()> {
|
||||
verify(
|
||||
&self.inner,
|
||||
&self.prefix,
|
||||
&digest.finalize(),
|
||||
&signature.inner,
|
||||
signature.len,
|
||||
)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> PrehashVerifier<Signature> for VerifyingKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn verify_prehash(&self, prehash: &[u8], signature: &Signature) -> signature::Result<()> {
|
||||
verify(
|
||||
&self.inner,
|
||||
&self.prefix,
|
||||
prehash,
|
||||
&signature.inner,
|
||||
signature.len,
|
||||
)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> Verifier<Signature> for VerifyingKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn verify(&self, msg: &[u8], signature: &Signature) -> signature::Result<()> {
|
||||
verify(
|
||||
&self.inner,
|
||||
&self.prefix.clone(),
|
||||
&D::digest(msg),
|
||||
&signature.inner,
|
||||
signature.len,
|
||||
)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Other trait impls
|
||||
//
|
||||
|
||||
impl<D> AsRef<RsaPublicKey> for VerifyingKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn as_ref(&self) -> &RsaPublicKey {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> AssociatedAlgorithmIdentifier for VerifyingKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
type Params = AnyRef<'static>;
|
||||
|
||||
const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID;
|
||||
}
|
||||
|
||||
// Implemented manually so we don't have to bind D with Clone
|
||||
impl<D> Clone for VerifyingKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
inner: self.inner.clone(),
|
||||
prefix: self.prefix.clone(),
|
||||
phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> EncodePublicKey for VerifyingKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn to_public_key_der(&self) -> pkcs8::spki::Result<Document> {
|
||||
self.inner.to_public_key_der()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> From<RsaPublicKey> for VerifyingKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn from(key: RsaPublicKey) -> Self {
|
||||
Self::new_unprefixed(key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> From<VerifyingKey<D>> for RsaPublicKey
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn from(key: VerifyingKey<D>) -> Self {
|
||||
key.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> SignatureAlgorithmIdentifier for VerifyingKey<D>
|
||||
where
|
||||
D: Digest + oid::RsaSignatureAssociatedOid,
|
||||
{
|
||||
type Params = AnyRef<'static>;
|
||||
|
||||
const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> =
|
||||
AlgorithmIdentifierRef {
|
||||
oid: D::OID,
|
||||
parameters: Some(AnyRef::NULL),
|
||||
};
|
||||
}
|
589
src/pss.rs
589
src/pss.rs
@ -9,40 +9,34 @@
|
||||
//! [Probabilistic Signature Scheme]: https://en.wikipedia.org/wiki/Probabilistic_signature_scheme
|
||||
//! [RFC8017 § 8.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.1
|
||||
|
||||
use alloc::{boxed::Box, string::ToString, vec::Vec};
|
||||
use core::fmt::{self, Debug, Display, Formatter, LowerHex, UpperHex};
|
||||
use core::marker::PhantomData;
|
||||
mod blinded_signing_key;
|
||||
mod signature;
|
||||
mod signing_key;
|
||||
mod verifying_key;
|
||||
|
||||
pub use self::{
|
||||
blinded_signing_key::BlindedSigningKey, signature::Signature, signing_key::SigningKey,
|
||||
verifying_key::VerifyingKey,
|
||||
};
|
||||
|
||||
use alloc::{boxed::Box, vec::Vec};
|
||||
use core::fmt::{self, Debug};
|
||||
|
||||
use const_oid::{AssociatedOid, ObjectIdentifier};
|
||||
use digest::{Digest, DynDigest, FixedOutputReset};
|
||||
use num_bigint::BigUint;
|
||||
use pkcs1::RsaPssParams;
|
||||
use pkcs8::{
|
||||
spki::{
|
||||
der::{Any, AnyRef},
|
||||
AlgorithmIdentifierOwned, AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier,
|
||||
DynSignatureAlgorithmIdentifier,
|
||||
},
|
||||
Document, EncodePrivateKey, EncodePublicKey, SecretDocument,
|
||||
};
|
||||
use pkcs8::spki::{der::Any, AlgorithmIdentifierOwned};
|
||||
use rand_core::CryptoRngCore;
|
||||
use signature::{
|
||||
hazmat::{PrehashVerifier, RandomizedPrehashSigner},
|
||||
DigestVerifier, Keypair, RandomizedDigestSigner, RandomizedSigner, SignatureEncoding, Verifier,
|
||||
};
|
||||
use zeroize::ZeroizeOnDrop;
|
||||
|
||||
use crate::algorithms::pad::{uint_to_be_pad, uint_to_zeroizing_be_pad};
|
||||
use crate::algorithms::pss::*;
|
||||
use crate::algorithms::rsa::{rsa_decrypt_and_check, rsa_encrypt};
|
||||
use crate::errors::{Error, Result};
|
||||
use crate::traits::SignatureScheme;
|
||||
use crate::traits::PublicKeyParts;
|
||||
use crate::traits::SignatureScheme;
|
||||
use crate::{RsaPrivateKey, RsaPublicKey};
|
||||
|
||||
#[cfg(feature = "getrandom")]
|
||||
use {rand_core::OsRng, signature::Signer};
|
||||
|
||||
/// Digital signatures using PSS padding.
|
||||
pub struct Pss {
|
||||
/// Create blinded signatures.
|
||||
@ -129,62 +123,6 @@ impl Debug for Pss {
|
||||
}
|
||||
}
|
||||
|
||||
/// RSASSA-PSS signatures as described in [RFC8017 § 8.1].
|
||||
///
|
||||
/// [RFC8017 § 8.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.1
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct Signature {
|
||||
inner: BigUint,
|
||||
len: usize,
|
||||
}
|
||||
|
||||
impl SignatureEncoding for Signature {
|
||||
type Repr = Box<[u8]>;
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for Signature {
|
||||
type Error = signature::Error;
|
||||
|
||||
fn try_from(bytes: &[u8]) -> signature::Result<Self> {
|
||||
Ok(Self {
|
||||
len: bytes.len(),
|
||||
inner: BigUint::from_bytes_be(bytes),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Signature> for Box<[u8]> {
|
||||
fn from(signature: Signature) -> Box<[u8]> {
|
||||
signature.inner.to_bytes_be().into_boxed_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Signature {
|
||||
fn fmt(&self, fmt: &mut Formatter<'_>) -> core::result::Result<(), core::fmt::Error> {
|
||||
fmt.debug_tuple("Signature")
|
||||
.field(&self.to_string())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl LowerHex for Signature {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "{:x}", &self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl UpperHex for Signature {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "{:X}", &self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Signature {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "{:X}", self)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn verify(
|
||||
pub_key: &RsaPublicKey,
|
||||
hashed: &[u8],
|
||||
@ -289,64 +227,6 @@ fn sign_pss_with_salt_digest<T: CryptoRngCore + ?Sized, D: Digest + FixedOutputR
|
||||
)
|
||||
}
|
||||
|
||||
/// Signing key for producing RSASSA-PSS signatures as described in
|
||||
/// [RFC8017 § 8.1].
|
||||
///
|
||||
/// [RFC8017 § 8.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.1
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
inner: RsaPrivateKey,
|
||||
salt_len: usize,
|
||||
phantom: PhantomData<D>,
|
||||
}
|
||||
|
||||
impl<D> SigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
/// Create a new RSASSA-PSS signing key.
|
||||
/// Digest output size is used as a salt length.
|
||||
pub fn new(key: RsaPrivateKey) -> Self {
|
||||
Self::new_with_salt_len(key, <D as Digest>::output_size())
|
||||
}
|
||||
|
||||
/// Create a new RSASSA-PSS signing key with a salt of the given length.
|
||||
pub fn new_with_salt_len(key: RsaPrivateKey, salt_len: usize) -> Self {
|
||||
Self {
|
||||
inner: key,
|
||||
salt_len,
|
||||
phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a new random RSASSA-PSS signing key.
|
||||
/// Digest output size is used as a salt length.
|
||||
pub fn random<R: CryptoRngCore + ?Sized>(rng: &mut R, bit_size: usize) -> Result<Self> {
|
||||
Self::random_with_salt_len(rng, bit_size, <D as Digest>::output_size())
|
||||
}
|
||||
|
||||
/// Generate a new random RSASSA-PSS signing key with a salt of the given length.
|
||||
pub fn random_with_salt_len<R: CryptoRngCore + ?Sized>(
|
||||
rng: &mut R,
|
||||
bit_size: usize,
|
||||
salt_len: usize,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
inner: RsaPrivateKey::new(rng, bit_size)?,
|
||||
salt_len,
|
||||
phantom: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Return specified salt length for this key
|
||||
pub fn salt_len(&self) -> usize {
|
||||
self.salt_len
|
||||
}
|
||||
}
|
||||
|
||||
fn get_pss_signature_algo_id<D>(salt_len: u8) -> pkcs8::spki::Result<AlgorithmIdentifierOwned>
|
||||
where
|
||||
D: Digest + AssociatedOid,
|
||||
@ -361,447 +241,6 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
impl<D> AssociatedAlgorithmIdentifier for SigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
type Params = AnyRef<'static>;
|
||||
|
||||
const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID;
|
||||
}
|
||||
|
||||
impl<D> DynSignatureAlgorithmIdentifier for SigningKey<D>
|
||||
where
|
||||
D: Digest + AssociatedOid,
|
||||
{
|
||||
fn signature_algorithm_identifier(&self) -> pkcs8::spki::Result<AlgorithmIdentifierOwned> {
|
||||
get_pss_signature_algo_id::<D>(self.salt_len as u8)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> From<RsaPrivateKey> for SigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn from(key: RsaPrivateKey) -> Self {
|
||||
Self::new(key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> From<SigningKey<D>> for RsaPrivateKey
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn from(key: SigningKey<D>) -> Self {
|
||||
key.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> EncodePrivateKey for SigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn to_pkcs8_der(&self) -> pkcs8::Result<SecretDocument> {
|
||||
self.inner.to_pkcs8_der()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> Keypair for SigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
type VerifyingKey = VerifyingKey<D>;
|
||||
fn verifying_key(&self) -> Self::VerifyingKey {
|
||||
VerifyingKey {
|
||||
inner: self.inner.to_public_key(),
|
||||
salt_len: self.salt_len,
|
||||
phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "getrandom")]
|
||||
impl<D> Signer<Signature> for SigningKey<D>
|
||||
where
|
||||
D: Digest + FixedOutputReset,
|
||||
{
|
||||
fn try_sign(&self, msg: &[u8]) -> signature::Result<Signature> {
|
||||
self.try_sign_with_rng(&mut OsRng, msg)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> RandomizedSigner<Signature> for SigningKey<D>
|
||||
where
|
||||
D: Digest + FixedOutputReset,
|
||||
{
|
||||
fn try_sign_with_rng(
|
||||
&self,
|
||||
rng: &mut impl CryptoRngCore,
|
||||
msg: &[u8],
|
||||
) -> signature::Result<Signature> {
|
||||
sign_digest::<_, D>(rng, false, &self.inner, &D::digest(msg), self.salt_len)?
|
||||
.as_slice()
|
||||
.try_into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> RandomizedDigestSigner<D, Signature> for SigningKey<D>
|
||||
where
|
||||
D: Digest + FixedOutputReset,
|
||||
{
|
||||
fn try_sign_digest_with_rng(
|
||||
&self,
|
||||
rng: &mut impl CryptoRngCore,
|
||||
digest: D,
|
||||
) -> signature::Result<Signature> {
|
||||
sign_digest::<_, D>(rng, false, &self.inner, &digest.finalize(), self.salt_len)?
|
||||
.as_slice()
|
||||
.try_into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> RandomizedPrehashSigner<Signature> for SigningKey<D>
|
||||
where
|
||||
D: Digest + FixedOutputReset,
|
||||
{
|
||||
fn sign_prehash_with_rng(
|
||||
&self,
|
||||
rng: &mut impl CryptoRngCore,
|
||||
prehash: &[u8],
|
||||
) -> signature::Result<Signature> {
|
||||
sign_digest::<_, D>(rng, false, &self.inner, prehash, self.salt_len)?
|
||||
.as_slice()
|
||||
.try_into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> AsRef<RsaPrivateKey> for SigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn as_ref(&self) -> &RsaPrivateKey {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> ZeroizeOnDrop for SigningKey<D> where D: Digest {}
|
||||
|
||||
/// Signing key for producing "blinded" RSASSA-PSS signatures as described in
|
||||
/// [draft-irtf-cfrg-rsa-blind-signatures](https://datatracker.ietf.org/doc/draft-irtf-cfrg-rsa-blind-signatures/).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BlindedSigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
inner: RsaPrivateKey,
|
||||
salt_len: usize,
|
||||
phantom: PhantomData<D>,
|
||||
}
|
||||
|
||||
impl<D> BlindedSigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
/// Create a new RSASSA-PSS signing key which produces "blinded"
|
||||
/// signatures.
|
||||
/// Digest output size is used as a salt length.
|
||||
pub fn new(key: RsaPrivateKey) -> Self {
|
||||
Self::new_with_salt_len(key, <D as Digest>::output_size())
|
||||
}
|
||||
|
||||
/// Create a new RSASSA-PSS signing key which produces "blinded"
|
||||
/// signatures with a salt of the given length.
|
||||
pub fn new_with_salt_len(key: RsaPrivateKey, salt_len: usize) -> Self {
|
||||
Self {
|
||||
inner: key,
|
||||
salt_len,
|
||||
phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new random RSASSA-PSS signing key which produces "blinded"
|
||||
/// signatures.
|
||||
/// Digest output size is used as a salt length.
|
||||
pub fn random<R: CryptoRngCore + ?Sized>(rng: &mut R, bit_size: usize) -> Result<Self> {
|
||||
Self::random_with_salt_len(rng, bit_size, <D as Digest>::output_size())
|
||||
}
|
||||
|
||||
/// Create a new random RSASSA-PSS signing key which produces "blinded"
|
||||
/// signatures with a salt of the given length.
|
||||
pub fn random_with_salt_len<R: CryptoRngCore + ?Sized>(
|
||||
rng: &mut R,
|
||||
bit_size: usize,
|
||||
salt_len: usize,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
inner: RsaPrivateKey::new(rng, bit_size)?,
|
||||
salt_len,
|
||||
phantom: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Return specified salt length for this key
|
||||
pub fn salt_len(&self) -> usize {
|
||||
self.salt_len
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> AssociatedAlgorithmIdentifier for BlindedSigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
type Params = AnyRef<'static>;
|
||||
|
||||
const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID;
|
||||
}
|
||||
|
||||
impl<D> DynSignatureAlgorithmIdentifier for BlindedSigningKey<D>
|
||||
where
|
||||
D: Digest + AssociatedOid,
|
||||
{
|
||||
fn signature_algorithm_identifier(&self) -> pkcs8::spki::Result<AlgorithmIdentifierOwned> {
|
||||
get_pss_signature_algo_id::<D>(self.salt_len as u8)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> From<RsaPrivateKey> for BlindedSigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn from(key: RsaPrivateKey) -> Self {
|
||||
Self::new(key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> From<BlindedSigningKey<D>> for RsaPrivateKey
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn from(key: BlindedSigningKey<D>) -> Self {
|
||||
key.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> EncodePrivateKey for BlindedSigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn to_pkcs8_der(&self) -> pkcs8::Result<SecretDocument> {
|
||||
self.inner.to_pkcs8_der()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> Keypair for BlindedSigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
type VerifyingKey = VerifyingKey<D>;
|
||||
fn verifying_key(&self) -> Self::VerifyingKey {
|
||||
VerifyingKey {
|
||||
inner: self.inner.to_public_key(),
|
||||
salt_len: self.salt_len,
|
||||
phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> RandomizedSigner<Signature> for BlindedSigningKey<D>
|
||||
where
|
||||
D: Digest + FixedOutputReset,
|
||||
{
|
||||
fn try_sign_with_rng(
|
||||
&self,
|
||||
rng: &mut impl CryptoRngCore,
|
||||
msg: &[u8],
|
||||
) -> signature::Result<Signature> {
|
||||
sign_digest::<_, D>(rng, true, &self.inner, &D::digest(msg), self.salt_len)?
|
||||
.as_slice()
|
||||
.try_into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> RandomizedDigestSigner<D, Signature> for BlindedSigningKey<D>
|
||||
where
|
||||
D: Digest + FixedOutputReset,
|
||||
{
|
||||
fn try_sign_digest_with_rng(
|
||||
&self,
|
||||
rng: &mut impl CryptoRngCore,
|
||||
digest: D,
|
||||
) -> signature::Result<Signature> {
|
||||
sign_digest::<_, D>(rng, true, &self.inner, &digest.finalize(), self.salt_len)?
|
||||
.as_slice()
|
||||
.try_into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> RandomizedPrehashSigner<Signature> for BlindedSigningKey<D>
|
||||
where
|
||||
D: Digest + FixedOutputReset,
|
||||
{
|
||||
fn sign_prehash_with_rng(
|
||||
&self,
|
||||
rng: &mut impl CryptoRngCore,
|
||||
prehash: &[u8],
|
||||
) -> signature::Result<Signature> {
|
||||
sign_digest::<_, D>(rng, true, &self.inner, prehash, self.salt_len)?
|
||||
.as_slice()
|
||||
.try_into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> AsRef<RsaPrivateKey> for BlindedSigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn as_ref(&self) -> &RsaPrivateKey {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> ZeroizeOnDrop for BlindedSigningKey<D> where D: Digest {}
|
||||
|
||||
/// Verifying key for checking the validity of RSASSA-PSS signatures as
|
||||
/// described in [RFC8017 § 8.1].
|
||||
///
|
||||
/// [RFC8017 § 8.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.1
|
||||
#[derive(Debug)]
|
||||
pub struct VerifyingKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
inner: RsaPublicKey,
|
||||
salt_len: usize,
|
||||
phantom: PhantomData<D>,
|
||||
}
|
||||
|
||||
/* Implemented manually so we don't have to bind D with Clone */
|
||||
impl<D> Clone for VerifyingKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
inner: self.inner.clone(),
|
||||
salt_len: self.salt_len,
|
||||
phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> VerifyingKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
/// Create a new RSASSA-PSS verifying key.
|
||||
/// Digest output size is used as a salt length.
|
||||
pub fn new(key: RsaPublicKey) -> Self {
|
||||
Self::new_with_salt_len(key, <D as Digest>::output_size())
|
||||
}
|
||||
|
||||
/// Create a new RSASSA-PSS verifying key.
|
||||
pub fn new_with_salt_len(key: RsaPublicKey, salt_len: usize) -> Self {
|
||||
Self {
|
||||
inner: key,
|
||||
salt_len,
|
||||
phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> AssociatedAlgorithmIdentifier for VerifyingKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
type Params = AnyRef<'static>;
|
||||
|
||||
const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID;
|
||||
}
|
||||
|
||||
impl<D> From<RsaPublicKey> for VerifyingKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn from(key: RsaPublicKey) -> Self {
|
||||
Self::new(key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> From<VerifyingKey<D>> for RsaPublicKey
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn from(key: VerifyingKey<D>) -> Self {
|
||||
key.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> Verifier<Signature> for VerifyingKey<D>
|
||||
where
|
||||
D: Digest + FixedOutputReset,
|
||||
{
|
||||
fn verify(&self, msg: &[u8], signature: &Signature) -> signature::Result<()> {
|
||||
verify_digest::<D>(
|
||||
&self.inner,
|
||||
&D::digest(msg),
|
||||
&signature.inner,
|
||||
signature.len,
|
||||
self.salt_len,
|
||||
)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> DigestVerifier<D, Signature> for VerifyingKey<D>
|
||||
where
|
||||
D: Digest + FixedOutputReset,
|
||||
{
|
||||
fn verify_digest(&self, digest: D, signature: &Signature) -> signature::Result<()> {
|
||||
verify_digest::<D>(
|
||||
&self.inner,
|
||||
&digest.finalize(),
|
||||
&signature.inner,
|
||||
signature.len,
|
||||
self.salt_len,
|
||||
)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> PrehashVerifier<Signature> for VerifyingKey<D>
|
||||
where
|
||||
D: Digest + FixedOutputReset,
|
||||
{
|
||||
fn verify_prehash(&self, prehash: &[u8], signature: &Signature) -> signature::Result<()> {
|
||||
verify_digest::<D>(
|
||||
&self.inner,
|
||||
prehash,
|
||||
&signature.inner,
|
||||
signature.len,
|
||||
self.salt_len,
|
||||
)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> AsRef<RsaPublicKey> for VerifyingKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn as_ref(&self) -> &RsaPublicKey {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> EncodePublicKey for VerifyingKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn to_public_key_der(&self) -> pkcs8::spki::Result<Document> {
|
||||
self.inner.to_public_key_der()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::pss::{BlindedSigningKey, Pss, Signature, SigningKey, VerifyingKey};
|
||||
|
200
src/pss/blinded_signing_key.rs
Normal file
200
src/pss/blinded_signing_key.rs
Normal file
@ -0,0 +1,200 @@
|
||||
use super::{get_pss_signature_algo_id, sign_digest, Signature, VerifyingKey};
|
||||
use crate::{Result, RsaPrivateKey};
|
||||
use const_oid::AssociatedOid;
|
||||
use core::marker::PhantomData;
|
||||
use digest::{Digest, FixedOutputReset};
|
||||
use pkcs8::{
|
||||
spki::{
|
||||
der::AnyRef, AlgorithmIdentifierOwned, AlgorithmIdentifierRef,
|
||||
AssociatedAlgorithmIdentifier, DynSignatureAlgorithmIdentifier,
|
||||
},
|
||||
EncodePrivateKey, SecretDocument,
|
||||
};
|
||||
use rand_core::CryptoRngCore;
|
||||
use signature::{
|
||||
hazmat::RandomizedPrehashSigner, Keypair, RandomizedDigestSigner, RandomizedSigner,
|
||||
};
|
||||
use zeroize::ZeroizeOnDrop;
|
||||
|
||||
/// Signing key for producing "blinded" RSASSA-PSS signatures as described in
|
||||
/// [draft-irtf-cfrg-rsa-blind-signatures](https://datatracker.ietf.org/doc/draft-irtf-cfrg-rsa-blind-signatures/).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BlindedSigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
inner: RsaPrivateKey,
|
||||
salt_len: usize,
|
||||
phantom: PhantomData<D>,
|
||||
}
|
||||
|
||||
impl<D> BlindedSigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
/// Create a new RSASSA-PSS signing key which produces "blinded"
|
||||
/// signatures.
|
||||
/// Digest output size is used as a salt length.
|
||||
pub fn new(key: RsaPrivateKey) -> Self {
|
||||
Self::new_with_salt_len(key, <D as Digest>::output_size())
|
||||
}
|
||||
|
||||
/// Create a new RSASSA-PSS signing key which produces "blinded"
|
||||
/// signatures with a salt of the given length.
|
||||
pub fn new_with_salt_len(key: RsaPrivateKey, salt_len: usize) -> Self {
|
||||
Self {
|
||||
inner: key,
|
||||
salt_len,
|
||||
phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new random RSASSA-PSS signing key which produces "blinded"
|
||||
/// signatures.
|
||||
/// Digest output size is used as a salt length.
|
||||
pub fn random<R: CryptoRngCore + ?Sized>(rng: &mut R, bit_size: usize) -> Result<Self> {
|
||||
Self::random_with_salt_len(rng, bit_size, <D as Digest>::output_size())
|
||||
}
|
||||
|
||||
/// Create a new random RSASSA-PSS signing key which produces "blinded"
|
||||
/// signatures with a salt of the given length.
|
||||
pub fn random_with_salt_len<R: CryptoRngCore + ?Sized>(
|
||||
rng: &mut R,
|
||||
bit_size: usize,
|
||||
salt_len: usize,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
inner: RsaPrivateKey::new(rng, bit_size)?,
|
||||
salt_len,
|
||||
phantom: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Return specified salt length for this key
|
||||
pub fn salt_len(&self) -> usize {
|
||||
self.salt_len
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// `*Signer` trait impls
|
||||
//
|
||||
|
||||
impl<D> RandomizedSigner<Signature> for BlindedSigningKey<D>
|
||||
where
|
||||
D: Digest + FixedOutputReset,
|
||||
{
|
||||
fn try_sign_with_rng(
|
||||
&self,
|
||||
rng: &mut impl CryptoRngCore,
|
||||
msg: &[u8],
|
||||
) -> signature::Result<Signature> {
|
||||
sign_digest::<_, D>(rng, true, &self.inner, &D::digest(msg), self.salt_len)?
|
||||
.as_slice()
|
||||
.try_into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> RandomizedDigestSigner<D, Signature> for BlindedSigningKey<D>
|
||||
where
|
||||
D: Digest + FixedOutputReset,
|
||||
{
|
||||
fn try_sign_digest_with_rng(
|
||||
&self,
|
||||
rng: &mut impl CryptoRngCore,
|
||||
digest: D,
|
||||
) -> signature::Result<Signature> {
|
||||
sign_digest::<_, D>(rng, true, &self.inner, &digest.finalize(), self.salt_len)?
|
||||
.as_slice()
|
||||
.try_into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> RandomizedPrehashSigner<Signature> for BlindedSigningKey<D>
|
||||
where
|
||||
D: Digest + FixedOutputReset,
|
||||
{
|
||||
fn sign_prehash_with_rng(
|
||||
&self,
|
||||
rng: &mut impl CryptoRngCore,
|
||||
prehash: &[u8],
|
||||
) -> signature::Result<Signature> {
|
||||
sign_digest::<_, D>(rng, true, &self.inner, prehash, self.salt_len)?
|
||||
.as_slice()
|
||||
.try_into()
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Other trait impls
|
||||
//
|
||||
|
||||
impl<D> AsRef<RsaPrivateKey> for BlindedSigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn as_ref(&self) -> &RsaPrivateKey {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> AssociatedAlgorithmIdentifier for BlindedSigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
type Params = AnyRef<'static>;
|
||||
|
||||
const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID;
|
||||
}
|
||||
|
||||
impl<D> DynSignatureAlgorithmIdentifier for BlindedSigningKey<D>
|
||||
where
|
||||
D: Digest + AssociatedOid,
|
||||
{
|
||||
fn signature_algorithm_identifier(&self) -> pkcs8::spki::Result<AlgorithmIdentifierOwned> {
|
||||
get_pss_signature_algo_id::<D>(self.salt_len as u8)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> EncodePrivateKey for BlindedSigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn to_pkcs8_der(&self) -> pkcs8::Result<SecretDocument> {
|
||||
self.inner.to_pkcs8_der()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> From<RsaPrivateKey> for BlindedSigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn from(key: RsaPrivateKey) -> Self {
|
||||
Self::new(key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> From<BlindedSigningKey<D>> for RsaPrivateKey
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn from(key: BlindedSigningKey<D>) -> Self {
|
||||
key.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> Keypair for BlindedSigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
type VerifyingKey = VerifyingKey<D>;
|
||||
fn verifying_key(&self) -> Self::VerifyingKey {
|
||||
VerifyingKey {
|
||||
inner: self.inner.to_public_key(),
|
||||
salt_len: self.salt_len,
|
||||
phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> ZeroizeOnDrop for BlindedSigningKey<D> where D: Digest {}
|
65
src/pss/signature.rs
Normal file
65
src/pss/signature.rs
Normal file
@ -0,0 +1,65 @@
|
||||
pub use ::signature::{
|
||||
hazmat::{PrehashSigner, PrehashVerifier},
|
||||
DigestSigner, DigestVerifier, Error, Keypair, RandomizedDigestSigner, RandomizedSigner, Result,
|
||||
SignatureEncoding, Signer, Verifier,
|
||||
};
|
||||
|
||||
use alloc::{boxed::Box, string::ToString};
|
||||
use core::fmt::{Debug, Display, Formatter, LowerHex, UpperHex};
|
||||
use num_bigint::BigUint;
|
||||
|
||||
/// RSASSA-PSS signatures as described in [RFC8017 § 8.1].
|
||||
///
|
||||
/// [RFC8017 § 8.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.1
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct Signature {
|
||||
pub(super) inner: BigUint,
|
||||
pub(super) len: usize,
|
||||
}
|
||||
|
||||
impl SignatureEncoding for Signature {
|
||||
type Repr = Box<[u8]>;
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for Signature {
|
||||
type Error = signature::Error;
|
||||
|
||||
fn try_from(bytes: &[u8]) -> signature::Result<Self> {
|
||||
Ok(Self {
|
||||
len: bytes.len(),
|
||||
inner: BigUint::from_bytes_be(bytes),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Signature> for Box<[u8]> {
|
||||
fn from(signature: Signature) -> Box<[u8]> {
|
||||
signature.inner.to_bytes_be().into_boxed_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Signature {
|
||||
fn fmt(&self, fmt: &mut Formatter<'_>) -> core::result::Result<(), core::fmt::Error> {
|
||||
fmt.debug_tuple("Signature")
|
||||
.field(&self.to_string())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl LowerHex for Signature {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "{:x}", &self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl UpperHex for Signature {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "{:X}", &self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Signature {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "{:X}", self)
|
||||
}
|
||||
}
|
222
src/pss/signing_key.rs
Normal file
222
src/pss/signing_key.rs
Normal file
@ -0,0 +1,222 @@
|
||||
use super::{get_pss_signature_algo_id, sign_digest, Signature, VerifyingKey};
|
||||
use crate::{Result, RsaPrivateKey};
|
||||
use const_oid::AssociatedOid;
|
||||
use core::marker::PhantomData;
|
||||
use digest::{Digest, FixedOutputReset};
|
||||
use pkcs8::{
|
||||
spki::{
|
||||
der::AnyRef, AlgorithmIdentifierOwned, AlgorithmIdentifierRef,
|
||||
AssociatedAlgorithmIdentifier, DynSignatureAlgorithmIdentifier,
|
||||
},
|
||||
EncodePrivateKey, SecretDocument,
|
||||
};
|
||||
use rand_core::CryptoRngCore;
|
||||
use signature::{
|
||||
hazmat::RandomizedPrehashSigner, Keypair, RandomizedDigestSigner, RandomizedSigner,
|
||||
};
|
||||
use zeroize::ZeroizeOnDrop;
|
||||
|
||||
#[cfg(feature = "getrandom")]
|
||||
use {
|
||||
rand_core::OsRng,
|
||||
signature::{hazmat::PrehashSigner, Signer},
|
||||
};
|
||||
|
||||
/// Signing key for producing RSASSA-PSS signatures as described in
|
||||
/// [RFC8017 § 8.1].
|
||||
///
|
||||
/// [RFC8017 § 8.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.1
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
inner: RsaPrivateKey,
|
||||
salt_len: usize,
|
||||
phantom: PhantomData<D>,
|
||||
}
|
||||
|
||||
impl<D> SigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
/// Create a new RSASSA-PSS signing key.
|
||||
/// Digest output size is used as a salt length.
|
||||
pub fn new(key: RsaPrivateKey) -> Self {
|
||||
Self::new_with_salt_len(key, <D as Digest>::output_size())
|
||||
}
|
||||
|
||||
/// Create a new RSASSA-PSS signing key with a salt of the given length.
|
||||
pub fn new_with_salt_len(key: RsaPrivateKey, salt_len: usize) -> Self {
|
||||
Self {
|
||||
inner: key,
|
||||
salt_len,
|
||||
phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a new random RSASSA-PSS signing key.
|
||||
/// Digest output size is used as a salt length.
|
||||
pub fn random<R: CryptoRngCore + ?Sized>(rng: &mut R, bit_size: usize) -> Result<Self> {
|
||||
Self::random_with_salt_len(rng, bit_size, <D as Digest>::output_size())
|
||||
}
|
||||
|
||||
/// Generate a new random RSASSA-PSS signing key with a salt of the given length.
|
||||
pub fn random_with_salt_len<R: CryptoRngCore + ?Sized>(
|
||||
rng: &mut R,
|
||||
bit_size: usize,
|
||||
salt_len: usize,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
inner: RsaPrivateKey::new(rng, bit_size)?,
|
||||
salt_len,
|
||||
phantom: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Return specified salt length for this key
|
||||
pub fn salt_len(&self) -> usize {
|
||||
self.salt_len
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// `*Signer` trait impls
|
||||
//
|
||||
|
||||
impl<D> RandomizedDigestSigner<D, Signature> for SigningKey<D>
|
||||
where
|
||||
D: Digest + FixedOutputReset,
|
||||
{
|
||||
fn try_sign_digest_with_rng(
|
||||
&self,
|
||||
rng: &mut impl CryptoRngCore,
|
||||
digest: D,
|
||||
) -> signature::Result<Signature> {
|
||||
sign_digest::<_, D>(rng, false, &self.inner, &digest.finalize(), self.salt_len)?
|
||||
.as_slice()
|
||||
.try_into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> RandomizedSigner<Signature> for SigningKey<D>
|
||||
where
|
||||
D: Digest + FixedOutputReset,
|
||||
{
|
||||
fn try_sign_with_rng(
|
||||
&self,
|
||||
rng: &mut impl CryptoRngCore,
|
||||
msg: &[u8],
|
||||
) -> signature::Result<Signature> {
|
||||
self.try_sign_digest_with_rng(rng, D::new_with_prefix(msg))
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> RandomizedPrehashSigner<Signature> for SigningKey<D>
|
||||
where
|
||||
D: Digest + FixedOutputReset,
|
||||
{
|
||||
fn sign_prehash_with_rng(
|
||||
&self,
|
||||
rng: &mut impl CryptoRngCore,
|
||||
prehash: &[u8],
|
||||
) -> signature::Result<Signature> {
|
||||
sign_digest::<_, D>(rng, false, &self.inner, prehash, self.salt_len)?
|
||||
.as_slice()
|
||||
.try_into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "getrandom")]
|
||||
impl<D> PrehashSigner<Signature> for SigningKey<D>
|
||||
where
|
||||
D: Digest + FixedOutputReset,
|
||||
{
|
||||
fn sign_prehash(&self, prehash: &[u8]) -> signature::Result<Signature> {
|
||||
self.sign_prehash_with_rng(&mut OsRng, prehash)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "getrandom")]
|
||||
impl<D> Signer<Signature> for SigningKey<D>
|
||||
where
|
||||
D: Digest + FixedOutputReset,
|
||||
{
|
||||
fn try_sign(&self, msg: &[u8]) -> signature::Result<Signature> {
|
||||
self.try_sign_with_rng(&mut OsRng, msg)
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Other trait impls
|
||||
//
|
||||
|
||||
impl<D> AsRef<RsaPrivateKey> for SigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn as_ref(&self) -> &RsaPrivateKey {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> AssociatedAlgorithmIdentifier for SigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
type Params = AnyRef<'static>;
|
||||
|
||||
const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID;
|
||||
}
|
||||
|
||||
impl<D> DynSignatureAlgorithmIdentifier for SigningKey<D>
|
||||
where
|
||||
D: Digest + AssociatedOid,
|
||||
{
|
||||
fn signature_algorithm_identifier(&self) -> pkcs8::spki::Result<AlgorithmIdentifierOwned> {
|
||||
get_pss_signature_algo_id::<D>(self.salt_len as u8)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> EncodePrivateKey for SigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn to_pkcs8_der(&self) -> pkcs8::Result<SecretDocument> {
|
||||
self.inner.to_pkcs8_der()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> From<RsaPrivateKey> for SigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn from(key: RsaPrivateKey) -> Self {
|
||||
Self::new(key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> From<SigningKey<D>> for RsaPrivateKey
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn from(key: SigningKey<D>) -> Self {
|
||||
key.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> Keypair for SigningKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
type VerifyingKey = VerifyingKey<D>;
|
||||
fn verifying_key(&self) -> Self::VerifyingKey {
|
||||
VerifyingKey {
|
||||
inner: self.inner.to_public_key(),
|
||||
salt_len: self.salt_len,
|
||||
phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> ZeroizeOnDrop for SigningKey<D> where D: Digest {}
|
158
src/pss/verifying_key.rs
Normal file
158
src/pss/verifying_key.rs
Normal file
@ -0,0 +1,158 @@
|
||||
use super::{verify_digest, Signature};
|
||||
use crate::RsaPublicKey;
|
||||
use core::marker::PhantomData;
|
||||
use digest::{Digest, FixedOutputReset};
|
||||
use pkcs8::{
|
||||
spki::{der::AnyRef, AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier},
|
||||
Document, EncodePublicKey,
|
||||
};
|
||||
use signature::{hazmat::PrehashVerifier, DigestVerifier, Verifier};
|
||||
|
||||
/// Verifying key for checking the validity of RSASSA-PSS signatures as
|
||||
/// described in [RFC8017 § 8.1].
|
||||
///
|
||||
/// [RFC8017 § 8.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.1
|
||||
#[derive(Debug)]
|
||||
pub struct VerifyingKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
pub(super) inner: RsaPublicKey,
|
||||
pub(super) salt_len: usize,
|
||||
pub(super) phantom: PhantomData<D>,
|
||||
}
|
||||
|
||||
impl<D> VerifyingKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
/// Create a new RSASSA-PSS verifying key.
|
||||
/// Digest output size is used as a salt length.
|
||||
pub fn new(key: RsaPublicKey) -> Self {
|
||||
Self::new_with_salt_len(key, <D as Digest>::output_size())
|
||||
}
|
||||
|
||||
/// Create a new RSASSA-PSS verifying key.
|
||||
pub fn new_with_salt_len(key: RsaPublicKey, salt_len: usize) -> Self {
|
||||
Self {
|
||||
inner: key,
|
||||
salt_len,
|
||||
phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// `*Verifier` trait impls
|
||||
//
|
||||
|
||||
impl<D> DigestVerifier<D, Signature> for VerifyingKey<D>
|
||||
where
|
||||
D: Digest + FixedOutputReset,
|
||||
{
|
||||
fn verify_digest(&self, digest: D, signature: &Signature) -> signature::Result<()> {
|
||||
verify_digest::<D>(
|
||||
&self.inner,
|
||||
&digest.finalize(),
|
||||
&signature.inner,
|
||||
signature.len,
|
||||
self.salt_len,
|
||||
)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> PrehashVerifier<Signature> for VerifyingKey<D>
|
||||
where
|
||||
D: Digest + FixedOutputReset,
|
||||
{
|
||||
fn verify_prehash(&self, prehash: &[u8], signature: &Signature) -> signature::Result<()> {
|
||||
verify_digest::<D>(
|
||||
&self.inner,
|
||||
prehash,
|
||||
&signature.inner,
|
||||
signature.len,
|
||||
self.salt_len,
|
||||
)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> Verifier<Signature> for VerifyingKey<D>
|
||||
where
|
||||
D: Digest + FixedOutputReset,
|
||||
{
|
||||
fn verify(&self, msg: &[u8], signature: &Signature) -> signature::Result<()> {
|
||||
verify_digest::<D>(
|
||||
&self.inner,
|
||||
&D::digest(msg),
|
||||
&signature.inner,
|
||||
signature.len,
|
||||
self.salt_len,
|
||||
)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Other trait impls
|
||||
//
|
||||
|
||||
impl<D> AsRef<RsaPublicKey> for VerifyingKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn as_ref(&self) -> &RsaPublicKey {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> AssociatedAlgorithmIdentifier for VerifyingKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
type Params = AnyRef<'static>;
|
||||
|
||||
const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID;
|
||||
}
|
||||
|
||||
// Implemented manually so we don't have to bind D with Clone
|
||||
impl<D> Clone for VerifyingKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
inner: self.inner.clone(),
|
||||
salt_len: self.salt_len,
|
||||
phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> EncodePublicKey for VerifyingKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn to_public_key_der(&self) -> pkcs8::spki::Result<Document> {
|
||||
self.inner.to_public_key_der()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> From<RsaPublicKey> for VerifyingKey<D>
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn from(key: RsaPublicKey) -> Self {
|
||||
Self::new(key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> From<VerifyingKey<D>> for RsaPublicKey
|
||||
where
|
||||
D: Digest,
|
||||
{
|
||||
fn from(key: VerifyingKey<D>) -> Self {
|
||||
key.inner
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user