Skip to content

Commit 2feb393

Browse files
committed
fixed RustCrypto#316 - correctly parse OpenSSH keys generated by PuTTYgen
1 parent c05cd47 commit 2feb393

File tree

2 files changed

+25
-3
lines changed

2 files changed

+25
-3
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
/target/
22
**/*.rs.bk
33
ssh-key/tests/scratch/
4+
.vscode

ssh-key/src/private.rs

+24-3
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ const DEFAULT_RSA_KEY_SIZE: usize = 4096;
176176
const 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"))]
@@ -358,6 +358,7 @@ impl PrivateKey {
358358
&mut &**buffer,
359359
self.public_key.key_data.clone(),
360360
self.cipher.block_size(),
361+
self.cipher.block_size() - 1,
361362
)
362363
}
363364

@@ -548,8 +549,10 @@ impl PrivateKey {
548549
reader: &mut impl Reader,
549550
public_key: public::KeyData,
550551
block_size: usize,
552+
max_padding_size: usize,
551553
) -> Result<Self> {
552554
debug_assert!(block_size <= MAX_BLOCK_SIZE);
555+
debug_assert!(max_padding_size <= MAX_BLOCK_SIZE);
553556

554557
// Ensure input data is padding-aligned
555558
if reader.remaining_len().checked_rem(block_size) != Some(0) {
@@ -575,7 +578,7 @@ impl PrivateKey {
575578

576579
let padding_len = reader.remaining_len();
577580

578-
if padding_len >= block_size {
581+
if padding_len > max_padding_size {
579582
return Err(encoding::Error::Length.into());
580583
}
581584

@@ -733,7 +736,25 @@ impl Decode for PrivateKey {
733736
}
734737

735738
reader.read_prefixed(|reader| {
736-
Self::decode_privatekey_comment_pair(reader, public_key, cipher.block_size())
739+
// PuTTYgen uses a non-standard block size of 16
740+
// and _always_ adds a padding even if data length
741+
// is divisible by 16 - for unencrypted keys
742+
// in the OpenSSH format.
743+
// We're only relaxing the exact length check, but will
744+
// still validate that the contents of the padding area.
745+
// In all other cases there can be up to (but not including)
746+
// `block_size` padding bytes as per `PROTOCOL.key`.
747+
let max_padding_size = match cipher {
748+
Cipher::None => 16,
749+
#[allow(clippy::arithmetic_side_effects)] // block sizes are constants
750+
_ => cipher.block_size() - 1,
751+
};
752+
Self::decode_privatekey_comment_pair(
753+
reader,
754+
public_key,
755+
cipher.block_size(),
756+
max_padding_size,
757+
)
737758
})
738759
}
739760
}

0 commit comments

Comments
 (0)