How should SSHSIG/ssh-keygen handle signatures by a Certificate
Paul Tagliamonte
paultag at gmail.com
Fri Feb 10 15:14:18 AEDT 2023
Heyya openssh-unix-dev,
Apologies in advance for what may be the weirdest email of the year so
far on this list (a low bar, to be fair).
I've been playing a bit with OpenSSH SSHKEYs and operationalizing them
with toy proofs of concepts, trying to better understand limits
on how to properly use them and design real code accordingly.
As such, I've hit a boundary condition that I would love to get some feedback
from the list on.
I've constructed an SSHSIG signature over a simple file ('text' namespace), but
using an SSH Certificate, rather than a plain public key. Some example files
can be found at the bottom[examples].
My questions going in:
1. Will it accept an SSH certificate at all?
2. If it does, will it accept the underlying Public Key too?
3. What happens if my principals in the SSHSIG principals file don't
match in the ssh certificate
4. What happens if the certificate is expired? SSHSIG has no
'time of signature', so how do we even know? Now?
The answers according to OpenSSH 9.1 / git
1. Yes!
2. No!
3/4. No validation on the Certificate is done
I have *no* idea what the best way to solve this is, but I see a few possible
options I'd love to get some feedback from the list on:
1. Can anyone think of a valid reason *at all* to accept SSH Certificates
in an SSHSIG?
IF YES: How are you validating? This would likely require custom validation to
check the Certificate, or generate some sort of Principals file in a
real-time way using all known Certificates issued by the CA. What is
the use-case? Is ssh-keygen working in a way you're happy with?
If I were forced to make it work, I'd dump all the issued Certificates
by a CA, and list them in the principals file with matching principals
and valid-* constraints matching the issued Certificate exactly. Maybe
even do it by CA keys, and apend certs to the file as I see them? I'm
not sure. I'm having a hard time with this one. How do you know what
the signature time is?
IF NO: Should we discourage this construction in the SSHSIG spec and add a
note to implementers to avoid validating this pattern, or creating such
signatures? I don't know if this is a MAY NOT, SHOULD NOT or MUST NOT
situation if it becomes discouraged. My gut tells me I would likely do
this is a way that looks like SHOULD NOT, since I guess it *is*
possible to do this on your own if you implement the SSHSIG spec
yourself, and validate without using the principals file, rather
trusting signatures by seeding the trusted CAs.
Either way, this brings up the next question:
2. Can anyone think of a valid reason *in ssh-keygen* to accept SSH Certificates
in an SSHSIG?
IF YES: How should the logic get cleaned up (if at all)? - at minimum, I'd
expect the time to get validated since everything else was provided
explicitly by the user in the principals file. I patched ssh-keygen
locally[1] to play with how hard this would be, it wasn't bad but it
did require some duplicate logic (as originally seen in
'sshkey_cert_check_authority') on the validity time. I don't like this
patch and it shouldn't be accepted, as-is, mostly because I think
there's a *lot* more work than this involved, and a few other codepaths
for validation in 'sig_*' that aren't included with that diff. Just
there for discussion more than anything else.
Relatedly, I have *no* idea what the expected behavior becomes when you
set valid-after=timestamp / valid-before=timestamp in the principals
file *in addition* to the Certificate having a
valid-before/valid-after. I suspect all constrants would have to be met
(which is to say, it becomes the latest valid-after and earliest
valid-before of all constraints in the cert and principals file
combined)
Even if we fix the valid time stuff, I have no idea what to do about
the principals. The only thing that comes to mind is to ensure that the
principal passed to 'sig_verify' is in the certificate principals in
addition to the principals file ssh-genkey was given (so wildcards
behave as expected, etc).
I'm reluctant to validate the Certificate's signing key against the
sshsig principals file (by some creative use of
'sshkey_cert_check_authority'), since they're not trusted SSH CAs, so
trusting their Certificates seems like a mistake to me.
Perhaps pulling the public key out of the Certificate in the SSHSIG and
using that to validate? Smells like a dangerous pattern, since there's
a lot of constrants on the key usage (principals, validity, usages, key
type, extensions -- at minimum) that would get dropped for validation
[intentionally this time].
What do others think here?
IF NO: I patched ssh-keygen locally to play with this[2]. This would allow such
constructions to exist, and even makes no effort to stop someone who's
tricked ssh-keygen to create such signatures (I generated these
signatures by hand, I'm not sure how to get ssh-keygen to create them
yet - although I'm sure it's possible, I'm just not a smart man), but it
will give the user an error when *validating* the Certificates.
Hopefully this check wouldn't impact anyone in the wild? If so I would
love to learn a bit more about that use case.
Example CLI outputs:
Certificate (as seen in 'ssh-keygen -L')
Type: ssh-ed25519-cert-v01 at openssh.com user certificate
Public key: ED25519-CERT SHA256:4HTjfBTMZ31VWFfpQLTCn3QHeV7llCl0gDl47SLIpTU
Signing CA: ED25519 SHA256:4HTjfBTMZ31VWFfpQLTCn3QHeV7llCl0gDl47SLIpTU (using ssh-ed25519)
Key ID: "test key"
Serial: 1
Valid: from 2023-01-19T09:51:58 to 2023-01-19T09:52:28
Principals:
me at mydomain.fqdn
Critical Options: (none)
Extensions: (none)
Current OpenSSH as provided by my OS:
```
$ ssh-keygen -Y verify -I test2 -f principals -n text -s hello.asc < hello
Good "text" signature for test2 with ED25519-CERT key SHA256:4HTjfBTMZ31VWFfpQLTCn3QHeV7llCl0gDl47SLIpTU
```
As-is using the underlying ed25519 public key from the Certificate (which is
to say the whole Certificate needs to be in the principals file, not the
public key from the Certificate, tbh - as expected - this is likely the
least surprising one).
```
$ ssh-keygen -Y verify -I test1 -f principals -n text -s hello.asc < hello
Could not verify signature.
```
With the verify-time patch:
```
$ ssh-keygen -Y verify -O verify-time=20230119095000 -I test2 -f principals -n text -s hello.asc < hello
Could not verify signature.
$ ssh-keygen -Y verify -O verify-time=20230119095200 -I test2 -f principals -n text -s hello.asc < hello
Good "text" signature for test2 with ED25519-CERT key SHA256:4HTjfBTMZ31VWFfpQLTCn3QHeV7llCl0gDl47SLIpTU
$ ssh-keygen -Y verify -O verify-time=20230119095900 -I test2 -f principals -n text -s hello.asc < hello
Could not verify signature.
```
With the deny Certificates patch:
```
$ ssh-keygen -Y verify -I test2 -f principals -n text -s hello.asc < hello
sig_verify: ssh certificates are not supported in sshsig
Could not verify signature.
```
[1]: verify-time patch
```
diff --git a/ssh-keygen.c b/ssh-keygen.c
index ae05440f..716e9486 100644
--- a/ssh-keygen.c
+++ b/ssh-keygen.c
@@ -2815,6 +2815,18 @@ sig_verify(const char *signature, const char *sig_namespace,
}
}
+ if (verify_time && sshkey_is_cert(sign_key)) {
+ /* If -O verify-time is set, validate the Certificate too */
+ if (verify_time <= sign_key->cert->valid_after) {
+ debug3_fr(r, "Certificate invalid: not yet valid");
+ goto done;
+ }
+ if (verify_time >= sign_key->cert->valid_before) {
+ debug3_fr(r, "Certificate invalid: expired");
+ goto done;
+ }
+ }
+
if (allowed_keys != NULL && (r = sshsig_check_allowed_keys(allowed_keys,
sign_key, principal, sig_namespace, verify_time)) != 0) {
debug3_fr(r, "sshsig_check_allowed_keys");
```
[2]: deny Certificates
```
diff --git a/ssh-keygen.c b/ssh-keygen.c
index ae05440f..ce1e1c29 100644
--- a/ssh-keygen.c
+++ b/ssh-keygen.c
@@ -2815,6 +2815,11 @@ sig_verify(const char *signature, const char *sig_namespace,
}
}
+ if (sshkey_is_cert(sign_key)) {
+ error_f("ssh certificates are not supported in sshsig");
+ goto done;
+ }
+
if (allowed_keys != NULL && (r = sshsig_check_allowed_keys(allowed_keys,
sign_key, principal, sig_namespace, verify_time)) != 0) {
debug3_fr(r, "sshsig_check_allowed_keys");
@@ -2883,6 +2888,10 @@ sig_find_principals(const char *signature, const char *allowed_keys,
error_fr(r, "sshsig_get_pubkey");
goto done;
}
+ if (sshkey_is_cert(sign_key)) {
+ error_f("ssh certificates are not supported in sshsig");
+ goto done;
+ }
if ((r = sshsig_find_principals(allowed_keys, sign_key,
verify_time, &principals)) != 0) {
if (r != SSH_ERR_KEY_NOT_FOUND)
```
[examples]:
'hello' file:
```
no one actually looks at this do they
```
'hello.asc' file:
```
-----BEGIN SSH SIGNATURE-----
U1NIU0lHAAAAAQAAAUYAAAAgc3NoLWVkMjU1MTktY2VydC12MDFAb3BlbnNzaC5j
b20AAAAgttJe6gKGMeH+MNY6L3Ip0BuJnZebPvkuW6Zl9ZJs35wAAAAggO3sKO3l
Z/uhRpVlFNbxnoLG09Xx7PEcUKtootiXWm0AAAAAAAAAAQAAAAEAAAAIdGVzdCBr
ZXkAAAAUAAAAEG1lQG15ZG9tYWluLmZxZG4AAAAAY8lZDgAAAABjyVksAAAAAAAA
AAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACCA7ewo7eVn+6FGlWUU1vGegsbT
1fHs8RxQq2ii2JdabQAAAFMAAAALc3NoLWVkMjU1MTkAAABAiAoeiwtDINpEZAWl
tfqG+MaxBk4YLZcUiDQKblxgBy2bqc52tLQE24jn4LKj96ZxHNITNBdvY0fODMiA
dBggAQAAAAR0ZXh0AAAAAAAAAAZzaGEyNTYAAABTAAAAC3NzaC1lZDI1NTE5AAAA
QEwhhhboXmBwfFEh5+ztPUfjVtPTvzboCZ/VBQyUvBsEitSZuoC1MrDLCP54qkRX
/uSGRXWwZEWLL+SJeeJWTQ8=
-----END SSH SIGNATURE-----
```
'principals' file:
```
test1 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIDt7Cjt5Wf7oUaVZRTW8Z6CxtPV8ezxHFCraKLYl1pt
test2 ssh-ed25519-cert-v01 at openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAILbSXuoChjHh/jDWOi9yKdAbiZ2Xmz75LlumZfWSbN+cAAAAIIDt7Cjt5Wf7oUaVZRTW8Z6CxtPV8ezxHFCraKLYl1ptAAAAAAAAAAEAAAABAAAACHRlc3Qga2V5AAAAFAAAABBtZUBteWRvbWFpbi5mcWRuAAAAAGPJWQ4AAAAAY8lZLAAAAAAAAAAAAAAAAAAAADMAAAALc3NoLWVkMjU1MTkAAAAggO3sKO3lZ/uhRpVlFNbxnoLG09Xx7PEcUKtootiXWm0AAABTAAAAC3NzaC1lZDI1NTE5AAAAQIgKHosLQyDaRGQFpbX6hvjGsQZOGC2XFIg0Cm5cYActm6nOdrS0BNuI5+Cyo/emcRzSEzQXb2NHzgzIgHQYIAE=
```
paultag
--
:wq
More information about the openssh-unix-dev
mailing list