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.
// This module has been merged into mastermod raw {pubtraitEncryptionPrimitive{/// Do NOT use directly! Only for implementors.fnraw_encryption_primitive(&self,plaintext:&[u8]) -> Result<Vec<u8>>;}pubtraitDecryptionPrimitive{/// Do NOT use directly! Only for implementors.fnraw_decryption_primitive<R:Rng>(&self,rng:Option<&mutR>,ciphertext:&[u8],) -> Result<Vec<u8>>;}}mod key {pubtraitPublicKeyParts{/// Returns the modulus of the key.fnn(&self) -> &BigUint;/// Returns the public exponent of the key.fne(&self) -> &BigUint;/// Returns the modulus size in bytes. Raw signatures and ciphertexts for/// or by this public key will have the same size.fnsize(&self) -> usize{(self.n().bits() + 7) / 8}}pubtraitPrivateKey:crate::raw::DecryptionPrimitive + PublicKeyParts{/// Could have functions like this for usability?pubfnsign_pkcs1v15(&self) -> crate::pkcs1v15::Signer;pubfnsign_pkcs1v15_blinded<R:Rng>(&self,rng:R) -> crate::pkcs1v15::Signer;pubfnsign_pss(&self) -> crate::pss::Signer;pubfnsign_pss_blinded(&self) -> crate::pss::Signer;}pubtraitPublicKey:crate::raw::EncryptionPrimitive + PublicKeyParts{/// Could have functions like this for usability?pubfnverify_pkcs1v15(&self) -> crate::pkcs1v15::Verifier;pubfnverify_pss(&self) -> crate::pss::Verifier;}pubstructRSAPrivateKey{ ... }implcrate::raw::DecryptionPrimitiveforRSAPrivateKey{ ... }implPublicKeyPartsforRSAPrivateKey{ ... }implPrivateKeyforRSAPrivateKey{pubfnsign_pkcs1v15(&self) -> crate::pkcs1v15::Signer{crate::pkcs1v15::Signer::unblinded(self)}pubfnsign_pkcs1v15_blinded<R:Rng>(&self,rng:R) -> crate::pkcs1v15::Signer{crate::pkcs1v15::Signer::blinded(rng,self)}pubfnsign_pss(&self) -> crate::pss::Signer{crate::pss::Signer::unblinded(self)}pubfnsign_pss_blinded(&self) -> crate::pss::Signer{crate::pss::Signer::blinded(self)}}pubstructRSAPublicKey{ ... }implcrate::raw::EncryptionPrimitiveforRSAPublicKey{ ... }implPublicKeyPartsforRSAPublicKey{ ... }implPublicKeyforRSAPublicKey{pubfnverify_pkcs1v15(&self) -> crate::pkcs1v15::Verifier{crate::pkcs1v15::Verifier::new(self)}pubfnverify_pss(&self) -> crate::pss::Verifier{crate::pss::Verifier::new(self)}}}/// PKCS#1 v1.5 signing (and encryption?)mod pkcs1v15 {use signature::Error;usecrate::{
key::{PrivateKey,PublicKey},
raw::DecryptionPrimitive,};pubstructSignature{bytes:Vec<u8>,}impl signature::SignatureforSignature{fnfrom_bytes(bytes:implAsRef<[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.pubstructSigner<'a,R:Rng,K:PrivateKey>{// RefCell in lieu of a stateful or randomizable signature traitrng:Option<RefCell<R>>,priv_key:&'aK,}impl<'a,R:Rng,K:PrivateKey>Signer<'a,R,K>{pubfnunblinded(priv_key:&'aK) -> Self{Signer{rng:None, priv_key }}pubfnblinded(rng:R,priv_key:&'aK) -> Self{Signer{rng:Some(RefCell::new(rng)), priv_key }}}impl<'a,R:Rng,K:PrivateKey> signature::Signer<Signature>forSigner<'a,R,K>{fntry_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>forSigner<'a,R,K>{fntry_sign_digest(&self,digest:D) -> Result<Signature,Error>{// Sign the digest (equivalent to current Some(Hash) case)}}pubstructVerifier<'a,PK:PublicKey>{pub_key:&'aPK,}impl<'a,PK:PublicKey>Verifier<'a,PK>{pubfnnew(pub_key:&'aPK) -> Self{Verifier{ pub_key }}}impl<'a,PK:PublicKey> signature::Verifier<Signature>forVerifier<'a,PK>{fnverify(&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>forVerifier<'a,PK>{fnverify_digest(&self,digest:D,signature:&Signature) -> Result<(),Error>{// Verify the digest (equivalent to current Some(Hash) case)}}}/// PSS signingmod pss {use signature::Error;usecrate::{
key::{PrivateKey,PublicKey},
raw::DecryptionPrimitive,};pubstructSignature{bytes:Vec<u8>,}impl signature::SignatureforSignature{fnfrom_bytes(bytes:implAsRef<[u8]>) -> Result<Self,Error>{// Parse a PSS signature here non-contextually// (i.e. length can't be verified as we don't know n)}}pubstructSigner<'a,R:Rng,K:PrivateKey>{// RefCell in lieu of a stateful or randomizable signature traitrng:RefCell<R>,priv_key:&'aK,salt_len:Option<usize>,blind:bool}impl<'a,R:Rng,K:PrivateKey>Signer<'a,R,K>{pubfnunblinded(rng:R,priv_key:&'aK,salt_len:Option<usize>) -> Self{Signer{rng:RefCell::new(rng), priv_key, salt_len,blind:false}}pubfnblinded(rng:R,priv_key:&'aK,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>forSigner<'a,R,K>{fntry_sign_digest(&self,digest:D) -> Result<Signature,Error>{ ...}}pubstructVerifier<'a,PK:PublicKey>{pub_key:&'aPK,}impl<'a,PK:PublicKey>Verifier<'a,PK>{pubfnnew(pub_key:&'aPK) -> Self{Verifier{ pub_key }}}impl<'a,PK:PublicKey> signature::DigestVerifier<D:Digest,Signature>forVerifier<'a,PK>{fnverify_digest(&self,digest:D,signature:&Signature) -> Result<(),Error>{ ...}}}// Do these improve usability?pubuse pkcs1v15::{SignatureasPkcs1v15Signature,SignerasPkcs1v15Signer,VerifierasPkcs1v15Verifier,};pubuse pss::{SignatureasPssSignature,SignerasPssSigner,VerifierasPssVerifier,};I'll update the proposal in this post as we discuss it.
Prompted by RustCrypto/signatures#25 (comment) and my desire to get #18 and #26 merged 😄
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
signaturecrate, and without handling the encryption cases):I'll update the proposal in this post as we discuss it.