Skip to content

Commit ae02e7d

Browse files
authored
ssh-key: Fix KeyData::Certificate encoding (#451)
KeyData was duplicating the algorithm string when encoding a certificate public key to its binary representation. It was also filling in the non-certificate variant of the Algorithm string. This fixes both. Signed-off-by: Ross Williams <[email protected]>
1 parent 71d7e70 commit ae02e7d

File tree

2 files changed

+47
-1
lines changed

2 files changed

+47
-1
lines changed

ssh-key/src/public/key_data.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,12 @@ impl Decode for KeyData {
301301

302302
impl Encode for KeyData {
303303
fn encoded_len(&self) -> encoding::Result<usize> {
304+
#[cfg(feature = "alloc")]
305+
if self.is_certificate() {
306+
// Certificate encodes its own Algorithm
307+
return self.encoded_key_data_len();
308+
}
309+
304310
[
305311
self.algorithm().encoded_len()?,
306312
self.encoded_key_data_len()?,
@@ -309,6 +315,12 @@ impl Encode for KeyData {
309315
}
310316

311317
fn encode(&self, writer: &mut impl Writer) -> encoding::Result<()> {
318+
#[cfg(feature = "alloc")]
319+
if self.is_certificate() {
320+
// Certificate encodes its own Algorithm
321+
return self.encode_key_data(writer);
322+
}
323+
312324
self.algorithm().encode(writer)?;
313325
self.encode_key_data(writer)
314326
}

ssh-key/tests/certificate.rs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
#![cfg(feature = "alloc")]
44

5-
use encoding::Decode;
5+
use encoding::{Base64Reader, Decode, Encode, Reader};
66
use hex_literal::hex;
77
use ssh_key::{Algorithm, Certificate, public::KeyData};
88
use std::str::FromStr;
@@ -301,6 +301,40 @@ fn decode_rsa_4096_keydata() {
301301
decode_keydata(RSA_4096_CERT_EXAMPLE)
302302
}
303303

304+
fn encode_keydata(certificate_str: &str) {
305+
// Decode the certificate's binary representation directly from the base64
306+
let cert_base64 = certificate_str.split_whitespace().nth(1).unwrap();
307+
let mut base64_reader = Base64Reader::new(cert_base64.as_bytes()).unwrap();
308+
let mut cert_bytes = vec![0; base64_reader.remaining_len()];
309+
base64_reader.read(&mut cert_bytes).unwrap();
310+
311+
// Parse the certificate from the same OpenSSH public key string, then re-encode it as binary
312+
// using KeyData's Encode implementation
313+
let cert = Certificate::from_str(certificate_str).unwrap();
314+
let key_data = KeyData::from(cert);
315+
let key_encoded = key_data.encode_vec().unwrap();
316+
317+
// Compare the decoded base64 to the parsed and re-encoded KeyData
318+
assert_eq!(cert_bytes, key_encoded);
319+
}
320+
321+
#[cfg(feature = "ecdsa")]
322+
#[test]
323+
fn encode_ecdsa_keydata() {
324+
encode_keydata(ECDSA_P256_CERT_EXAMPLE);
325+
}
326+
327+
#[cfg(feature = "ed25519")]
328+
#[test]
329+
fn encode_ed25519_keydata() {
330+
encode_keydata(ED25519_CERT_EXAMPLE);
331+
}
332+
333+
#[test]
334+
fn encode_rsa_4096_keydata() {
335+
encode_keydata(RSA_4096_CERT_EXAMPLE);
336+
}
337+
304338
#[cfg(feature = "ed25519")]
305339
#[test]
306340
fn verify_ed25519_certificate_signature() {

0 commit comments

Comments
 (0)