rsa_fdh/
common.rs

1use digest::{Digest, Update};
2use fdh::{FullDomainHash, VariableOutput};
3use num_bigint::BigUint;
4use rand::Rng;
5use rsa::errors::Error as RSAError;
6use rsa::internals;
7use rsa::{PublicKey, PublicKeyParts, RSAPrivateKey};
8use subtle::ConstantTimeEq;
9use thiserror::Error;
10
11/// Error types
12#[derive(Debug, Error)]
13pub enum Error {
14    #[error("rsa-fdh: digest big-endian numeric value is too large")]
15    DigestTooLarge,
16    #[error("rsa-fdh: digest is incorrectly sized")]
17    DigestIncorrectSize,
18    #[error("rsa-fdh: verification failed")]
19    Verification,
20    #[error("rsa-fdh: public key modulus is too large")]
21    ModulusTooLarge,
22    #[error("rsa-fdh: rsa error: {}", 0)]
23    RSAError(RSAError),
24}
25
26/// Hash the message using a full-domain-hash, returning both the digest and the initialization vector.
27pub fn hash_message<H: Digest + Clone, P: PublicKey>(
28    signer_public_key: &P,
29    message: &[u8],
30) -> Result<(Vec<u8>, u8), Error>
31where
32    H::OutputSize: Clone,
33{
34    let size = signer_public_key.size();
35    let mut hasher = FullDomainHash::<H>::new(size).unwrap(); // will never panic.
36    hasher.update(message);
37
38    // Append modulus n to the message before hashing
39    let append = signer_public_key.n().to_bytes_be();
40    hasher.update(append);
41
42    let iv: u8 = 0;
43    let zero = BigUint::from(0u8);
44    let (digest, iv) = hasher
45        .results_between(
46            iv,
47            &zero,
48            &BigUint::from_bytes_be(&signer_public_key.n().to_bytes_be()),
49        )
50        .map_err(|_| Error::ModulusTooLarge)?;
51
52    Ok((digest, iv))
53}
54
55/// Verifies a signature after it has been unblinded.
56pub fn verify_hashed<K: PublicKey>(pub_key: &K, hashed: &[u8], sig: &[u8]) -> Result<(), Error> {
57    if hashed.len() != pub_key.size() {
58        return Err(Error::Verification);
59    }
60
61    let n = BigUint::from_bytes_be(&pub_key.n().to_bytes_be());
62
63    let m = BigUint::from_bytes_be(&hashed);
64    if m >= n {
65        return Err(Error::Verification);
66    }
67
68    let c = BigUint::from_bytes_be(sig);
69    let mut m = internals::encrypt(pub_key, &c).to_bytes_be();
70    if m.len() < hashed.len() {
71        m = left_pad(&m, hashed.len());
72    }
73
74    // Constant time compare message with hashed
75    let ok = m.ct_eq(&hashed);
76
77    if ok.unwrap_u8() != 1 {
78        return Err(Error::Verification);
79    }
80
81    Ok(())
82}
83
84/// Sign the given blinded digest.
85pub fn sign_hashed<R: Rng>(
86    rng: &mut R,
87    priv_key: &RSAPrivateKey,
88    hashed: &[u8],
89) -> Result<Vec<u8>, Error> {
90    if priv_key.size() < hashed.len() {
91        return Err(Error::DigestIncorrectSize);
92    }
93
94    let n = priv_key.n();
95    let m = BigUint::from_bytes_be(&hashed);
96
97    if m >= *n {
98        return Err(Error::DigestTooLarge);
99    }
100
101    let c = internals::decrypt_and_check(Some(rng), priv_key, &m)
102        .map_err(Error::RSAError)?
103        .to_bytes_be();
104
105    Ok(c)
106}
107
108// Returns a new vector of the given length, with 0s left padded.
109pub fn left_pad(input: &[u8], size: usize) -> Vec<u8> {
110    let n = if input.len() > size {
111        size
112    } else {
113        input.len()
114    };
115
116    let mut out = vec![0u8; size];
117    out[size - n..].copy_from_slice(input);
118    out
119}