# RSA Signatures

```c
#define FIO_RSA
#include "fio-stl.h"
```

RSA here means signature work for TLS and X.509: verify PKCS#1 v1.5 signatures, verify RSA-PSS signatures, and create RSA-PSS signatures for TLS 1.3 `CertificateVerify`.

Supported modulus sizes are 2048, 3072, and 4096 bits. Smaller keys do not get a party hat.

**Security note:** this implementation has not been independently audited. Prefer a tested crypto library for high-value private keys, compliance, HSM integration, or anything that keeps auditors awake.

## Constants and Hash IDs

```c
#define FIO_RSA_MAX_BITS  4096
#define FIO_RSA_MAX_BYTES (FIO_RSA_MAX_BITS / 8)
#define FIO_RSA_MAX_WORDS (FIO_RSA_MAX_BYTES / 8)
```

```c
typedef enum {
  FIO_RSA_HASH_SHA256 = 0,
  FIO_RSA_HASH_SHA384 = 1,
  FIO_RSA_HASH_SHA512 = 2,
} fio_rsa_hash_e;
```

`hash_len` must match the selected hash: 32 bytes for SHA-256, 48 for SHA-384, and 64 for SHA-512.

## Key Types

### `fio_rsa_pubkey_s`

```c
typedef struct {
  const uint8_t *n;
  size_t n_len;
  const uint8_t *e;
  size_t e_len;
} fio_rsa_pubkey_s;
```

Public key for verification. `n` is the modulus and `e` is the public exponent, both as big-endian byte arrays. This matches the DER layout used in X.509 certificates.

### `fio_rsa_privkey_s`

```c
typedef struct {
  const uint8_t *n; size_t n_len;
  const uint8_t *d; size_t d_len;
  const uint8_t *e; size_t e_len;
  const uint8_t *p; size_t p_len;
  const uint8_t *q; size_t q_len;
  const uint8_t *dP; size_t dP_len;
  const uint8_t *dQ; size_t dQ_len;
  const uint8_t *qInv; size_t qInv_len;
} fio_rsa_privkey_s;
```

Private key for RSA-PSS signing. `n` and `d` are required. The public exponent `e` and CRT fields (`p`, `q`, `dP`, `dQ`, `qInv`) are optional.

When CRT parameters are available, signing uses CRT with message blinding. If only `n` and `d` are available, signing falls back to the non-CRT path.

## Verification

### `fio_rsa_verify_pkcs1`

```c
SFUNC int fio_rsa_verify_pkcs1(const uint8_t *sig,
                               size_t sig_len,
                               const uint8_t *msg_hash,
                               size_t hash_len,
                               fio_rsa_hash_e hash_alg,
                               const fio_rsa_pubkey_s *key);
```

Verifies an RSA PKCS#1 v1.5 signature with `DigestInfo` encoding.

This covers the X.509 signature algorithms commonly named:

- `sha256WithRSAEncryption`
- `sha384WithRSAEncryption`
- `sha512WithRSAEncryption`

Parameters:

- `sig`: signature bytes; length must match the modulus length.
- `sig_len`: signature length in bytes.
- `msg_hash`: pre-computed message hash.
- `hash_len`: 32, 48, or 64 bytes.
- `hash_alg`: matching `FIO_RSA_HASH_*` value.
- `key`: RSA public key.

Returns `0` for a valid signature, `-1` on invalid input or a bad signature.

### `fio_rsa_verify_pss`

```c
SFUNC int fio_rsa_verify_pss(const uint8_t *sig,
                             size_t sig_len,
                             const uint8_t *msg_hash,
                             size_t hash_len,
                             fio_rsa_hash_e hash_alg,
                             const fio_rsa_pubkey_s *key);
```

Verifies an RSA-PSS signature. This is the RSA signature style TLS 1.3 uses for `CertificateVerify`.

The implementation uses:

- MGF1 with the same hash function;
- salt length equal to the hash length;
- trailer byte `0xBC`.

Returns `0` for a valid signature, `-1` on invalid input or a bad signature.

## Signing

### `fio_rsa_sign_pss`

```c
SFUNC int fio_rsa_sign_pss(uint8_t *signature,
                           size_t *sig_len,
                           const uint8_t *msg_hash,
                           size_t hash_len,
                           fio_rsa_hash_e hash_alg,
                           const fio_rsa_privkey_s *key);
```

Creates an RSA-PSS signature using `RSASSA-PSS-SIGN` from RFC 8017, with the TLS 1.3 settings listed above.

- `signature` must have room for `key->n_len` bytes.
- `sig_len` receives the signature length, which equals `key->n_len` on success.
- `msg_hash` is already hashed; this function does not hash the message.
- `key` must include `n` and `d`; CRT fields are optional.

Returns `0` on success, `-1` on error.

PKCS#1 v1.5 signing is not exposed. TLS 1.3 does not want it for `CertificateVerify`, and this header agrees.

## Example: Verify an RSA-PSS Signature

```c
#define FIO_RSA
#define FIO_SHA2
#include "fio-stl.h"

int verify_pss(const uint8_t *sig,
               size_t sig_len,
               const uint8_t *modulus,
               size_t modulus_len,
               const uint8_t *exponent,
               size_t exponent_len,
               const void *message,
               size_t message_len) {
  fio_rsa_pubkey_s key = {
      .n = modulus,
      .n_len = modulus_len,
      .e = exponent,
      .e_len = exponent_len,
  };

  fio_u256 hash = fio_sha256(message, message_len);
  return fio_rsa_verify_pss(sig, sig_len, hash.u8, 32,
                            FIO_RSA_HASH_SHA256, &key);
}
```

## Practical Notes

- The signature length must match the modulus length.
- The public exponent is usually `65537` (`0x010001`), but the API accepts DER-style big-endian bytes.
- The module works on pre-computed hashes. Pick the hash to match the certificate or protocol signature algorithm.
- For X.509 parsing, this module is normally used through [X.509](https://facil.io/0.8.x/http-1-x-parser/), which extracts the RSA key and dispatches verification.

------------------------------------------------------------
