@@ -176,7 +176,7 @@ const DEFAULT_RSA_KEY_SIZE: usize = 4096;
176176const MAX_BLOCK_SIZE : usize = 16 ;
177177
178178/// 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 ] ;
180180
181181/// Unix file permissions for SSH private keys.
182182#[ cfg( all( unix, feature = "std" ) ) ]
@@ -354,10 +354,12 @@ impl PrivateKey {
354354 let mut buffer = Zeroizing :: new ( ciphertext. to_vec ( ) ) ;
355355 self . cipher . decrypt ( & key, & iv, & mut buffer, self . auth_tag ) ?;
356356
357+ #[ allow( clippy:: arithmetic_side_effects) ] // block sizes are constants
357358 Self :: decode_privatekey_comment_pair (
358359 & mut & * * buffer,
359360 self . public_key . key_data . clone ( ) ,
360361 self . cipher . block_size ( ) ,
362+ self . cipher . block_size ( ) - 1 ,
361363 )
362364 }
363365
@@ -548,8 +550,10 @@ impl PrivateKey {
548550 reader : & mut impl Reader ,
549551 public_key : public:: KeyData ,
550552 block_size : usize ,
553+ max_padding_size : usize ,
551554 ) -> Result < Self > {
552555 debug_assert ! ( block_size <= MAX_BLOCK_SIZE ) ;
556+ debug_assert ! ( max_padding_size <= MAX_BLOCK_SIZE ) ;
553557
554558 // Ensure input data is padding-aligned
555559 if reader. remaining_len ( ) . checked_rem ( block_size) != Some ( 0 ) {
@@ -575,7 +579,7 @@ impl PrivateKey {
575579
576580 let padding_len = reader. remaining_len ( ) ;
577581
578- if padding_len >= block_size {
582+ if padding_len > max_padding_size {
579583 return Err ( encoding:: Error :: Length . into ( ) ) ;
580584 }
581585
@@ -733,7 +737,25 @@ impl Decode for PrivateKey {
733737 }
734738
735739 reader. read_prefixed ( |reader| {
736- Self :: decode_privatekey_comment_pair ( reader, public_key, cipher. block_size ( ) )
740+ // PuTTYgen uses a non-standard block size of 16
741+ // and _always_ adds a padding even if data length
742+ // is divisible by 16 - for unencrypted keys
743+ // in the OpenSSH format.
744+ // We're only relaxing the exact length check, but will
745+ // still validate that the contents of the padding area.
746+ // In all other cases there can be up to (but not including)
747+ // `block_size` padding bytes as per `PROTOCOL.key`.
748+ let max_padding_size = match cipher {
749+ Cipher :: None => 16 ,
750+ #[ allow( clippy:: arithmetic_side_effects) ] // block sizes are constants
751+ _ => cipher. block_size ( ) - 1 ,
752+ } ;
753+ Self :: decode_privatekey_comment_pair (
754+ reader,
755+ public_key,
756+ cipher. block_size ( ) ,
757+ max_padding_size,
758+ )
737759 } )
738760 }
739761}
0 commit comments