Skip to content

Commit be938d5

Browse files
committed
[WIP] pkcs5: add back support for using AES-GCM in PBES2
This support was originally added in #1433, but reverted in #2292 to unblock a release of the `pkcs5` crate until such time as there is a stable release of `aes-gcm`. TODO: still pending a stable release of `aes-gcm`
1 parent 7d69c45 commit be938d5

7 files changed

Lines changed: 294 additions & 3 deletions

File tree

Cargo.lock

Lines changed: 64 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkcs5/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ spki = "0.8"
2121

2222
# optional dependencies
2323
aes = { version = "0.9", optional = true, default-features = false }
24+
aes-gcm = { version = "0.11.0-rc.3", optional = true, default-features = false, features = ["aes"] }
2425
cbc = { version = "0.2", optional = true }
2526
des = { version = "0.9", optional = true, default-features = false }
2627
pbkdf2 = { version = "0.13", optional = true, default-features = false, features = ["hmac"] }
@@ -39,7 +40,7 @@ alloc = []
3940
3des = ["dep:des", "pbes2"]
4041
des-insecure = ["dep:des", "pbes2"]
4142
getrandom = ["dep:getrandom", "rand_core"]
42-
pbes2 = ["dep:aes", "dep:cbc", "dep:pbkdf2", "dep:scrypt", "dep:sha2"]
43+
pbes2 = ["dep:aes", "dep:aes-gcm", "dep:cbc", "dep:pbkdf2", "dep:scrypt", "dep:sha2"]
4344
rand_core = ["dep:rand_core"]
4445
sha1-insecure = ["dep:sha1", "pbes2"]
4546

pkcs5/src/pbes2.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,14 @@ pub const AES_192_CBC_OID: ObjectIdentifier =
3939
pub const AES_256_CBC_OID: ObjectIdentifier =
4040
ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.1.42");
4141

42+
/// 128-bit Advanced Encryption Standard (AES) algorithm with Galois Counter Mode
43+
pub const AES_128_GCM_OID: ObjectIdentifier =
44+
ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.1.6");
45+
46+
/// 256-bit Advanced Encryption Standard (AES) algorithm with Galois Counter Mode
47+
pub const AES_256_GCM_OID: ObjectIdentifier =
48+
ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.1.46");
49+
4250
/// DES operating in CBC mode
4351
#[cfg(feature = "des-insecure")]
4452
pub const DES_CBC_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.14.3.2.7");
@@ -55,6 +63,12 @@ pub const PBES2_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.11
5563
/// AES cipher block size
5664
const AES_BLOCK_SIZE: usize = 16;
5765

66+
/// GCM nonce size
67+
///
68+
/// We could use any value here but GCM is most efficient
69+
/// with 96 bit nonces
70+
const GCM_NONCE_SIZE: usize = 12;
71+
5872
/// DES / Triple DES block size
5973
#[cfg(any(feature = "3des", feature = "des-insecure"))]
6074
const DES_BLOCK_SIZE: usize = 8;
@@ -255,6 +269,40 @@ impl Parameters {
255269
Ok(Self { kdf, encryption })
256270
}
257271

272+
/// Initialize PBES2 parameters using scrypt as the password-based
273+
/// key derivation function and AES-128-GCM as the symmetric cipher.
274+
///
275+
/// For more information on scrypt parameters, see documentation for the
276+
/// [`scrypt::Params`] struct.
277+
// TODO(tarcieri): encapsulate `scrypt::Params`?
278+
#[cfg(feature = "pbes2")]
279+
pub fn scrypt_aes128gcm(
280+
params: scrypt::Params,
281+
salt: &[u8],
282+
gcm_nonce: [u8; GCM_NONCE_SIZE],
283+
) -> Result<Self> {
284+
let kdf = ScryptParams::from_params_and_salt(params, salt)?.into();
285+
let encryption = EncryptionScheme::Aes128Gcm { nonce: gcm_nonce };
286+
Ok(Self { kdf, encryption })
287+
}
288+
289+
/// Initialize PBES2 parameters using scrypt as the password-based
290+
/// key derivation function and AES-256-GCM as the symmetric cipher.
291+
///
292+
/// For more information on scrypt parameters, see documentation for the
293+
/// [`scrypt::Params`] struct.
294+
// TODO(tarcieri): encapsulate `scrypt::Params`?
295+
#[cfg(feature = "pbes2")]
296+
pub fn scrypt_aes256gcm(
297+
params: scrypt::Params,
298+
salt: &[u8],
299+
gcm_nonce: [u8; GCM_NONCE_SIZE],
300+
) -> Result<Self> {
301+
let kdf = ScryptParams::from_params_and_salt(params, salt)?.into();
302+
let encryption = EncryptionScheme::Aes256Gcm { nonce: gcm_nonce };
303+
Ok(Self { kdf, encryption })
304+
}
305+
258306
/// Attempt to decrypt the given ciphertext, allocating and returning a
259307
/// byte vector containing the plaintext.
260308
///
@@ -387,6 +435,18 @@ pub enum EncryptionScheme {
387435
iv: [u8; AES_BLOCK_SIZE],
388436
},
389437

438+
/// AES-128 in CBC mode
439+
Aes128Gcm {
440+
/// GCM nonce
441+
nonce: [u8; GCM_NONCE_SIZE],
442+
},
443+
444+
/// AES-256 in GCM mode
445+
Aes256Gcm {
446+
/// GCM nonce
447+
nonce: [u8; GCM_NONCE_SIZE],
448+
},
449+
390450
/// 3-Key Triple DES in CBC mode
391451
#[cfg(feature = "3des")]
392452
DesEde3Cbc {
@@ -410,6 +470,8 @@ impl EncryptionScheme {
410470
Self::Aes128Cbc { .. } => 16,
411471
Self::Aes192Cbc { .. } => 24,
412472
Self::Aes256Cbc { .. } => 32,
473+
Self::Aes128Gcm { .. } => 16,
474+
Self::Aes256Gcm { .. } => 32,
413475
#[cfg(feature = "des-insecure")]
414476
Self::DesCbc { .. } => 8,
415477
#[cfg(feature = "3des")]
@@ -424,6 +486,8 @@ impl EncryptionScheme {
424486
Self::Aes128Cbc { .. } => AES_128_CBC_OID,
425487
Self::Aes192Cbc { .. } => AES_192_CBC_OID,
426488
Self::Aes256Cbc { .. } => AES_256_CBC_OID,
489+
Self::Aes128Gcm { .. } => AES_128_GCM_OID,
490+
Self::Aes256Gcm { .. } => AES_256_GCM_OID,
427491
#[cfg(feature = "des-insecure")]
428492
Self::DesCbc { .. } => DES_CBC_OID,
429493
#[cfg(feature = "3des")]
@@ -468,6 +532,12 @@ impl TryFrom<AlgorithmIdentifierRef<'_>> for EncryptionScheme {
468532
AES_256_CBC_OID => Ok(Self::Aes256Cbc {
469533
iv: iv.try_into().map_err(|_| Tag::OctetString.value_error())?,
470534
}),
535+
AES_128_GCM_OID => Ok(Self::Aes128Gcm {
536+
nonce: iv.try_into().map_err(|_| Tag::OctetString.value_error())?,
537+
}),
538+
AES_256_GCM_OID => Ok(Self::Aes256Gcm {
539+
nonce: iv.try_into().map_err(|_| Tag::OctetString.value_error())?,
540+
}),
471541
#[cfg(feature = "des-insecure")]
472542
DES_CBC_OID => Ok(Self::DesCbc {
473543
iv: iv[0..DES_BLOCK_SIZE]
@@ -493,6 +563,8 @@ impl<'a> TryFrom<&'a EncryptionScheme> for AlgorithmIdentifierRef<'a> {
493563
EncryptionScheme::Aes128Cbc { iv } => iv.as_slice(),
494564
EncryptionScheme::Aes192Cbc { iv } => iv.as_slice(),
495565
EncryptionScheme::Aes256Cbc { iv } => iv.as_slice(),
566+
EncryptionScheme::Aes128Gcm { nonce } => nonce.as_slice(),
567+
EncryptionScheme::Aes256Gcm { nonce } => nonce.as_slice(),
496568
#[cfg(feature = "des-insecure")]
497569
EncryptionScheme::DesCbc { iv } => iv.as_slice(),
498570
#[cfg(feature = "3des")]

pkcs5/src/pbes2/encryption.rs

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
use super::{EncryptionScheme, Kdf, Parameters, Pbkdf2Params, Pbkdf2Prf, ScryptParams};
44
use crate::{Error, Result};
5+
use aes_gcm::{KeyInit as GcmKeyInit, Nonce, Tag, aead::AeadInOut};
56
use cbc::cipher::{
67
BlockCipherDecrypt, BlockCipherEncrypt, BlockModeDecrypt, BlockModeEncrypt, KeyInit, KeyIvInit,
78
block_padding::Pkcs7,
@@ -12,7 +13,7 @@ use pbkdf2::{
1213
digest::{
1314
FixedOutput, HashMarker, Update,
1415
block_api::BlockSizeUser,
15-
typenum::{IsLess, NonZero, True, U256},
16+
typenum::{IsLess, NonZero, True, U12, U16, U256},
1617
},
1718
},
1819
pbkdf2_hmac,
@@ -47,6 +48,65 @@ fn cbc_decrypt<'a, C: BlockCipherDecrypt + KeyInit>(
4748
.map_err(|_| Error::DecryptFailed)
4849
}
4950

51+
fn gcm_encrypt<C, NonceSize, TagSize>(
52+
es: EncryptionScheme,
53+
key: EncryptionKey,
54+
nonce: Nonce<NonceSize>,
55+
buffer: &mut [u8],
56+
pos: usize,
57+
) -> Result<&[u8]>
58+
where
59+
C: BlockSizeUser<BlockSize = U16> + GcmKeyInit + BlockCipherEncrypt,
60+
aes_gcm::AesGcm<C, NonceSize, TagSize>: GcmKeyInit,
61+
TagSize: aes_gcm::TagSize,
62+
NonceSize: aes::cipher::array::ArraySize,
63+
{
64+
if buffer.len() < TagSize::USIZE + pos {
65+
return Err(Error::EncryptFailed);
66+
}
67+
let gcm =
68+
<aes_gcm::AesGcm<C, NonceSize, TagSize> as GcmKeyInit>::new_from_slice(key.as_slice())
69+
.map_err(|_| es.to_alg_params_invalid())?;
70+
let tag = gcm
71+
.encrypt_inout_detached(&nonce, &[], (&mut buffer[..pos]).into())
72+
.map_err(|_| Error::EncryptFailed)?;
73+
buffer[pos..].copy_from_slice(tag.as_ref());
74+
Ok(&buffer[0..pos + TagSize::USIZE])
75+
}
76+
77+
fn gcm_decrypt<C, NonceSize, TagSize>(
78+
es: EncryptionScheme,
79+
key: EncryptionKey,
80+
nonce: Nonce<NonceSize>,
81+
buffer: &mut [u8],
82+
) -> Result<&[u8]>
83+
where
84+
C: BlockSizeUser<BlockSize = U16> + GcmKeyInit + BlockCipherEncrypt,
85+
aes_gcm::AesGcm<C, NonceSize, TagSize>: GcmKeyInit,
86+
TagSize: aes_gcm::TagSize,
87+
NonceSize: aes::cipher::array::ArraySize,
88+
{
89+
let msg_len = buffer
90+
.len()
91+
.checked_sub(TagSize::USIZE)
92+
.ok_or(Error::DecryptFailed)?;
93+
94+
let gcm =
95+
<aes_gcm::AesGcm<C, NonceSize, TagSize> as GcmKeyInit>::new_from_slice(key.as_slice())
96+
.map_err(|_| es.to_alg_params_invalid())?;
97+
98+
let tag = Tag::try_from(&buffer[msg_len..]).map_err(|_| Error::DecryptFailed)?;
99+
100+
if gcm
101+
.decrypt_inout_detached(&nonce, &[], (&mut buffer[..msg_len]).into(), &tag)
102+
.is_err()
103+
{
104+
return Err(Error::DecryptFailed);
105+
}
106+
107+
Ok(&buffer[..msg_len])
108+
}
109+
50110
pub fn encrypt_in_place<'b>(
51111
params: &Parameters,
52112
password: impl AsRef<[u8]>,
@@ -64,6 +124,12 @@ pub fn encrypt_in_place<'b>(
64124
EncryptionScheme::Aes128Cbc { iv } => cbc_encrypt::<aes::Aes128Enc>(es, key, &iv, buf, pos),
65125
EncryptionScheme::Aes192Cbc { iv } => cbc_encrypt::<aes::Aes192Enc>(es, key, &iv, buf, pos),
66126
EncryptionScheme::Aes256Cbc { iv } => cbc_encrypt::<aes::Aes256Enc>(es, key, &iv, buf, pos),
127+
EncryptionScheme::Aes128Gcm { nonce } => {
128+
gcm_encrypt::<aes::Aes128Enc, U12, U16>(es, key, Nonce::from(nonce), buf, pos)
129+
}
130+
EncryptionScheme::Aes256Gcm { nonce } => {
131+
gcm_encrypt::<aes::Aes256Enc, U12, U16>(es, key, Nonce::from(nonce), buf, pos)
132+
}
67133
#[cfg(feature = "3des")]
68134
EncryptionScheme::DesEde3Cbc { iv } => cbc_encrypt::<des::TdesEde3>(es, key, &iv, buf, pos),
69135
#[cfg(feature = "des-insecure")]
@@ -86,6 +152,12 @@ pub fn decrypt_in_place<'a>(
86152
EncryptionScheme::Aes128Cbc { iv } => cbc_decrypt::<aes::Aes128Dec>(es, key, &iv, buf),
87153
EncryptionScheme::Aes192Cbc { iv } => cbc_decrypt::<aes::Aes192Dec>(es, key, &iv, buf),
88154
EncryptionScheme::Aes256Cbc { iv } => cbc_decrypt::<aes::Aes256Dec>(es, key, &iv, buf),
155+
EncryptionScheme::Aes128Gcm { nonce } => {
156+
gcm_decrypt::<aes::Aes128Enc, U12, U16>(es, key, Nonce::from(nonce), buf)
157+
}
158+
EncryptionScheme::Aes256Gcm { nonce } => {
159+
gcm_decrypt::<aes::Aes256Enc, U12, U16>(es, key, Nonce::from(nonce), buf)
160+
}
89161
#[cfg(feature = "3des")]
90162
EncryptionScheme::DesEde3Cbc { iv } => cbc_decrypt::<des::TdesEde3>(es, key, &iv, buf),
91163
#[cfg(feature = "des-insecure")]

0 commit comments

Comments
 (0)