Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 6 additions & 3 deletions docs/templates.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
18 changes: 15 additions & 3 deletions lib/src/ssh_signing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,15 @@ fn ensure_key_as_file(key: &str) -> SshResult<Either<PathBuf, tempfile::TempPath
Ok(either::Right(pub_key_path))
}

fn parse_fingerprint(output: Vec<u8>) -> SshResult<String> {
Ok(parse_utf8_string(output)?
.rsplit_once(' ')
.ok_or(SshError::BadResult)?
.1
.trim()
.into())
}

impl SshBackend {
pub fn new(
program: OsString,
Expand Down Expand Up @@ -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
Expand All @@ -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)),
Expand Down
7 changes: 6 additions & 1 deletion lib/tests/test_ssh_signing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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(), "[email protected]");

let check = backend.verify(b"invalid-commit-data", &signature).unwrap();
Expand All @@ -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");
}

Expand All @@ -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");
}

Expand Down Expand Up @@ -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);
}