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