When computing a rsa-sha2 signature, the signature value is not extended to the same number of octets as the key modulus. This leads to authentication failures at least for the Erlang public_key library.
Description of the Bug
You can reproduce the issue as follows.
- Enable the SSH agent of GnuPG with the enable-ssh-support option.
- Setup the agent socket in your shell.
$ export SSH_AUTH_SOCK="$(gpgconf --list-dirs agent-ssh-socket)"- Add the key test.asc to your keyring. It is important to reproduce the issue with the provided keys. You will find the key in the OpenSSH-Format in test and the corresponding public key in test.pub.
$ gpg --import test.asc- Add the keys keygrip to the sshcontrol file.
8AE5C1CD8EE6BF269E9D72A31A9C6BC7F2267406 0
- Check for the key to be present in the agent.
$ ssh-add -l 3072 SHA256:m7ZbRbJqW/0T7eWLX6pTGvw/yv1r+wEJTVB75RwDjx8 test (RSA)
- Compute two signatures with the paramiko Python package.
import paramiko
a = paramiko.agent.Agent()
k = a.get_keys()[0]
sig = k.sign_ssh_data(bytes([ 196 ]), algorithm='rsa-sha2-256')
print(len(sig[20:]))
with open("196.bin", "wb") as f:
f.write(sig[20:])
sig = k.sign_ssh_data(bytes([ 197 ]), algorithm='rsa-sha2-256')
print(len(sig[20:]))
with open("197.bin", "wb") as f:
f.write(sig[20:])The term [20:] extracts the raw signature value from the agent protocol. The output was written to the files 196.bin and 197.bin. You will find that the signature 197.bin just consists of 383 octets whereas 196.bin consists of 384 octets which matches the key modulus length of 3072 bits.
$ ls -l 196.bin 197.bin -rw-r--r-- 1 demo demo 384 27. Okt 12:07 196.bin -rw-r--r-- 1 demo demo 383 27. Okt 12:33 197.bin
It's because the 8 most significant bits of the signature value in the latter case are all zero und thus truncated in the output. If this case occurs during an authentication attempt, the Erlang library will refuse it with an error message.
RFC 8332 section 3 specifies the format of the SSH signature rsa-sha2-256 and rsa-sha2-512:
The resulting signature is encoded as follows:
string "rsa-sha2-256" / "rsa-sha2-512"
string rsa_signature_blobThe value for 'rsa_signature_blob' is encoded as a string that
contains an octet string S (which is the output of RSASSA-PKCS1-v1_5)
and that has the same length (in octets) as the RSA modulus. When S
contains leading zeros, there exist signers that will send a shorter
encoding of S that omits them. A verifier MAY accept shorter
encodings of S with one or more leading zeros omitted.
It specifies the signature encoding must have the same number of octets as the key modulus. Supporting truncated values is truly optional and cannot be relied on.
In contrast for ssh-rsa signatures, RFC 4253 specifies in section 6.6 for the signature encoding that it must not have any padding.
The resulting signature is encoded as follows:
string "ssh-rsa"
string rsa_signature_blobThe value for 'rsa_signature_blob' is encoded as a string containing
s (which is an integer, without lengths or padding, unsigned, and in
network byte order).
So in this case, truncating leading 0-bytes should still be mandatory.
Related information
If you load the same key into the OpenSSH agent ...
$ unset SSH_AUTH_SOCK $ eval $(ssh-agent) $ ssh-add test Identity added: test (test)
... and perform step 6 again, you will get a the same signature, but with a leading 0-byte in the latter case.
Proposed resolution
Signature values for rsa-sha2-256 and rsa-sha2-512 should be extended to the length of the key modulus.