From aac71a5abc8ab0c8bc71edf660149cd3a90e6523 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C5=A0pa=C4=8Dek?= Date: Thu, 14 Jul 2022 18:35:52 +0200 Subject: [PATCH] Don't support AEAD-encrypted keys for now The previous implementation did not work. --- src/keys/openssh.rs | 24 +++----- ...sa_aes128-gcm => encrypted_rsa_aes128_gcm} | 0 ...8-gcm.pub => encrypted_rsa_aes128_gcm.pub} | 0 tests/keys/keys.rs | 58 ++++++++++++++++--- tests/keys/main.rs | 13 +++++ tests/keys/print_keys.py | 44 +++++++++----- 6 files changed, 100 insertions(+), 39 deletions(-) rename tests/keys/{encrypted_rsa_aes128-gcm => encrypted_rsa_aes128_gcm} (100%) rename tests/keys/{encrypted_rsa_aes128-gcm.pub => encrypted_rsa_aes128_gcm.pub} (100%) diff --git a/src/keys/openssh.rs b/src/keys/openssh.rs index a0ad824..416f5d3 100644 --- a/src/keys/openssh.rs +++ b/src/keys/openssh.rs @@ -32,7 +32,7 @@ pub struct OpensshKeypairNopass { pub comment: Option, } - static PEM_TAG: &str = "OPENSSH PRIVATE KEY"; +static PEM_TAG: &str = "OPENSSH PRIVATE KEY"; /// Decode a private key from OpenSSH PEM format. /// @@ -180,22 +180,12 @@ fn decrypt(raw: &RawKeypair, passphrase: &[u8]) -> Result> { decrypt.decrypt(&mut data); Ok(data) }, - CipherAlgoVariant::Aead(algo) => { - let mut decrypt = (algo.make_decrypt)(key, iv); - if raw.ciphertext.len() < algo.tag_len { - return Err(Error::Decode("OpenSSH keypair ciphertext is too short")) - } - let plaintext_len = raw.ciphertext.len() - algo.tag_len; - - // the AEAD algos assume that the first four bytes are packet length, which is handled - // in a special way, so we add dummy zeros at the beginning... - let mut data = vec![0; 4 + plaintext_len]; - data[4..].copy_from_slice(&raw.ciphertext[..plaintext_len]); - let tag = &raw.ciphertext[plaintext_len..]; - decrypt.decrypt_and_verify(0, &mut data, tag)?; - - // ...and now we must remove the dummy length - Ok(data[4..].to_vec()) + CipherAlgoVariant::Aead(_) => { + // if we ever want to implement this: + // - for aes-gcm, the associated data is empty (for SSH packets, the associated data is + // the packet length) + // - the tag is stored _after_ the ciphertext + Err(Error::Decode("OpenSSH keypair encoded with an AEAD cipher is not supported")) }, } } diff --git a/tests/keys/encrypted_rsa_aes128-gcm b/tests/keys/encrypted_rsa_aes128_gcm similarity index 100% rename from tests/keys/encrypted_rsa_aes128-gcm rename to tests/keys/encrypted_rsa_aes128_gcm diff --git a/tests/keys/encrypted_rsa_aes128-gcm.pub b/tests/keys/encrypted_rsa_aes128_gcm.pub similarity index 100% rename from tests/keys/encrypted_rsa_aes128-gcm.pub rename to tests/keys/encrypted_rsa_aes128_gcm.pub diff --git a/tests/keys/keys.rs b/tests/keys/keys.rs index 69baaf8..afc2b35 100644 --- a/tests/keys/keys.rs +++ b/tests/keys/keys.rs @@ -393,8 +393,8 @@ pub static ENCRYPTED_RSA_KEYPAIR_PEM: &'static str = concat!( ); pub fn encrypted_ed25519() -> makiko::Privkey { - let private_bytes = hex!("826246ef3ed7700959c7b2710cbd0f38baa760b393e461567e4d8467e6770194"); - let public_bytes = hex!("01a1edfb698c7645b3180380b5306e09aa4222c079b9ec0f02f38a22e836c0a8"); + let private_bytes = hex!("658ffa60f316e34424206cb7423a9083034e92cbbb9c4558767dfbcd9ae0ca7e"); + let public_bytes = hex!("b745e4c87adfbd3945c243969a127b9767132eb232279d3b16607fa70940b027"); makiko::Privkey::Ed25519(ed25519_dalek::Keypair { secret: ed25519_dalek::SecretKey::from_bytes(&private_bytes).unwrap(), public: ed25519_dalek::PublicKey::from_bytes(&public_bytes).unwrap(), @@ -402,12 +402,12 @@ pub fn encrypted_ed25519() -> makiko::Privkey { } pub static ENCRYPTED_ED25519_KEYPAIR_PEM: &'static str = concat!( "-----BEGIN OPENSSH PRIVATE KEY-----\n", - "b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABAnUPbYHd\n", - "WthhgytMG2mFLLAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIAGh7ftpjHZFsxgD\n", - "gLUwbgmqQiLAebnsDwLziiLoNsCoAAAAoN+it8qLfEr263uVqcQG4Zg2bR4Ey5yj2bwOyA\n", - "JPROjjfeiNDncRjY42dnhg1nV/WwPAXFoEvEWIltl1VWz+ypCz4sPqVgKhxYuimnWR4vxW\n", - "f+shjPGKqNZPEurQqfCNk52YkricHkHH3057KaPRqZIO3FIbmIF5UGFQ8R/JD88H/1MS5y\n", - "V7mcUEIaoQY2J0bC79O1+rOKihjWL9ViXo0oI=\n", + "b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABDTPlM9fP\n", + "LvZc+iYbijNu54AAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAILdF5Mh63705RcJD\n", + "lpoSe5dnEy6yMiedOxZgf6cJQLAnAAAAoDlV9qwZ8jlW1M/MRx/i2EZ7sMPMAGmr5hv10Y\n", + "I4HHAc0rEff6tnAerWBzz7xhcQ0fQEuFd1rkZNgFRiXKBp96VlaXLehtzpD+1G51Qeb0Z7\n", + "MUAX3nNfin30FRR9fveg5kiqVbajeMgH21Kj0O/sBIzyUJLo5ObarTLNnSqnc2ym9lNFJA\n", + "FdNqwwocrmcsXlfv6o8y7oNH1Sa2oACCRliMg=\n", "-----END OPENSSH PRIVATE KEY-----\n", ); @@ -451,3 +451,45 @@ pub static ENCRYPTED_ECDSA_P384_KEYPAIR_PEM: &'static str = concat!( "-----END OPENSSH PRIVATE KEY-----\n", ); +pub static ENCRYPTED_RSA_AES128_GCM_KEYPAIR_PEM: &'static str = concat!( + "-----BEGIN OPENSSH PRIVATE KEY-----\n", + "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\n", + "-----END OPENSSH PRIVATE KEY-----\n", +); + diff --git a/tests/keys/main.rs b/tests/keys/main.rs index 4538b4d..244cb61 100644 --- a/tests/keys/main.rs +++ b/tests/keys/main.rs @@ -72,3 +72,16 @@ fn check_decode_privkey(expected_privkey: makiko::Privkey, pem_data: &str, passw check_decode_privkey(keys::encrypted_ecdsa_p384(), keys::ENCRYPTED_ECDSA_P384_KEYPAIR_PEM, Some("password")); } + +#[test] +#[should_panic] // this is not implemented +fn test_decode_encrypted_rsa_aes128_gcm() { + // the `cryptography` library in Python does not support keys encrypted using aes128-gcm, so + // the keys.rs file does not contain `encrypted_rsa_aes128_gcm()` + let pem_data = keys::ENCRYPTED_RSA_AES128_GCM_KEYPAIR_PEM; + let decoded = makiko::keys::decode_openssh_pem_keypair(pem_data.as_bytes(), b"password".as_slice()) + .expect("could not decode keypair"); + // at least check that the keypair is valid + assert_eq!(decoded.pubkey, decoded.privkey.pubkey()); +} + diff --git a/tests/keys/print_keys.py b/tests/keys/print_keys.py index 6da2fe2..3be735e 100644 --- a/tests/keys/print_keys.py +++ b/tests/keys/print_keys.py @@ -5,7 +5,7 @@ from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey, RSAPublicKey from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePrivateKey -def print_keypair(private_key, public_key): +def print_privkey(private_key, public_key): if isinstance(private_key, Ed25519PrivateKey): raw_private = private_key.private_bytes( serialization.Encoding.Raw, @@ -76,24 +76,19 @@ def to_be_bytes(x): print("use makiko::elliptic_curve;") print() -for name in [ - "alice_ed25519", "edward_ed25519", - "ruth_rsa_1024", "ruth_rsa_2048", "ruth_rsa_4096", - "eda_ecdsa_p256", "eda_ecdsa_p384", - "encrypted_rsa", "encrypted_ed25519", - "encrypted_ecdsa_p256", "encrypted_ecdsa_p384", - #"encrypted_rsa_aes128-gcm", -]: +def print_key(name, password="", decode=True): private_file = os.path.join(base_dir, name) public_file = os.path.join(base_dir, f"{name}.pub") private_bytes = open(private_file, "rb").read() public_bytes = open(public_file, "rb").read() - private_key = serialization.load_ssh_private_key(private_bytes, b"password") - public_key = serialization.load_ssh_public_key(public_bytes) - print(f"pub fn {name}() -> makiko::Privkey {{") - print_keypair(private_key, public_key) - print(f"}}") + if decode: + private_key = serialization.load_ssh_private_key(private_bytes, b"password") + public_key = serialization.load_ssh_public_key(public_bytes) + + print(f"pub fn {name}() -> makiko::Privkey {{") + print_privkey(private_key, public_key) + print(f"}}") private_str = private_bytes.decode("utf-8") print(f"pub static {name.upper()}_KEYPAIR_PEM: &'static str = concat!(") @@ -107,3 +102,24 @@ def to_be_bytes(x): print(");") print() + +for name in [ + "alice_ed25519", + "edward_ed25519", + "ruth_rsa_1024", "ruth_rsa_2048", "ruth_rsa_4096", + "eda_ecdsa_p256", "eda_ecdsa_p384", +]: + print_key(name) + +for name in [ + "encrypted_rsa", + "encrypted_ed25519", + "encrypted_ecdsa_p256", + "encrypted_ecdsa_p384", +]: + print_key(name, "password") + +for name in [ + "encrypted_rsa_aes128_gcm", +]: + print_key(name, "password", decode=False)