Skip to content

New API design #34

@str4d

Description

@str4d

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 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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions