Page MenuHome GnuPG

`rsa-sha2` signature values are improperly truncated
Closed, ResolvedPublic

Description

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.

  1. Enable the SSH agent of GnuPG with the enable-ssh-support option.
  2. Setup the agent socket in your shell.
$ export SSH_AUTH_SOCK="$(gpgconf --list-dirs agent-ssh-socket)"
  1. 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
  1. Add the keys keygrip to the sshcontrol file.
8AE5C1CD8EE6BF269E9D72A31A9C6BC7F2267406 0
  1. Check for the key to be present in the agent.
$ ssh-add -l
3072 SHA256:m7ZbRbJqW/0T7eWLX6pTGvw/yv1r+wEJTVB75RwDjx8 test (RSA)
  1. 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_blob

The 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_blob

The 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.

Details

Version
2.4.8

Event Timeline

gniibe triaged this task as Normal priority.
gniibe added a subscriber: gniibe.

Thank you for your report.

Here is a change of my experiment:

This is a rough experiment so that I can understand the issue.
With this change, the step 6 above returns 384-byte correctly.

Thanks for the quick response. I can confirm the patch works in my setup.

gniibe mentioned this in Unknown Object (Maniphest Task).Nov 3 2025, 3:52 AM
gniibe changed the task status from Open to Testing.Nov 4 2025, 5:56 AM

Pushed the revised change to master.

gniibe mentioned this in Unknown Object (Maniphest Task).Nov 10 2025, 2:51 AM