Skip to content

Commit

Permalink
fixed RustCrypto#316 - correctly parse OpenSSH keys generated by PuTT…
Browse files Browse the repository at this point in the history
…Ygen
  • Loading branch information
Eugeny committed Dec 28, 2024
1 parent 6ae2c08 commit bff59ee
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 3 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/target/
**/*.rs.bk
ssh-key/tests/scratch/
.vscode
27 changes: 24 additions & 3 deletions ssh-key/src/private.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ const DEFAULT_RSA_KEY_SIZE: usize = 4096;
const MAX_BLOCK_SIZE: usize = 16;

/// Padding bytes to use.
const PADDING_BYTES: [u8; MAX_BLOCK_SIZE - 1] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
const PADDING_BYTES: [u8; MAX_BLOCK_SIZE] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];

/// Unix file permissions for SSH private keys.
#[cfg(all(unix, feature = "std"))]
Expand Down Expand Up @@ -358,6 +358,7 @@ impl PrivateKey {
&mut &**buffer,
self.public_key.key_data.clone(),
self.cipher.block_size(),
self.cipher.block_size() - 1,
)
}

Expand Down Expand Up @@ -548,8 +549,10 @@ impl PrivateKey {
reader: &mut impl Reader,
public_key: public::KeyData,
block_size: usize,
max_padding_size: usize,
) -> Result<Self> {
debug_assert!(block_size <= MAX_BLOCK_SIZE);
debug_assert!(max_padding_size <= MAX_BLOCK_SIZE);

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

let padding_len = reader.remaining_len();

if padding_len >= block_size {
if padding_len > max_padding_size {
return Err(encoding::Error::Length.into());
}

Expand Down Expand Up @@ -733,7 +736,25 @@ impl Decode for PrivateKey {
}

reader.read_prefixed(|reader| {
Self::decode_privatekey_comment_pair(reader, public_key, cipher.block_size())
// PuTTYgen uses a non-standard block size of 16
// and _always_ adds a padding even if data length
// is divisible by 16 - for unencrypted keys
// in the OpenSSH format.
// We're only relaxing the exact length check, but will
// still validate that the contents of the padding area.
// In all other cases there can be up to (but not including)
// `block_size` padding bytes as per `PROTOCOL.key`.
let max_padding_size = match cipher {
Cipher::None => 16,
#[allow(clippy::arithmetic_side_effects)] // block sizes are constants
_ => cipher.block_size() - 1,
};
Self::decode_privatekey_comment_pair(
reader,
public_key,
cipher.block_size(),
max_padding_size,
)
})
}
}
Expand Down

0 comments on commit bff59ee

Please sign in to comment.