rsa_fdh/
lib.rs

1//! RSA-FDH is a is provably secure blind-signing signature scheme that uses RSA and a full domain hash.
2//!
3//! This crate implements two RSA-FDH signature schemes:
4//!
5//! 1. A regular signature scheme with Full Domain Hash (FDH) padding.
6//!
7//! 2. A blind signature scheme that that supports blind-signing to keep the message being signed secret from the signer.
8//!
9//! ### Regular signature scheme example
10//!
11//! ```
12//! use rsa::{RSAPrivateKey, RSAPublicKey};
13//! use sha2::{Sha256, Digest};
14//!
15//! // Set up rng and message
16//! let mut rng = rand::thread_rng();;
17//! let message = b"NEVER GOING TO GIVE YOU UP";
18//!
19//! // Create the keys
20//! let signer_priv_key = RSAPrivateKey::new(&mut rng, 2048).unwrap();
21//! let signer_pub_key: RSAPublicKey = signer_priv_key.clone().into();
22//!
23//! // Apply a standard digest to the message
24//! let mut hasher = Sha256::new();
25//! hasher.update(message);
26//! let digest = hasher.finalize();
27//!
28//! // Obtain a signture
29//! let signature = rsa_fdh::sign::<Sha256, _>(&mut rng, &signer_priv_key, &digest).unwrap();
30//!
31//! // Verify the signature
32//! let ok = rsa_fdh::verify::<Sha256, _>(&signer_pub_key, &digest, &signature);
33//! assert!(ok.is_ok());
34//! ```
35
36use rand::Rng;
37use rsa::{PublicKey, RSAPrivateKey, RSAPublicKey};
38
39pub mod blind;
40mod common;
41
42pub use common::Error;
43
44/// Sign a message.
45///
46/// Generally the message should be hashed by the requester before being sent to the signer.
47/// The signer will apply RSA-FDH padding before singing the message.
48/// The resulting signature is not a blind signature.
49pub fn sign<H: digest::Digest + Clone, R: Rng>(
50    rng: &mut R,
51    priv_key: &RSAPrivateKey,
52    message: &[u8],
53) -> Result<Vec<u8>, Error>
54where
55    H::OutputSize: Clone,
56{
57    let public_key = priv_key.to_public_key();
58    let (hashed, _iv) = common::hash_message::<H, RSAPublicKey>(&public_key, message)?;
59
60    common::sign_hashed(rng, priv_key, &hashed)
61}
62
63/// Verify a signature.
64///
65/// Generally the message should be hashed before verifying the digest against the provided signature.
66pub fn verify<H: digest::Digest + Clone, K: PublicKey>(
67    pub_key: &K,
68    message: &[u8],
69    sig: &[u8],
70) -> Result<(), Error>
71where
72    H::OutputSize: Clone,
73{
74    // Apply FDH
75    let (hashed, _iv) = common::hash_message::<H, K>(pub_key, message)?;
76
77    common::verify_hashed(pub_key, &hashed, sig)
78}
79
80#[cfg(test)]
81mod tests {
82    use crate as rsa_fdh;
83    use rsa::RSAPrivateKey;
84    use sha2::{Digest, Sha256};
85
86    #[test]
87    fn regular_test() -> Result<(), rsa_fdh::Error> {
88        // Stage 1: Setup
89        // --------------
90        let mut rng = rand::thread_rng();
91        let message = b"NEVER GOING TO GIVE YOU UP";
92
93        // Hash the message normally
94        let mut hasher = Sha256::new();
95        hasher.update(message);
96        let digest = hasher.finalize();
97
98        // Create the keys
99        let signer_priv_key = RSAPrivateKey::new(&mut rng, 256).unwrap();
100        let signer_pub_key = signer_priv_key.to_public_key();
101
102        // Do this a bunch so that we get a good sampling of possibe digests.
103        for _ in 0..500 {
104            let signature = rsa_fdh::sign::<Sha256, _>(&mut rng, &signer_priv_key, &digest)?;
105            rsa_fdh::verify::<Sha256, _>(&signer_pub_key, &digest, &signature)?;
106        }
107
108        Ok(())
109    }
110
111    #[test]
112    fn error_test() -> Result<(), rsa_fdh::Error> {
113        let mut rng = rand::thread_rng();
114        let message = b"NEVER GOING TO GIVE YOU UP";
115
116        // Hash the message normally
117        let mut hasher = Sha256::new();
118        hasher.update(message);
119        let digest = hasher.finalize();
120
121        // Create the keys
122        let key_1 = RSAPrivateKey::new(&mut rng, 256).unwrap();
123        let public_1 = key_1.to_public_key();
124        let signature_1 = rsa_fdh::sign::<Sha256, _>(&mut rng, &key_1, &digest)?;
125
126        let key_2 = RSAPrivateKey::new(&mut rng, 512).unwrap();
127        let public_2 = key_1.to_public_key();
128        let signature_2 = rsa_fdh::sign::<Sha256, _>(&mut rng, &key_2, &digest)?;
129
130        // Assert that signatures are different
131        assert!(signature_1 != signature_2);
132
133        // Assert that they don't cross validate
134        assert!(rsa_fdh::verify::<Sha256, _>(&public_1, &signature_2, &digest).is_err());
135        assert!(rsa_fdh::verify::<Sha256, _>(&public_2, &signature_1, &digest).is_err());
136
137        Ok(())
138    }
139}