@@ -176,7 +176,7 @@ const DEFAULT_RSA_KEY_SIZE: usize = 4096;
176
176
const MAX_BLOCK_SIZE : usize = 16 ;
177
177
178
178
/// Padding bytes to use.
179
- const PADDING_BYTES : [ u8 ; MAX_BLOCK_SIZE - 1 ] = [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 ] ;
179
+ const PADDING_BYTES : [ u8 ; MAX_BLOCK_SIZE ] = [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 , 16 ] ;
180
180
181
181
/// Unix file permissions for SSH private keys.
182
182
#[ cfg( all( unix, feature = "std" ) ) ]
@@ -377,10 +377,12 @@ impl PrivateKey {
377
377
let mut buffer = Zeroizing :: new ( ciphertext. to_vec ( ) ) ;
378
378
self . cipher . decrypt ( & key, & iv, & mut buffer, self . auth_tag ) ?;
379
379
380
+ #[ allow( clippy:: arithmetic_side_effects) ] // block sizes are constants
380
381
Self :: decode_privatekey_comment_pair (
381
382
& mut & * * buffer,
382
383
self . public_key . key_data . clone ( ) ,
383
384
self . cipher . block_size ( ) ,
385
+ self . cipher . block_size ( ) - 1 ,
384
386
)
385
387
}
386
388
@@ -571,8 +573,10 @@ impl PrivateKey {
571
573
reader : & mut impl Reader ,
572
574
public_key : public:: KeyData ,
573
575
block_size : usize ,
576
+ max_padding_size : usize ,
574
577
) -> Result < Self > {
575
578
debug_assert ! ( block_size <= MAX_BLOCK_SIZE ) ;
579
+ debug_assert ! ( max_padding_size <= MAX_BLOCK_SIZE ) ;
576
580
577
581
// Ensure input data is padding-aligned
578
582
if reader. remaining_len ( ) . checked_rem ( block_size) != Some ( 0 ) {
@@ -598,7 +602,7 @@ impl PrivateKey {
598
602
599
603
let padding_len = reader. remaining_len ( ) ;
600
604
601
- if padding_len >= block_size {
605
+ if padding_len > max_padding_size {
602
606
return Err ( encoding:: Error :: Length . into ( ) ) ;
603
607
}
604
608
@@ -756,7 +760,25 @@ impl Decode for PrivateKey {
756
760
}
757
761
758
762
reader. read_prefixed ( |reader| {
759
- Self :: decode_privatekey_comment_pair ( reader, public_key, cipher. block_size ( ) )
763
+ // PuTTYgen uses a non-standard block size of 16
764
+ // and _always_ adds a padding even if data length
765
+ // is divisible by 16 - for unencrypted keys
766
+ // in the OpenSSH format.
767
+ // We're only relaxing the exact length check, but will
768
+ // still validate that the contents of the padding area.
769
+ // In all other cases there can be up to (but not including)
770
+ // `block_size` padding bytes as per `PROTOCOL.key`.
771
+ let max_padding_size = match cipher {
772
+ Cipher :: None => 16 ,
773
+ #[ allow( clippy:: arithmetic_side_effects) ] // block sizes are constants
774
+ _ => cipher. block_size ( ) - 1 ,
775
+ } ;
776
+ Self :: decode_privatekey_comment_pair (
777
+ reader,
778
+ public_key,
779
+ cipher. block_size ( ) ,
780
+ max_padding_size,
781
+ )
760
782
} )
761
783
}
762
784
}
0 commit comments