Prompted by https://github.com/RustCrypto/signatures/issues/25#issuecomment-525248346 and my desire to get #18 and #26 merged :smile:
The main design difference compared to the current traits is that instead of making the "padding scheme" a parameter of the pubkey sign/verify functions, we make each scheme a first-class primitive that wraps the common public or private key. It needs to be an explicit choice by the user anyway.
Current draft proposal (as of 2020-03-07) (without changes to the signature
crate, and without handling the encryption cases):
// This module has been merged into master
mod raw {
pub trait EncryptionPrimitive {
/// Do NOT use directly! Only for implementors.
fn raw_encryption_primitive(&self, plaintext: &[u8]) -> Result<Vec<u8>>;
}
pub trait DecryptionPrimitive {
/// Do NOT use directly! Only for implementors.
fn raw_decryption_primitive<R: Rng>(
&self,
rng: Option<&mut R>,
ciphertext: &[u8],
) -> Result<Vec<u8>>;
}
}
mod key {
pub trait PublicKeyParts {
/// Returns the modulus of the key.
fn n(&self) -> &BigUint;
/// Returns the public exponent of the key.
fn e(&self) -> &BigUint;
/// Returns the modulus size in bytes. Raw signatures and ciphertexts for
/// or by this public key will have the same size.
fn size(&self) -> usize {
(self.n().bits() + 7) / 8
}
}
pub trait PrivateKey: crate::raw::DecryptionPrimitive + PublicKeyParts {
/// Could have functions like this for usability?
pub fn sign_pkcs1v15(&self) -> crate::pkcs1v15::Signer;
pub fn sign_pkcs1v15_blinded<R: Rng>(&self, rng: R) -> crate::pkcs1v15::Signer;
pub fn sign_pss(&self) -> crate::pss::Signer;
pub fn sign_pss_blinded(&self) -> crate::pss::Signer;
}
pub trait PublicKey: crate::raw::EncryptionPrimitive + PublicKeyParts {
/// Could have functions like this for usability?
pub fn verify_pkcs1v15(&self) -> crate::pkcs1v15::Verifier;
pub fn verify_pss(&self) -> crate::pss::Verifier;
}
pub struct RSAPrivateKey { ... }
impl crate::raw::DecryptionPrimitive for RSAPrivateKey { ... }
impl PublicKeyParts for RSAPrivateKey { ... }
impl PrivateKey for RSAPrivateKey {
pub fn sign_pkcs1v15(&self) -> crate::pkcs1v15::Signer {
crate::pkcs1v15::Signer::unblinded(self)
}
pub fn sign_pkcs1v15_blinded<R: Rng>(&self, rng: R) -> crate::pkcs1v15::Signer {
crate::pkcs1v15::Signer::blinded(rng, self)
}
pub fn sign_pss(&self) -> crate::pss::Signer {
crate::pss::Signer::unblinded(self)
}
pub fn sign_pss_blinded(&self) -> crate::pss::Signer {
crate::pss::Signer::blinded(self)
}
}
pub struct RSAPublicKey { ... }
impl crate::raw::EncryptionPrimitive for RSAPublicKey { ... }
impl PublicKeyParts for RSAPublicKey { ... }
impl PublicKey for RSAPublicKey {
pub fn verify_pkcs1v15(&self) -> crate::pkcs1v15::Verifier {
crate::pkcs1v15::Verifier::new(self)
}
pub fn verify_pss(&self) -> crate::pss::Verifier {
crate::pss::Verifier::new(self)
}
}
}
/// PKCS#1 v1.5 signing (and encryption?)
mod pkcs1v15 {
use signature::Error;
use crate::{
key::{PrivateKey, PublicKey},
raw::DecryptionPrimitive,
};
pub struct Signature {
bytes: Vec<u8>,
}
impl signature::Signature for Signature {
fn from_bytes(bytes: impl AsRef<[u8]>) -> Result<Self, Error> {
// Parse a PKCS#1 v1.5 signature here non-contextually
// (i.e. length can't be verified as we don't know n)
}
}
// Technically all we need is K: DecryptionPrimitive, but PrivateKey
// is the correct user-level encapsulation, and we should keep
// DecryptionPrimitive internal as much as possible.
pub struct Signer<'a, R: Rng, K: PrivateKey> {
// RefCell in lieu of a stateful or randomizable signature trait
rng: Option<RefCell<R>>,
priv_key: &'a K,
}
impl<'a, R: Rng, K: PrivateKey> Signer<'a, R, K> {
pub fn unblinded(priv_key: &'a K) -> Self {
Signer { rng: None, priv_key }
}
pub fn blinded(rng: R, priv_key: &'a K) -> Self {
Signer { rng: Some(RefCell::new(rng)), priv_key }
}
}
impl<'a, R: Rng, K: PrivateKey> signature::Signer<Signature> for Signer<'a, R, K> {
fn try_sign(&self, msg: &[u8]) -> Result<Signature, Error> {
// Sign the message directly (equivalent to current None case)
}
}
impl<'a, R: Rng, K: PrivateKey> signature::DigestSigner<D: Digest, Signature> for Signer<'a, R, K> {
fn try_sign_digest(&self, digest: D) -> Result<Signature, Error> {
// Sign the digest (equivalent to current Some(Hash) case)
}
}
pub struct Verifier<'a, PK: PublicKey> {
pub_key: &'a PK,
}
impl<'a, PK: PublicKey> Verifier<'a, PK> {
pub fn new(pub_key: &'a PK) -> Self {
Verifier { pub_key }
}
}
impl<'a, PK: PublicKey> signature::Verifier<Signature> for Verifier<'a, PK> {
fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), Error> {
// Verify the message directly (equivalent to current None case)
}
}
impl<'a, PK: PublicKey> signature::DigestVerifier<D: Digest, Signature> for Verifier<'a, PK> {
fn verify_digest(&self, digest: D, signature: &Signature) -> Result<(), Error> {
// Verify the digest (equivalent to current Some(Hash) case)
}
}
}
/// PSS signing
mod pss {
use signature::Error;
use crate::{
key::{PrivateKey, PublicKey},
raw::DecryptionPrimitive,
};
pub struct Signature {
bytes: Vec<u8>,
}
impl signature::Signature for Signature {
fn from_bytes(bytes: impl AsRef<[u8]>) -> Result<Self, Error> {
// Parse a PSS signature here non-contextually
// (i.e. length can't be verified as we don't know n)
}
}
pub struct Signer<'a, R: Rng, K: PrivateKey> {
// RefCell in lieu of a stateful or randomizable signature trait
rng: RefCell<R>,
priv_key: &'a K,
salt_len: Option<usize>,
blind: bool
}
impl<'a, R: Rng, K: PrivateKey> Signer<'a, R, K> {
pub fn unblinded(rng: R, priv_key: &'a K, salt_len: Option<usize>) -> Self {
Signer { rng: RefCell::new(rng), priv_key, salt_len, blind: false }
}
pub fn blinded(rng: R, priv_key: &'a K, salt_len: Option<usize>) -> Self {
Signer { rng: RefCell::new(rng), priv_key, salt_len, blind: true }
}
}
impl<'a, R: Rng, K: PrivateKey> signature::DigestSigner<D: Digest, Signature> for Signer<'a, R, K> {
fn try_sign_digest(&self, digest: D) -> Result<Signature, Error> { ... }
}
pub struct Verifier<'a, PK: PublicKey> {
pub_key: &'a PK,
}
impl<'a, PK: PublicKey> Verifier<'a, PK> {
pub fn new(pub_key: &'a PK) -> Self {
Verifier { pub_key }
}
}
impl<'a, PK: PublicKey> signature::DigestVerifier<D: Digest, Signature> for Verifier<'a, PK> {
fn verify_digest(&self, digest: D, signature: &Signature) -> Result<(), Error> { ... }
}
}
// Do these improve usability?
pub use pkcs1v15::{
Signature as Pkcs1v15Signature,
Signer as Pkcs1v15Signer,
Verifier as Pkcs1v15Verifier,
};
pub use pss::{
Signature as PssSignature,
Signer as PssSigner,
Verifier as PssVerifier,
};
I'll update the proposal in this post as we discuss it.
discussion api