Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 28 additions & 12 deletions src/algorithms/pad.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,39 @@ fn be_pad_into(bytes: &[u8], out: &mut [u8]) {
out[start..].copy_from_slice(tail);
}

/// Converts input to the new vector of the given length, using BE and with 0s left padded.
/// Integer-to-Octet-String Primitive (I2OSP) from [RFC8017 §4.1].
///
/// `input` must fit in `padded_len` bytes.
/// Converts `input` to exactly `padded_len` big-endian bytes, left-padded with zeros.
/// `input` must fit in `padded_len` bytes. This is a caller-established precondition, checked by a
/// debug assertion.
///
/// Use [`i2osp_zeroizing`] when encoding secret values.
///
/// [RFC8017 §4.1]: https://www.rfc-editor.org/rfc/rfc8017.html#section-4.1
#[inline]
pub(crate) fn uint_to_be_pad(input: BoxedUint, padded_len: usize) -> Vec<u8> {
pub(crate) fn i2osp(input: BoxedUint, padded_len: usize) -> Vec<u8> {
let mut out = vec![0u8; padded_len];
be_pad_into(&input.to_be_bytes(), &mut out);
out
}

/// Converts input to the new vector of the given length, using BE and with 0s left padded.
/// Computes [`i2osp`] (Integer-to-Octet-String Primitive) with zeroization of the input and output
/// buffers.
///
/// The input integer, temporary byte buffer, and returned vector are each zeroized on drop.
/// The same input-width precondition as [`i2osp`] applies.
///
/// For RSA decryption, this computes `EM = I2OSP(m, k)`, where `padded_len` is `k`, the public
/// modulus length in octets.
///
/// `input` must fit in `padded_len` bytes.
/// [draft-irtf-cfrg-rsa-guidance-10 §7.2] requires this conversion to be constant-time with respect
/// to `m`. Serialization and copying in this function depend only on `padded_len` and the input's
/// storage precision. To satisfy that requirement, callers must ensure that both widths are
/// independent of `m`'s value.
///
/// For secret values: every intermediate buffer, including the returned one, is zeroized on drop.
/// [draft-irtf-cfrg-rsa-guidance-10 §7.2]: https://www.ietf.org/archive/id/draft-irtf-cfrg-rsa-guidance-10.html#section-7.2
#[inline]
pub(crate) fn uint_to_zeroizing_be_pad(input: BoxedUint, padded_len: usize) -> Zeroizing<Vec<u8>> {
pub(crate) fn i2osp_zeroizing(input: BoxedUint, padded_len: usize) -> Zeroizing<Vec<u8>> {
let input = Zeroizing::new(input);
let bytes = Zeroizing::new(input.to_be_bytes());
let mut out = Zeroizing::new(vec![0u8; padded_len]);
Expand All @@ -55,24 +71,24 @@ mod tests {
#[test]
fn be_pad() {
let x = BoxedUint::from(0x0102u64);
let p = uint_to_be_pad(x.clone(), 16);
let p = i2osp(x.clone(), 16);
assert_eq!(&p[..14], &[0u8; 14]);
assert_eq!(&p[14..], &[1, 2]);

let p = uint_to_zeroizing_be_pad(x.clone(), 3);
let p = i2osp_zeroizing(x.clone(), 3);
assert_eq!(&p[..], &[0, 1, 2]);

let p = uint_to_be_pad(x.clone(), 8);
let p = i2osp(x.clone(), 8);
assert_eq!(&p[..], &[0, 0, 0, 0, 0, 0, 1, 2]);

let p = uint_to_be_pad(BoxedUint::zero_with_precision(128), 4);
let p = i2osp(BoxedUint::zero_with_precision(128), 4);
assert_eq!(&p[..], &[0u8; 4]);
}

#[cfg(debug_assertions)]
#[test]
#[should_panic(expected = "integer exceeds output width")]
fn be_pad_rejects_broken_precondition() {
uint_to_zeroizing_be_pad(BoxedUint::from(0x0102u64), 1);
i2osp_zeroizing(BoxedUint::from(0x0102u64), 1);
}
}
10 changes: 5 additions & 5 deletions src/oaep.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use digest::{Digest, FixedOutputReset};
use rand_core::TryCryptoRng;

use crate::algorithms::oaep::*;
use crate::algorithms::pad::{uint_to_be_pad, uint_to_zeroizing_be_pad};
use crate::algorithms::pad::{i2osp, i2osp_zeroizing};
use crate::algorithms::rsa::{rsa_decrypt_and_check, rsa_encrypt};
use crate::errors::{Error, Result};
use crate::key::{self, RsaPrivateKey, RsaPublicKey};
Expand Down Expand Up @@ -215,7 +215,7 @@ where
let em = oaep_encrypt(rng, msg, digest, mgf_digest, label, pub_key.size())?;

let int = BoxedUint::from_be_slice(&em, pub_key.n_bits_precision())?;
Ok(uint_to_be_pad(rsa_encrypt(pub_key, &int)?, pub_key.size()))
Ok(i2osp(rsa_encrypt(pub_key, &int)?, pub_key.size()))
}

/// Encrypts the given message with RSA and the padding scheme from
Expand All @@ -241,7 +241,7 @@ where
let em = oaep_encrypt_digest::<_, D, MGD>(rng, msg, label, pub_key.size())?;

let int = BoxedUint::from_be_slice(&em, pub_key.n_bits_precision())?;
Ok(uint_to_be_pad(rsa_encrypt(pub_key, &int)?, pub_key.size()))
Ok(i2osp(rsa_encrypt(pub_key, &int)?, pub_key.size()))
}

/// Decrypts a plaintext using RSA and the padding scheme from [PKCS#1 OAEP].
Expand Down Expand Up @@ -277,7 +277,7 @@ where
let ciphertext = BoxedUint::from_be_slice(ciphertext, priv_key.n_bits_precision())?;

let em = rsa_decrypt_and_check(priv_key, rng, &ciphertext)?;
let mut em = uint_to_zeroizing_be_pad(em, priv_key.size());
let mut em = i2osp_zeroizing(em, priv_key.size());

oaep_decrypt(&mut em, digest, mgf_digest, label, priv_key.size())
}
Expand Down Expand Up @@ -314,7 +314,7 @@ where

let ciphertext = BoxedUint::from_be_slice(ciphertext, priv_key.n_bits_precision())?;
let em = rsa_decrypt_and_check(priv_key, rng, &ciphertext)?;
let mut em = uint_to_zeroizing_be_pad(em, priv_key.size());
let mut em = i2osp_zeroizing(em, priv_key.size());

oaep_decrypt_digest::<D, MGD>(&mut em, label, priv_key.size())
}
Expand Down
10 changes: 5 additions & 5 deletions src/pkcs1v15.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ use crypto_bigint::BoxedUint;
use digest::Digest;
use rand_core::TryCryptoRng;

use crate::algorithms::pad::{uint_to_be_pad, uint_to_zeroizing_be_pad};
use crate::algorithms::pad::{i2osp, i2osp_zeroizing};
use crate::algorithms::pkcs1v15::*;
use crate::algorithms::rsa::{rsa_decrypt_and_check, rsa_encrypt};
use crate::errors::{Error, Result};
Expand Down Expand Up @@ -156,7 +156,7 @@ fn encrypt<R: TryCryptoRng + ?Sized>(

let em = pkcs1v15_encrypt_pad(rng, msg, pub_key.size())?;
let int = BoxedUint::from_be_slice(&em, pub_key.n_bits_precision())?;
Ok(uint_to_be_pad(rsa_encrypt(pub_key, &int)?, pub_key.size()))
Ok(i2osp(rsa_encrypt(pub_key, &int)?, pub_key.size()))
}

/// Decrypts a plaintext using RSA and the padding scheme from PKCS#1 v1.5.
Expand All @@ -183,7 +183,7 @@ fn decrypt<R: TryCryptoRng + ?Sized>(

let ciphertext = BoxedUint::from_be_slice(ciphertext, priv_key.n_bits_precision())?;
let em = rsa_decrypt_and_check(priv_key, rng, &ciphertext)?;
let em = uint_to_zeroizing_be_pad(em, priv_key.size());
let em = i2osp_zeroizing(em, priv_key.size());

pkcs1v15_encrypt_unpad(&em, priv_key.size())
}
Expand Down Expand Up @@ -212,7 +212,7 @@ fn sign<R: TryCryptoRng + ?Sized>(

let em = BoxedUint::from_be_slice(&em, priv_key.n_bits_precision())?;
// The signature itself is public; only the computation is secret
Ok(uint_to_be_pad(
Ok(i2osp(
rsa_decrypt_and_check(priv_key, rng, &em)?,
priv_key.size(),
))
Expand All @@ -226,7 +226,7 @@ fn verify(pub_key: &RsaPublicKey, prefix: &[u8], hashed: &[u8], sig: &BoxedUint)
return Err(Error::Verification);
}

let em = uint_to_be_pad(rsa_encrypt(pub_key, sig)?, pub_key.size());
let em = i2osp(rsa_encrypt(pub_key, sig)?, pub_key.size());

pkcs1v15_sign_unpad(prefix, hashed, &em, pub_key.size())
}
Expand Down
10 changes: 5 additions & 5 deletions src/pss.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use crypto_bigint::BoxedUint;
use digest::{Digest, FixedOutputReset};
use rand_core::TryCryptoRng;

use crate::algorithms::pad::uint_to_be_pad;
use crate::algorithms::pad::i2osp;
use crate::algorithms::pss::*;
use crate::algorithms::rsa::{rsa_decrypt_and_check, rsa_encrypt};
use crate::errors::{Error, Result};
Expand Down Expand Up @@ -157,7 +157,7 @@ where
return Err(Error::Verification);
}
let raw = rsa_encrypt(pub_key, sig)?;
let mut em = uint_to_be_pad(raw, pub_key.size());
let mut em = i2osp(raw, pub_key.size());

emsa_pss_verify(hashed, &mut em, salt_len, digest, pub_key.n().bits() as _)
}
Expand All @@ -176,7 +176,7 @@ where
return Err(Error::Verification);
}

let mut em = uint_to_be_pad(rsa_encrypt(pub_key, sig)?, pub_key.size());
let mut em = i2osp(rsa_encrypt(pub_key, sig)?, pub_key.size());

emsa_pss_verify_digest::<D>(hashed, &mut em, salt_len, pub_key.n().bits() as _)
}
Expand Down Expand Up @@ -243,7 +243,7 @@ where

let em = BoxedUint::from_be_slice(&em, priv_key.n_bits_precision())?;
let raw = rsa_decrypt_and_check(priv_key, blind_rng, &em)?;
Ok(uint_to_be_pad(raw, priv_key.size()))
Ok(i2osp(raw, priv_key.size()))
}

fn sign_pss_with_salt_digest<T, D>(
Expand All @@ -260,7 +260,7 @@ where
let em = emsa_pss_encode_digest::<D>(hashed, em_bits as _, salt)?;

let em = BoxedUint::from_be_slice(&em, priv_key.n_bits_precision())?;
Ok(uint_to_be_pad(
Ok(i2osp(
rsa_decrypt_and_check(priv_key, blind_rng, &em)?,
priv_key.size(),
))
Expand Down
Loading