From 60c7375eaa93867f1b917d5eef163762ef9dc55e Mon Sep 17 00:00:00 2001 From: "Neal H. Walfield" Date: Tue, 22 Dec 2020 13:49:32 +0100 Subject: sq: Add test cases for sq key adopt. --- ...elace-encryption-subkey-signing-subkey-priv.pgp | 34 +++ sq/tests/data/keys/bob-babbage-cert-only-priv.pgp | 18 ++ ...carol-encryption-subkey-signing-subkey-priv.pgp | 34 +++ sq/tests/sq-key-adopt.rs | 313 +++++++++++++++++++++ 4 files changed, 399 insertions(+) create mode 100644 sq/tests/data/keys/alice-lovelace-encryption-subkey-signing-subkey-priv.pgp create mode 100644 sq/tests/data/keys/bob-babbage-cert-only-priv.pgp create mode 100644 sq/tests/data/keys/carol-encryption-subkey-signing-subkey-priv.pgp create mode 100644 sq/tests/sq-key-adopt.rs diff --git a/sq/tests/data/keys/alice-lovelace-encryption-subkey-signing-subkey-priv.pgp b/sq/tests/data/keys/alice-lovelace-encryption-subkey-signing-subkey-priv.pgp new file mode 100644 index 00000000..a51312c1 --- /dev/null +++ b/sq/tests/data/keys/alice-lovelace-encryption-subkey-signing-subkey-priv.pgp @@ -0,0 +1,34 @@ +-----BEGIN PGP PRIVATE KEY BLOCK----- +Comment: 5CCB BA06 74EA 5162 615E 36E9 80E5 ADE9 43CA 0DC3 +Comment: Alice Lovelace + +xVgEX+EpIRYJKwYBBAHaRw8BAQdAk2lV4viRCsrrlI7oZiAFrDW15Ub501ffvU7S +6yW72T0AAP98f2/23DyuAfXHregxl+xGYN6o6fi77Vi8dYa9wiIb2hSFwsALBB8W +CgB9BYJf4SkhAwsJBwkQgOWt6UPKDcNHFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMu +c2VxdW9pYS1wZ3Aub3Jn2zij0tETOdhNjV1/3sgGfKSJvhvxhFFtOKv5oVCjCg8D +FQoIApsBAh4BFiEEXMu6BnTqUWJhXjbpgOWt6UPKDcMAABzeAQCVmqRLJsxJGNMJ +h/YMWE13a0A7aUyPGz9/KZwPo3SPDgD8CQ68/y4TKiJ7mq1uXFe7Zy05FaMHsvvi +Eu0c7pfbsgLNIkFsaWNlIExvdmVsYWNlIDxhbGljZUBleGFtcGxlLm9yZz7CwA4E +ExYKAIAFgl/hKSEDCwkHCRCA5a3pQ8oNw0cUAAAAAAAeACBzYWx0QG5vdGF0aW9u +cy5zZXF1b2lhLXBncC5vcmeT5w4uORuaHCWWIK+rzraqwjpRPwebVp6RwmPGxQZ1 +CgMVCggCmQECmwECHgEWIQRcy7oGdOpRYmFeNumA5a3pQ8oNwwAA1cwBAJXM7Y/7 +eu1CSEZnHpGDzPU6sG1vW1DMmrGrfpOC6MaaAQC9g7cgz3lw+0JIIRALUnza1ldV +Lbo1RvwxCXimdRJLDsdYBF/hKSEWCSsGAQQB2kcPAQEHQD94W151HvcrTgi/kxaj ++GXZVR/Y88YApOIW/4wBFbggAAEA1RuFSSulQ5yhBMbbiSjdtdpBzcqy/mS0/AgQ +JHkJYZMQH8LAwgQYFgoBNAWCX+EpIQkQgOWt6UPKDcNHFAAAAAAAHgAgc2FsdEBu +b3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3Jnjj9o9zLfMD6kI28uKxXnh+OtMtHxobKf +KotBWcbP934CmwICHgG+oAQZFgoAbwWCX+EpIQkQ3EJ5dpXWJOVHFAAAAAAAHgAg +c2FsdEBub3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3JnhKRR+9OiWWt5iSsaCcLw3ubD +IOfv9HDCSwEdRnCy0/YWIQRqOx7HYjNivAZudavcQnl2ldYk5QAACdUA/2iU3Zj9 +XUaSD5JglQGEKErfKMYYUZpPOaxTUNotkvLcAP4g47bG6RQPs2KE9ofZyt2vRU+o +gML+lxdpIyjq4lDUCBYhBFzLugZ06lFiYV426YDlrelDyg3DAAC3jAD/UTXGuANz +bSSWSwezqCWkuDVF1gGCmptlybiSoQkmbfgA/1KKZ2EMfFn31bDfl5xcLv/ZpL/P +QgNNKImcXhHo+7gOx10EX+EpIRIKKwYBBAGXVQEFAQEHQJb42VFMY8HDOjz6YID9 +2VlIXRDiriB4y9/kSmbIFW1dAwEICQAA/300m8woegh+4aanj0Jj7Mg1TCAjrIsW +2Wd4iLyRVcEIEBHCwAMEGBYKAHUFgl/hKSEJEIDlrelDyg3DRxQAAAAAAB4AIHNh +bHRAbm90YXRpb25zLnNlcXVvaWEtcGdwLm9yZ6N5upvK+V2YoTHp4L4PBhl2gGMt +yj1Y9/Vr9A/Ua87TApsMAh4BFiEEXMu6BnTqUWJhXjbpgOWt6UPKDcMAALb2APsF +i7eiPmR16BxyGGlNbr7oGm1zN8YCHj2wmO2L2sS8CQEAxlzlmpOg1617ktMjBU8i +rxOum0Q1TX/9zb7N8K95jg4= +=m6EI +-----END PGP PRIVATE KEY BLOCK----- diff --git a/sq/tests/data/keys/bob-babbage-cert-only-priv.pgp b/sq/tests/data/keys/bob-babbage-cert-only-priv.pgp new file mode 100644 index 00000000..2e0135d9 --- /dev/null +++ b/sq/tests/data/keys/bob-babbage-cert-only-priv.pgp @@ -0,0 +1,18 @@ +-----BEGIN PGP PRIVATE KEY BLOCK----- +Comment: C1CF 22F6 C838 07CE 3901 6CDE 8463 B196 87EE 13BB +Comment: Bob Babbage + +xVgEX+EpfxYJKwYBBAHaRw8BAQdAIBDRr/OoeKeXc8rAd/zqLcW62SS85wP5PZlu +3nzemY0AAP9Nr3vg0+0SYqxgIyIlTgx2Cqo8Awht4On30SYMw3q3qA+XwsALBB8W +CgB9BYJf4Sl/AwsJBwkQhGOxlofuE7tHFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMu +c2VxdW9pYS1wZ3Aub3Jn6JgygN28hsGXfF45I4NJ9YVpNFcafFcHSJRXVwG6+RoD +FQoIApsBAh4BFiEEwc8i9sg4B845AWzehGOxlofuE7sAAM1vAQCN1ZW+O1LwSdwN +gTkRHRzTtat5zuEFUCY5m95GQU+95wD9E84SdcHHJIkxjAcPciVFvruxEl0QfTJZ +p3BRfZaPag3NHUJvYiBCYWJiYWdlIDxib2JAZXhhbXBsZS5vcmc+wsAOBBMWCgCA +BYJf4Sl/AwsJBwkQhGOxlofuE7tHFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMuc2Vx +dW9pYS1wZ3Aub3JnvOACsorPbBGC3UkzAaU1xwXXiWYy0G2ToPt3eZ6kHBwDFQoI +ApkBApsBAh4BFiEEwc8i9sg4B845AWzehGOxlofuE7sAAL5vAP9SHL/eMSGRDcAB +FuyttzUGfhFnLrkUsN4uqJSc1AbBvAD/T1eyKmxClkJ77lT8SglwnnBGWzowivdh +VsJaNN6UDg8= +=owhs +-----END PGP PRIVATE KEY BLOCK----- diff --git a/sq/tests/data/keys/carol-encryption-subkey-signing-subkey-priv.pgp b/sq/tests/data/keys/carol-encryption-subkey-signing-subkey-priv.pgp new file mode 100644 index 00000000..3a7ad221 --- /dev/null +++ b/sq/tests/data/keys/carol-encryption-subkey-signing-subkey-priv.pgp @@ -0,0 +1,34 @@ +-----BEGIN PGP PRIVATE KEY BLOCK----- +Comment: 0B17 34A8 2726 A5D1 D5AC 1568 1EC1 4781 FD88 09B4 +Comment: Carol + +xVgEX+E3kBYJKwYBBAHaRw8BAQdASs0ILN2aEvj/twvb1vo7wKT9ABHU6o4ujRX/ +0fFZjpsAAP4xwTpSP8Xe+lSjjGoGoalMi/mO6P1UHK5pgpPga/GS+xM9wsALBB8W +CgB9BYJf4TeQAwsJBwkQHsFHgf2ICbRHFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMu +c2VxdW9pYS1wZ3Aub3JnuxssSC5qyTYMvQCsI1mlRJdJXqozDMWspEHgO6obGNED +FQoIApsBAh4BFiEECxc0qCcmpdHVrBVoHsFHgf2ICbQAAPmBAQC6ZDOJwf/bP5ji +5uHYGe+m+AfGaPMVTFh6FQjRqBD16wEA6botbClQofBaSx3Rru3XfY3c/cnUi5Jd +dCZbSbQr0AvNGUNhcm9sIDxjYXJvbEBleGFtcGxlLm9yZz7CwA4EExYKAIAFgl/h +N5ADCwkHCRAewUeB/YgJtEcUAAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lh +LXBncC5vcmdY79mcCoWanc8hEICEfhzTTNkR9+wPApKRCcz/9coeFQMVCggCmQEC +mwECHgEWIQQLFzSoJyal0dWsFWgewUeB/YgJtAAAC/cA/1y6eWTbR+DNQOPs2fp/ +W4GY8FSuSn0z8IxCy2fPksRpAP92e/0CBkKjaSkaM9r8dKsKpT/krw7WtdAy8WJM +mHMHBcdYBF/hN5AWCSsGAQQB2kcPAQEHQGP9UaE+6d8Xd/oqtuAe0O4RVH5mXfWh +ycHiEsMO+JpSAAD/VgdJfyRSjqdCgp5YK/U8+URcYpATf6B5nHmOc/AqZe0PncLA +wgQYFgoBNAWCX+E3kAkQHsFHgf2ICbRHFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMu +c2VxdW9pYS1wZ3Aub3JnN4Uv4Qds5xBXfPiQV///S+yksEfVpNda9iLGWasIwGMC +mwICHgG+oAQZFgoAbwWCX+E3kAkQBdi56tuSqMFHFAAAAAAAHgAgc2FsdEBub3Rh +dGlvbnMuc2VxdW9pYS1wZ3Aub3Jnk4tn1OpEZd6L6U71mZYgeOSg2WYtQ1bN8hhf +aqm7xF0WIQQ9VqQkPVzDRWON+xkF2Lnq25KowQAAZ4kA/i1OTj9naXJQT1mSlLki +Vt3M/BF/o5V9lBPOTBHurb+rAQCRJgWhfbGanDzENHjAMjxaJFuEpQifVrWvZaFx +GMsvARYhBAsXNKgnJqXR1awVaB7BR4H9iAm0AAA6mAEAloE+iTDr12OLD4m4+d3/ +C8NPedm4Wh8m517WN3lM138BAIbB3YtdYwa9M73lqvjTX7yYwwF1DwkCHrpWFFHf +nFoLx10EX+E3kBIKKwYBBAGXVQEFAQEHQJqfwagmR2vflwcoPIFgoGK7zRNNpRVo +GiRvGWwpdek7AwEICQAA/0S6DsVF5rOBKrt5K6JWa6X8uY8zAHtCOeY1no2Srcxg +EN7CwAMEGBYKAHUFgl/hN5AJEB7BR4H9iAm0RxQAAAAAAB4AIHNhbHRAbm90YXRp +b25zLnNlcXVvaWEtcGdwLm9yZ2f6a257Ki8vnzBBjR4YfcGcp12EFVbPXgctYDwP +nP2qApsMAh4BFiEECxc0qCcmpdHVrBVoHsFHgf2ICbQAAFouAP96l5VD9Q+OS+fJ +g65UnJkGM0iJaykDLIHX9XL789CGcgD/RgIL13421jrt1CZm96/KtabLOSUpNeu3 +xzJTFjzE9wU= +=/Mna +-----END PGP PRIVATE KEY BLOCK----- diff --git a/sq/tests/sq-key-adopt.rs b/sq/tests/sq-key-adopt.rs new file mode 100644 index 00000000..b3097f14 --- /dev/null +++ b/sq/tests/sq-key-adopt.rs @@ -0,0 +1,313 @@ +#[cfg(test)] +mod integration { + use std::path; + + use assert_cmd::Command; + use predicates::prelude::*; + + use sequoia_openpgp as openpgp; + + use openpgp::Fingerprint; + use openpgp::Result; + use openpgp::cert::prelude::*; + use openpgp::policy::StandardPolicy; + use openpgp::parse::Parse; + use openpgp::types::KeyFlags; + + fn dir() -> path::PathBuf { + path::Path::new("tests").join("data").join("keys") + } + fn alice() -> path::PathBuf { + // Fingerprint: 5CCB BA06 74EA 5162 615E 36E9 80E5 ADE9 43CA 0DC3 + // Public-key algo: EdDSA Edwards-curve Digital Signature Algorithm + // Public-key size: 256 bits + // Secret key: Unencrypted + // Creation time: 2020-12-21 23:00:49 UTC + // Key flags: certification + // + // Subkey: 6A3B 1EC7 6233 62BC 066E 75AB DC42 7976 95D6 24E5 + // Public-key algo: EdDSA Edwards-curve Digital Signature Algorithm + // Public-key size: 256 bits + // Secret key: Unencrypted + // Creation time: 2020-12-21 23:00:49 UTC + // Key flags: signing + // + // Subkey: 827E 4397 F330 7EDA 6ABD 2A6E AD9C 461D 6D2F 0982 + // Public-key algo: ECDH public key algorithm + // Public-key size: 256 bits + // Secret key: Unencrypted + // Creation time: 2020-12-21 23:00:49 UTC + // Key flags: transport encryption, data-at-rest encryption + // + // UserID: Alice Lovelace + dir().join("alice-lovelace-encryption-subkey-signing-subkey-priv.pgp") + } + fn alice_primary() -> (Fingerprint, KeyFlags) { + ("5CCB BA06 74EA 5162 615E 36E9 80E5 ADE9 43CA 0DC3".parse().unwrap(), + KeyFlags::empty().set_certification()) + } + fn alice_signing() -> (Fingerprint, KeyFlags) { + ("6A3B 1EC7 6233 62BC 066E 75AB DC42 7976 95D6 24E5".parse().unwrap(), + KeyFlags::empty().set_signing()) + } + fn alice_encryption() -> (Fingerprint, KeyFlags) { + ("827E 4397 F330 7EDA 6ABD 2A6E AD9C 461D 6D2F 0982".parse().unwrap(), + KeyFlags::empty().set_transport_encryption().set_storage_encryption()) + } + fn bob() -> path::PathBuf { + // Fingerprint: C1CF 22F6 C838 07CE 3901 6CDE 8463 B196 87EE 13BB + // Public-key algo: EdDSA Edwards-curve Digital Signature Algorithm + // Public-key size: 256 bits + // Secret key: Unencrypted + // Creation time: 2020-12-21 23:02:23 UTC + // Key flags: certification + // + // UserID: Bob Babbage + dir().join("bob-babbage-cert-only-priv.pgp") + } + fn bob_primary() -> (Fingerprint, KeyFlags) { + ("C1CF 22F6 C838 07CE 3901 6CDE 8463 B196 87EE 13BB".parse().unwrap(), + KeyFlags::empty().set_certification()) + } + + fn carol() -> path::PathBuf { + // Fingerprint: 0B17 34A8 2726 A5D1 D5AC 1568 1EC1 4781 FD88 09B4 + // Public-key algo: EdDSA Edwards-curve Digital Signature Algorithm + // Public-key size: 256 bits + // Secret key: Unencrypted + // Creation time: 2020-12-22 00:02:24 UTC + // Key flags: certification + // + // Subkey: 3D56 A424 3D5C C345 638D FB19 05D8 B9EA DB92 A8C1 + // Public-key algo: EdDSA Edwards-curve Digital Signature Algorithm + // Public-key size: 256 bits + // Secret key: Unencrypted + // Creation time: 2020-12-22 00:02:24 UTC + // Key flags: signing + // + // Subkey: 1F47 6866 1260 CFFA D3DE B630 5652 476A 8B74 5CE5 + // Public-key algo: ECDH public key algorithm + // Public-key size: 256 bits + // Secret key: Unencrypted + // Creation time: 2020-12-22 00:02:24 UTC + // Key flags: transport encryption, data-at-rest encryption + // + // UserID: Carol + dir().join("carol-encryption-subkey-signing-subkey-priv.pgp") + } + fn carol_primary() -> (Fingerprint, KeyFlags) { + ("0B17 34A8 2726 A5D1 D5AC 1568 1EC1 4781 FD88 09B4".parse().unwrap(), + KeyFlags::empty().set_certification()) + } + fn carol_signing() -> (Fingerprint, KeyFlags) { + ("3D56 A424 3D5C C345 638D FB19 05D8 B9EA DB92 A8C1".parse().unwrap(), + KeyFlags::empty().set_signing()) + } + fn carol_encryption() -> (Fingerprint, KeyFlags) { + ("1F47 6866 1260 CFFA D3DE B630 5652 476A 8B74 5CE5".parse().unwrap(), + KeyFlags::empty().set_transport_encryption().set_storage_encryption()) + } + + fn check(output: &[u8], + key_count: usize, + keys: ((Fingerprint, KeyFlags), &[(Fingerprint, KeyFlags)])) + -> Result<()> + { + let p = &StandardPolicy::new(); + + let cert = Cert::from_bytes(output).unwrap(); + let vc = cert.with_policy(p, None).unwrap(); + + assert_eq!(key_count, vc.keys().count()); + + assert_eq!(vc.primary_key().fingerprint(), keys.0.0); + assert_eq!(vc.primary_key().key_flags(), Some(keys.0.1)); + + for (subkey, keyflags) in keys.1 { + let mut found = false; + for k in vc.keys().subkeys() { + if k.fingerprint() == *subkey { + assert_eq!(k.key_flags().as_ref(), Some(keyflags)); + found = true; + break; + } + } + assert!(found); + } + + Ok(()) + } + + #[test] + fn adopt_encryption() -> Result<()> { + // Adopt an encryption subkey. + Command::cargo_bin("sq").unwrap().arg("key").arg("adopt") + .arg(bob()) + .arg("--keyring").arg(alice()) + .arg("--key").arg(alice_encryption().0.to_hex()) + .assert() + .code(0) + .stdout(predicate::function(|output: &[u8]| -> bool { + check(output, 2, (bob_primary(), &[alice_encryption()])).is_ok() + })); + + Ok(()) + } + + #[test] + fn adopt_signing() -> Result<()> { + // Adopt a signing subkey (subkey has secret key material). + Command::cargo_bin("sq").unwrap().arg("key").arg("adopt") + .arg(bob()) + .arg("--keyring").arg(alice()) + .arg("--key").arg(alice_signing().0.to_hex()) + .assert() + .code(0) + .stdout(predicate::function(|output: &[u8]| -> bool { + check(output, 2, (bob_primary(), &[alice_signing()])).is_ok() + })); + + Ok(()) + } + + #[test] + fn adopt_certification() -> Result<()> { + // Adopt a certification subkey (subkey has secret key material). + Command::cargo_bin("sq").unwrap().arg("key").arg("adopt") + .arg(carol()) + .arg("--keyring").arg(alice()) + .arg("--key").arg(alice_primary().0.to_hex()) + .assert() + .code(0) + .stdout(predicate::function(|output: &[u8]| -> bool { + check(output, 4, (carol_primary(), &[alice_primary()])).is_ok() + })); + + Ok(()) + } + + #[test] + fn adopt_encryption_and_signing() -> Result<()> { + // Adopt an encryption subkey and a signing subkey. + Command::cargo_bin("sq").unwrap().arg("key").arg("adopt") + .arg(bob()) + .arg("--keyring").arg(alice()) + .arg("--key").arg(alice_signing().0.to_hex()) + .arg("--key").arg(alice_encryption().0.to_hex()) + .assert() + .code(0) + .stdout(predicate::function(|output: &[u8]| -> bool { + check(output, 3, + (bob_primary(), + &[alice_signing(), alice_encryption()])) + .is_ok() + })); + + Ok(()) + } + + #[test] + fn adopt_twice() -> Result<()> { + // Adopt the same an encryption subkey twice. + Command::cargo_bin("sq").unwrap().arg("key").arg("adopt") + .arg(bob()) + .arg("--keyring").arg(alice()) + .arg("--key").arg(alice_encryption().0.to_hex()) + .arg("--key").arg(alice_encryption().0.to_hex()) + .assert() + .code(0) + .stdout(predicate::function(|output: &[u8]| -> bool { + check(output, 2, (bob_primary(), &[alice_encryption()])).is_ok() + })); + + Ok(()) + } + + #[test] + fn adopt_key_appears_twice() -> Result<()> { + // Adopt the an encryption subkey that appears twice. + Command::cargo_bin("sq").unwrap().arg("key").arg("adopt") + .arg(bob()) + .arg("--keyring").arg(alice()) + .arg("--keyring").arg(alice()) + .arg("--key").arg(alice_encryption().0.to_hex()) + .assert() + .code(0) + .stdout(predicate::function(|output: &[u8]| -> bool { + check(output, 2, (bob_primary(), &[alice_encryption()])).is_ok() + })); + + Ok(()) + } + + #[test] + fn adopt_own_encryption() -> Result<()> { + // Adopt its own encryption subkey. This should be a noop. + Command::cargo_bin("sq").unwrap().arg("key").arg("adopt") + .arg(alice()) + .arg("--keyring").arg(alice()) + .arg("--key").arg(alice_encryption().0.to_hex()) + .assert() + .code(0) + .stdout(predicate::function(|output: &[u8]| -> bool { + check(output, 3, (alice_primary(), &[alice_encryption()])).is_ok() + })); + + Ok(()) + } + + #[test] + fn adopt_own_primary() -> Result<()> { + // Adopt own primary key. + Command::cargo_bin("sq").unwrap().arg("key").arg("adopt") + .arg(bob()) + .arg("--keyring").arg(bob()) + .arg("--key").arg(bob_primary().0.to_hex()) + .assert() + .code(0) + .stdout(predicate::function(|output: &[u8]| -> bool { + check(output, 2, (bob_primary(), &[bob_primary()])).is_ok() + })); + + Ok(()) + } + + #[test] + fn adopt_missing() -> Result<()> { + // Adopt a key that is not present. + Command::cargo_bin("sq").unwrap().arg("key").arg("adopt") + .arg(bob()) + .arg("--keyring").arg(bob()) + .arg("--key").arg("1234 5678 90AB CDEF 1234 5678 90AB CDEF") + .assert() + .code(1); + + Ok(()) + } + + #[test] + fn adopt_from_multiple() -> Result<()> { + // Adopt from multiple certificates simultaneously. + Command::cargo_bin("sq").unwrap().arg("key").arg("adopt") + .arg(bob()) + .arg("--keyring").arg(alice()) + .arg("--key").arg(alice_signing().0.to_hex()) + .arg("--key").arg(alice_encryption().0.to_hex()) + .arg("--keyring").arg(carol()) + .arg("--key").arg(carol_signing().0.to_hex()) + .arg("--key").arg(carol_encryption().0.to_hex()) + .assert() + .code(0) + .stdout(predicate::function(|output: &[u8]| -> bool { + check(output, 5, + (bob_primary(), + &[ + alice_signing(), alice_encryption(), + carol_signing(), carol_encryption() + ])) + .is_ok() + })); + + Ok(()) + } +} -- cgit v1.2.3