|
8 | 8 |
|
9 | 9 | use alloc::vec::Vec; |
10 | 10 | use const_oid::AssociatedOid; |
11 | | -use crypto_bigint::{Choice, CtAssign, CtEq, CtSelect}; |
12 | | -use digest::Digest; |
| 11 | +use crypto_bigint::{BoxedUint, Choice, CtAssign, CtEq, CtGt, CtLt, CtSelect}; |
| 12 | +use digest::{Digest, OutputSizeUser}; |
| 13 | +use hmac::{Hmac, KeyInit, Mac}; |
13 | 14 | use rand_core::TryCryptoRng; |
| 15 | +use sha2::Sha256; |
14 | 16 | use zeroize::Zeroizing; |
15 | 17 |
|
16 | | -use crate::errors::{Error, Result}; |
17 | | - |
18 | | -#[cfg(feature = "implicit_rejection")] |
19 | | -use { |
20 | | - crate::algorithms::pad::uint_to_zeroizing_be_pad, |
21 | | - crypto_bigint::{BoxedUint, CtGt, CtLt}, |
22 | | - digest::OutputSizeUser, |
23 | | - hmac::{Hmac, KeyInit, Mac}, |
24 | | - sha2::Sha256, |
| 18 | +use crate::{ |
| 19 | + algorithms::pad::uint_to_zeroizing_be_pad, |
| 20 | + errors::{Error, Result}, |
25 | 21 | }; |
26 | 22 |
|
27 | 23 | /// Fills the provided slice with random values, which are guaranteed |
@@ -68,74 +64,16 @@ where |
68 | 64 | Ok(em) |
69 | 65 | } |
70 | 66 |
|
71 | | -/// Removes the encryption padding scheme from PKCS#1 v1.5. |
72 | | -/// |
73 | | -/// Note that whether this function returns an error or not discloses secret |
74 | | -/// information. If an attacker can cause this function to run repeatedly and |
75 | | -/// learn whether each instance returned an error then they can decrypt and |
76 | | -/// forge signatures as if they had the private key. See |
77 | | -/// `decrypt_session_key` for a way of solving this problem. |
78 | | -#[inline] |
79 | | -pub(crate) fn pkcs1v15_encrypt_unpad(em: Vec<u8>, k: usize) -> Result<Vec<u8>> { |
80 | | - let (valid, out, index) = decrypt_inner(em, k)?; |
81 | | - if valid == 0 { |
82 | | - return Err(Error::Decryption); |
83 | | - } |
84 | | - |
85 | | - Ok(out[index as usize..].to_vec()) |
86 | | -} |
87 | | - |
88 | | -/// Removes the PKCS1v15 padding It returns one or zero in valid that indicates whether the |
89 | | -/// plaintext was correctly structured. In either case, the plaintext is |
90 | | -/// returned in em so that it may be read independently of whether it was valid |
91 | | -/// in order to maintain constant memory access patterns. If the plaintext was |
92 | | -/// valid then index contains the index of the original message in em. |
93 | | -#[inline] |
94 | | -fn decrypt_inner(em: Vec<u8>, k: usize) -> Result<(u8, Vec<u8>, u32)> { |
95 | | - if k < 11 { |
96 | | - return Err(Error::Decryption); |
97 | | - } |
98 | | - |
99 | | - let first_byte_is_zero = em[0].ct_eq(&0u8); |
100 | | - let second_byte_is_two = em[1].ct_eq(&2u8); |
101 | | - |
102 | | - // The remainder of the plaintext must be a string of non-zero random |
103 | | - // octets, followed by a 0, followed by the message. |
104 | | - // looking_for_index: 1 iff we are still looking for the zero. |
105 | | - // index: the offset of the first zero byte. |
106 | | - let mut looking_for_index = Choice::TRUE; |
107 | | - let mut index = 0u32; |
108 | | - |
109 | | - for (i, el) in em.iter().enumerate().skip(2) { |
110 | | - let equals0 = el.ct_eq(&0u8); |
111 | | - index.ct_assign(&(i as u32), looking_for_index & equals0); |
112 | | - looking_for_index &= !equals0; |
113 | | - } |
114 | | - |
115 | | - // The PS padding must be at least 8 bytes long, and it starts two |
116 | | - // bytes into em. |
117 | | - // TODO: WARNING: THIS MUST BE CONSTANT TIME CHECK: |
118 | | - // Ref: https://github.com/dalek-cryptography/subtle/issues/20 |
119 | | - // This is currently copy & paste from the constant time impl in |
120 | | - // go, but very likely not sufficient. |
121 | | - let valid_ps = Choice::from_u8_lsb((((2i32 + 8i32 - index as i32 - 1i32) >> 31) & 1) as u8); |
122 | | - let valid = first_byte_is_zero & second_byte_is_two & !looking_for_index & valid_ps; |
123 | | - index = u32::ct_select(&0, &(index + 1), valid); |
124 | | - |
125 | | - Ok((valid.to_u8(), em, index)) |
126 | | -} |
127 | | - |
128 | 67 | /// Removes PKCS#1 v1.5 encryption padding with implicit rejection. |
129 | 68 | /// |
130 | | -/// Unlike [`pkcs1v15_encrypt_unpad`], this function does not return an error if |
| 69 | +/// This function does not return an error if |
131 | 70 | /// the padding is invalid. Instead, it deterministically generates and returns |
132 | 71 | /// a replacement random message using a key-derivation function. |
133 | 72 | /// As a result, callers cannot distinguish between valid and |
134 | 73 | /// invalid padding based on the output, thus preventing side-channel attacks. |
135 | 74 | /// |
136 | 75 | /// See |
137 | 76 | /// [draft-irtf-cfrg-rsa-guidance-08 § 7.2](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-rsa-guidance-08#section-7.2) |
138 | | -#[cfg(feature = "implicit_rejection")] |
139 | 77 | pub(crate) fn pkcs1v15_encrypt_unpad_implicit_rejection( |
140 | 78 | em: Vec<u8>, |
141 | 79 | k: usize, |
@@ -168,7 +106,7 @@ pub(crate) fn pkcs1v15_encrypt_unpad_implicit_rejection( |
168 | 106 |
|
169 | 107 | // Select the rejection length from the prf output. |
170 | 108 | let rejection_length = rejection_lengths.chunks_exact(2).fold(0u16, |acc, el| { |
171 | | - let candidate_length = (u16::from(el[0]) << 8 | u16::from(el[1])) & mask; |
| 109 | + let candidate_length = ((u16::from(el[0]) << 8) | u16::from(el[1])) & mask; |
172 | 110 | let less_than_max_length = candidate_length.ct_lt(&max_length); |
173 | 111 | acc.ct_select(&candidate_length, less_than_max_length) |
174 | 112 | }); |
@@ -216,10 +154,8 @@ pub(crate) fn pkcs1v15_encrypt_unpad_implicit_rejection( |
216 | 154 | Ok(output) |
217 | 155 | } |
218 | 156 |
|
219 | | -#[cfg(feature = "implicit_rejection")] |
220 | 157 | pub(crate) struct KeyDerivationKey(Zeroizing<[u8; 32]>); |
221 | 158 |
|
222 | | -#[cfg(feature = "implicit_rejection")] |
223 | 159 | impl KeyDerivationKey { |
224 | 160 | /// Derives a key derivation key from the private key, the ciphertext, and the key length. |
225 | 161 | /// |
|
0 commit comments