diff --git a/CHANGELOG.md b/CHANGELOG.md index a334cc7634..98728dc742 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,9 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). * `jj metaedit` now accepts `-m`/`--message` option to non-interactively update the change description. +* The `CryptographicSignature.key()` template method now also works for SSH + signatures and returns the corresponding public key fingerprint. + ### Fixed bugs * `jj metaedit --author-timestamp` twice with the same value no longer diff --git a/docs/templates.md b/docs/templates.md index 9e206118e2..4fd8cfb063 100644 --- a/docs/templates.md +++ b/docs/templates.md @@ -240,9 +240,12 @@ _Conversion: `Boolean`: no, `Serialize`: no, `Template`: no_ The following methods are defined. -* `.status() -> String`: The signature's status (`"good"`, `"bad"`, `"unknown"`, `"invalid"`). -* `.key() -> String`: The signature's key id representation (for GPG, this is the key fingerprint). -* `.display() -> String`: The signature's display string (for GPG this is the formatted primary user ID). +* `.status() -> String`: The signature's status (`"good"`, `"bad"`, `"unknown"`, + `"invalid"`). +* `.key() -> String`: The signature's key id representation (for GPG and SSH, + this is the public key fingerprint). +* `.display() -> String`: The signature's display string (for GPG, this is the + formatted primary user ID; for SSH, this is the principal). !!! warning diff --git a/lib/src/ssh_signing.rs b/lib/src/ssh_signing.rs index 5473cb0e15..af0ddc2731 100644 --- a/lib/src/ssh_signing.rs +++ b/lib/src/ssh_signing.rs @@ -111,6 +111,15 @@ fn ensure_key_as_file(key: &str) -> SshResult) -> SshResult { + Ok(parse_utf8_string(output)? + .rsplit_once(' ') + .ok_or(SshError::BadResult)? + .1 + .trim() + .into()) +} + impl SshBackend { pub fn new( program: OsString, @@ -276,7 +285,10 @@ impl SigningBackend for SshBackend { Ok(_) => SigStatus::Good, Err(_) => SigStatus::Bad, }; - Ok(Verification::new(status, None, Some(principal))) + + let key = result.ok().map(parse_fingerprint).transpose()?; + + Ok(Verification::new(status, key, Some(principal))) } _ => { command @@ -290,9 +302,9 @@ impl SigningBackend for SshBackend { let result = run_command(&mut command, data); match result { - Ok(_) => Ok(Verification::new( + Ok(result) => Ok(Verification::new( SigStatus::Unknown, - None, + Some(parse_fingerprint(result)?), Some("Signature OK. Unknown principal".into()), )), Err(_) => Ok(Verification::new(SigStatus::Bad, None, None)), diff --git a/lib/tests/test_ssh_signing.rs b/lib/tests/test_ssh_signing.rs index c0268c9c8a..68dd0c711b 100644 --- a/lib/tests/test_ssh_signing.rs +++ b/lib/tests/test_ssh_signing.rs @@ -37,6 +37,8 @@ y2yxhhHnagH52avUqw5hAAAAAAECAwQF static PUBLIC_KEY: &str = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGj+J6N6SO+4P8dOZqfR1oiay2yxhhHnagH52avUqw5h"; +static FINGERPRINT: &str = "SHA256:CaeelDOMvTqGZPjAS9fdbnACrLg68N1Bb9ux5y6GjGw"; + struct SshEnvironment { _keys: tempfile::TempDir, private_key_path: PathBuf, @@ -145,7 +147,7 @@ fn ssh_signing_roundtrip() { let check = backend.verify(data, &signature).unwrap(); assert_eq!(check.status, SigStatus::Good); - + assert_eq!(check.key.unwrap(), FINGERPRINT); assert_eq!(check.display.unwrap(), "test@example.com"); let check = backend.verify(b"invalid-commit-data", &signature).unwrap(); @@ -167,6 +169,7 @@ fn ssh_signing_bad_allowed_signers() { let check = backend.verify(data, &signature).unwrap(); assert_eq!(check.status, SigStatus::Unknown); + assert_eq!(check.key.unwrap(), FINGERPRINT); assert_eq!(check.display.unwrap(), "Signature OK. Unknown principal"); } @@ -184,6 +187,7 @@ fn ssh_signing_missing_allowed_signers() { let check = backend.verify(data, &signature).unwrap(); assert_eq!(check.status, SigStatus::Unknown); + assert_eq!(check.key.unwrap(), FINGERPRINT); assert_eq!(check.display.unwrap(), "Signature OK. Unknown principal"); } @@ -220,4 +224,5 @@ fn ssh_signing_revocation_unrevoked() { let check = backend.verify(data, &signature).unwrap(); assert_eq!(check.status, SigStatus::Good); + assert_eq!(check.key.unwrap(), FINGERPRINT); }