@@ -13,6 +13,8 @@ use core::fmt::Debug;
13
13
#[ non_exhaustive]
14
14
#[ derive( Debug , PartialEq , Eq , Clone , Copy ) ]
15
15
pub ( crate ) enum PaddingStrategy {
16
+ /// ISO 10126 padding. For compatibility purposes only. Applies non-random PKCS7 padding.
17
+ ISO10126 ,
16
18
/// PKCS#7 Padding. ([See RFC 5652](https://datatracker.ietf.org/doc/html/rfc5652#section-6.3))
17
19
PKCS7 ,
18
20
}
@@ -23,7 +25,8 @@ impl PaddingStrategy {
23
25
InOut : AsMut < [ u8 ] > + for < ' in_out > Extend < & ' in_out u8 > ,
24
26
{
25
27
match self {
26
- PaddingStrategy :: PKCS7 => {
28
+ // PKCS7 padding can be unpadded as ISO 10126 padding
29
+ PaddingStrategy :: ISO10126 | PaddingStrategy :: PKCS7 => {
27
30
let mut padding_buffer = [ 0u8 ; MAX_CIPHER_BLOCK_LEN ] ;
28
31
29
32
let in_out_len = in_out. as_mut ( ) . len ( ) ;
@@ -40,14 +43,23 @@ impl PaddingStrategy {
40
43
}
41
44
42
45
fn remove_padding ( self , block_len : usize , in_out : & mut [ u8 ] ) -> Result < & mut [ u8 ] , Unspecified > {
46
+ if in_out. is_empty ( ) || in_out. len ( ) < block_len {
47
+ return Err ( Unspecified ) ;
48
+ }
43
49
match self {
44
- PaddingStrategy :: PKCS7 => {
45
- let block_size: u8 = block_len. try_into ( ) . map_err ( |_| Unspecified ) ?;
46
-
47
- if in_out. is_empty ( ) || in_out. len ( ) < block_len {
50
+ PaddingStrategy :: ISO10126 => {
51
+ let padding: u8 = in_out[ in_out. len ( ) - 1 ] ;
52
+ if padding == 0 || padding as usize > block_len {
48
53
return Err ( Unspecified ) ;
49
54
}
50
55
56
+ // ISO 10126 padding is a random padding scheme, so we cannot verify the padding bytes
57
+ let final_len = in_out. len ( ) - padding as usize ;
58
+ Ok ( & mut in_out[ 0 ..final_len] )
59
+ }
60
+ PaddingStrategy :: PKCS7 => {
61
+ let block_size: u8 = block_len. try_into ( ) . map_err ( |_| Unspecified ) ?;
62
+
51
63
let padding: u8 = in_out[ in_out. len ( ) - 1 ] ;
52
64
if padding == 0 || padding > block_size {
53
65
return Err ( Unspecified ) ;
@@ -208,6 +220,23 @@ impl PaddedBlockDecryptingKey {
208
220
Self :: new ( key, OperatingMode :: CBC , PaddingStrategy :: PKCS7 )
209
221
}
210
222
223
+ /// Constructs a new `PaddedBlockDecryptingKey` cipher with chaining block cipher (CBC) mode.
224
+ /// Decrypted data is unpadded following the ISO 10126 scheme
225
+ /// (compatible with PKCS#7 and ANSI X.923).
226
+ ///
227
+ /// Offered for computability purposes only.
228
+ ///
229
+ // # FIPS
230
+ // Use this function with an `UnboundCipherKey` constructed with one of the following algorithms:
231
+ // * `AES_128`
232
+ // * `AES_256`
233
+ //
234
+ /// # Errors
235
+ /// * [`Unspecified`]: Returned if there is an error constructing the `PaddedBlockDecryptingKey`.
236
+ pub fn cbc_iso10126 ( key : UnboundCipherKey ) -> Result < Self , Unspecified > {
237
+ Self :: new ( key, OperatingMode :: CBC , PaddingStrategy :: ISO10126 )
238
+ }
239
+
211
240
/// Constructs a new `PaddedBlockDecryptingKey` cipher with electronic code book (ECB) mode.
212
241
/// Decrypted data is unpadded following the PKCS#7 scheme.
213
242
///
@@ -330,6 +359,16 @@ mod tests {
330
359
assert_eq ! ( input. as_slice( ) , plaintext) ;
331
360
}
332
361
362
+ #[ test]
363
+ fn test_unpad_iso10126 ( ) {
364
+ let mut input = from_hex ( "01020304050607fedcba9805" ) . unwrap ( ) ;
365
+ let padding = PaddingStrategy :: ISO10126 ;
366
+ let block_len = 8 ;
367
+
368
+ let unpadded = padding. remove_padding ( block_len, & mut input) . unwrap ( ) ;
369
+ assert_eq ! ( unpadded, & mut [ 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ) ;
370
+ }
371
+
333
372
#[ test]
334
373
fn test_aes_128_cbc ( ) {
335
374
let key = from_hex ( "000102030405060708090a0b0c0d0e0f" ) . unwrap ( ) ;
@@ -390,7 +429,13 @@ mod tests {
390
429
391
430
let context = encrypting_key. less_safe_encrypt( & mut in_out, ec) . unwrap( ) ;
392
431
393
- assert_eq!( expected_ciphertext, in_out) ;
432
+ if ( $padding == PaddingStrategy :: ISO10126 ) {
433
+ // This padding scheme is technically non-deterministic in nature if the padding is more then one
434
+ // byte. So just validate the input length of in_out is no longer the plaintext.
435
+ assert_ne!( input, in_out[ ..input. len( ) ] ) ;
436
+ } else {
437
+ assert_eq!( expected_ciphertext, in_out) ;
438
+ }
394
439
395
440
let unbound_key2 = UnboundCipherKey :: new( alg, & key) . unwrap( ) ;
396
441
let decrypting_key =
@@ -435,6 +480,28 @@ mod tests {
435
480
"ad96993f248bd6a29760ec7ccda95ee1"
436
481
) ;
437
482
483
+ padded_cipher_kat ! (
484
+ test_openssl_aes_128_cbc_iso10126_15_bytes,
485
+ & AES_128 ,
486
+ OperatingMode :: CBC ,
487
+ PaddingStrategy :: ISO10126 ,
488
+ "053304bb3899e1d99db9d29343ea782d" ,
489
+ "b5313560244a4822c46c2a0c9d0cf7fd" ,
490
+ "a3e4c990356c01f320043c3d8d6f43" ,
491
+ "ad96993f248bd6a29760ec7ccda95ee1"
492
+ ) ;
493
+
494
+ padded_cipher_kat ! (
495
+ test_openssl_aes_128_cbc_iso10126_16_bytes,
496
+ & AES_128 ,
497
+ OperatingMode :: CBC ,
498
+ PaddingStrategy :: ISO10126 ,
499
+ "053304bb3899e1d99db9d29343ea782d" ,
500
+ "b83452fc9c80215a6ecdc505b5154c90" ,
501
+ "736e65616b7920726163636f6f6e7321" ,
502
+ "44563399c6bb2133e013161dc5bd4fa8ce83ef997ddb04bbbbe3632b68e9cde0"
503
+ ) ;
504
+
438
505
padded_cipher_kat ! (
439
506
test_openssl_aes_128_cbc_16_bytes,
440
507
& AES_128 ,
0 commit comments