# X.509 Certificates

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

This module parses DER-encoded X.509v3 certificates for TLS 1.3 style certificate checks. It is a compact parser, verifier, trust-store checker, TLS Certificate-message splitter, and small self-signed certificate generator.

It is non-allocating while parsing: `fio_x509_cert_s` stores pointers into the original DER bytes. Keep those bytes alive. Certificates dislike disappearing floors.

**Scope note:** this is a minimal TLS-oriented X.509 implementation, not a full PKI policy engine. Not all X.509 features are supported.

## Supported Pieces

- RSA, ECDSA P-256, ECDSA P-384, and Ed25519 public keys.
- RSA PKCS#1 v1.5, RSA-PSS, ECDSA, and Ed25519 signature verification.
- Validity period checks.
- Hostname matching using SAN DNS names and CN fallback, with one-label wildcards.
- Basic Constraints and Key Usage.
- Certificate chain validation against an optional trust store.
- TLS 1.3 Certificate message parsing.
- Self-signed certificate generation for Ed25519 and P-256 keys.

## Main Types

### Key and Signature Enums

```c
typedef enum {
  FIO_X509_KEY_UNKNOWN = 0,
  FIO_X509_KEY_RSA = 1,
  FIO_X509_KEY_ECDSA_P256 = 2,
  FIO_X509_KEY_ECDSA_P384 = 3,
  FIO_X509_KEY_ED25519 = 4,
} fio_x509_key_algo_e;
```

```c
typedef enum {
  FIO_X509_SIGNATURE_UNKNOWN = 0,
  FIO_X509_SIGNATURE_RSA_PKCS1_SHA256 = 1,
  FIO_X509_SIGNATURE_RSA_PKCS1_SHA384 = 2,
  FIO_X509_SIGNATURE_RSA_PKCS1_SHA512 = 3,
  FIO_X509_SIGNATURE_RSA_PSS_SHA256 = 4,
  FIO_X509_SIGNATURE_RSA_PSS_SHA384 = 5,
  FIO_X509_SIGNATURE_RSA_PSS_SHA512 = 6,
  FIO_X509_SIGNATURE_ECDSA_SHA256 = 7,
  FIO_X509_SIGNATURE_ECDSA_SHA384 = 8,
  FIO_X509_SIGNATURE_ED25519 = 9,
} fio_x509_signature_algo_e;
```

### Key Usage and Errors

`fio_x509_key_usage_e` defines RFC 5280 key-usage bits such as `FIO_X509_KU_DIGITAL_SIGNATURE`, `FIO_X509_KU_KEY_ENCIPHERMENT`, and `FIO_X509_KU_KEY_CERT_SIGN`. ASN.1 bit strings use MSB-first ordering, so the constants look a little backwards until they save you from off-by-one sadness.

`fio_x509_error_e` gives chain validation results: `FIO_X509_OK`, parse failure, expired/not-yet-valid, signature failure, issuer mismatch, not-a-CA, no trust anchor, hostname mismatch, empty chain, and chain-too-long.

### `fio_x509_trust_store_s`

```c
typedef struct {
  const uint8_t **roots;
  const size_t *root_lens;
  size_t root_count;
} fio_x509_trust_store_s;
```

A simple root store: arrays of DER certificate pointers and lengths.

### `fio_tls_cert_entry_s`

```c
typedef struct {
  const uint8_t *cert;
  size_t cert_len;
} fio_tls_cert_entry_s;
```

One certificate entry extracted from a TLS 1.3 `Certificate` handshake message.

### `fio_x509_cert_s`

```c
typedef struct fio_x509_cert_s fio_x509_cert_s;
```

The parsed certificate structure uses non-owning buffer views
(`fio_buf_info_s` / `fio_ubuf_info_s`) and packs to 256 bytes with no
interior padding:

- `verified`: non-zero when a TLS backend verified this certificate's chain
  (peer inspection only; always zero after `fio_x509_parse`);
- `der`: a reference to the original DER bytes (not a copy);
- `fingerprint`: 32-byte SHA-256 of the DER bytes (see `fio_x509_fingerprint`);
- `version`, `serial`, and validity timestamps;
- raw subject and issuer DNs;
- subject CN;
- public key type and key data;
- signature algorithm and signature bytes;
- TBS certificate bytes used for verification;
- Basic Constraints (`is_ca`), Key Usage (`has_key_usage`, `key_usage`), and
  SAN data (first DNS name, first IP address, and the raw SAN extension for
  iterating the rest).

All view pointers reference the original DER buffer.

## Parsing and Single-Certificate Checks

### `fio_x509_parse`

```c
SFUNC int fio_x509_parse(fio_x509_cert_s *cert,
                         const uint8_t *der_data,
                         size_t der_len);
```

Parses one DER certificate. `cert` is zeroed first. Returns `0` on success, `-1` on parse error.

### `fio_x509_fingerprint`

```c
SFUNC void fio_x509_fingerprint(fio_x509_cert_s *cert);
```

Computes the SHA-256 of the certificate's DER bytes into
`cert->fingerprint` (32 raw bytes). Call after `fio_x509_parse`; hashing is
lazy so parsing never pays for it. Requires the SHA2 module.

### `fio_x509_verify_signature`

```c
SFUNC int fio_x509_verify_signature(const fio_x509_cert_s *cert,
                                    const fio_x509_cert_s *issuer);
```

Verifies that `issuer` signed `cert`, using the issuer public key and the certificate signature algorithm. Returns `0` when the signature is valid, `-1` otherwise.

### `fio_x509_check_validity`

```c
FIO_IFUNC int fio_x509_check_validity(const fio_x509_cert_s *cert,
                                      int64_t current_time);
```

Checks `not_before <= current_time <= not_after`. Returns `0` when the certificate is in its validity window.

### `fio_x509_match_hostname`

```c
SFUNC int fio_x509_match_hostname(const fio_x509_cert_s *cert,
                                  const char *hostname,
                                  size_t hostname_len);
```

Checks whether a certificate matches a hostname. SAN DNS entries are supported, with CN fallback. Wildcards are one-label wildcards such as `*.example.com`; they do not swallow `a.b.example.com`.

Returns `0` for a match, `-1` for no match.

Distinguished Names are compared as raw DER with the core
`FIO_BUF_INFO_IS_EQ` macro (e.g.,
`FIO_BUF_INFO_IS_EQ(cert.issuer, root.subject)`).

## Chain Validation

### `fio_x509_verify_chain`

```c
SFUNC int fio_x509_verify_chain(const uint8_t **certs,
                                const size_t *cert_lens,
                                size_t cert_count,
                                const char *hostname,
                                int64_t current_time,
                                fio_x509_trust_store_s *trust_store);
```

Validates a certificate chain for TLS-style use.

Expected order:

1. `certs[0]`: end-entity / server certificate.
2. `certs[1]`: intermediate that signed `certs[0]`.
3. `certs[n-1]`: closest-to-root certificate.

Validation parses all certificates, checks validity periods, optionally matches the hostname, verifies each signature with the next certificate, checks issuer/subject DN links, requires CA certificates where needed, and optionally checks the final certificate against `trust_store`.

Trust anchors are matched by (subject DN, public key): the anchor's key must
verify the final certificate's signature. This also covers self-signed
certificates — a same-named impostor with a different key is rejected.

Returns `FIO_X509_OK` (`0`) on success, or a `FIO_X509_ERR_*` code.

### `fio_x509_is_trusted`

```c
SFUNC int fio_x509_is_trusted(const fio_x509_cert_s *cert,
                              fio_x509_trust_store_s *trust_store);
```

Checks whether `cert` appears in the trust store by subject DN match. Returns `0` if trusted, `-1` if not found.

### `fio_x509_error_str`

```c
FIO_IFUNC const char *fio_x509_error_str(int error);
```

Returns a static string for a chain validation error code.

## TLS Certificate Messages

```c
#define FIO_TLS_CERT_PARSE_ERROR ((size_t)-1)
```

### `fio_tls_parse_certificate_message`

```c
SFUNC size_t fio_tls_parse_certificate_message(fio_tls_cert_entry_s *entries,
                                               size_t max_entries,
                                               const uint8_t *data,
                                               size_t data_len);
```

Parses a TLS 1.3 `Certificate` message body, after the handshake header. It extracts certificate entries and skips per-certificate extensions.

Returns the number of certificates parsed, or `FIO_TLS_CERT_PARSE_ERROR` on malformed input.

## Self-Signed Certificate Generation

Generation supports Ed25519 and P-256 key pairs.

```c
typedef enum {
  FIO_X509_KEYPAIR_ED25519 = 1,
  FIO_X509_KEYPAIR_P256 = 2,
} fio_x509_keypair_type_e;
```

`fio_x509_keypair_s` stores the selected key type, secret key bytes, public key bytes, and their lengths. Use `fio_x509_keypair_clear` when done.

`fio_x509_cert_options_s` controls the subject CN, organization, organizational unit, country, validity window, SAN DNS names, CA flag, and key-usage bits.

### `fio_x509_keypair_ed25519`

```c
SFUNC int fio_x509_keypair_ed25519(fio_x509_keypair_s *keypair);
```

Generates an Ed25519 key pair for certificate signing. Requires the Ed25519 module to be included. Returns `0` on success.

### `fio_x509_keypair_p256`

```c
SFUNC int fio_x509_keypair_p256(fio_x509_keypair_s *keypair);
```

Generates a P-256 key pair for certificate signing. Requires the P-256 module to be included. Returns `0` on success.

### `fio_x509_self_signed_cert`

```c
SFUNC size_t fio_x509_self_signed_cert(uint8_t *buf,
                                       size_t buf_len,
                                       const fio_x509_keypair_s *keypair,
                                       const fio_x509_cert_options_s *options);
```

Writes a DER-encoded self-signed X.509v3 certificate. Call with `buf == NULL` to get a worst-case buffer size, then call again with a real buffer.

Returns bytes written, or `0` on error.

### `fio_x509_keypair_clear`

```c
FIO_IFUNC void fio_x509_keypair_clear(fio_x509_keypair_s *keypair);
```

Securely clears key material and zeros the structure.

## Example: Parse and Check Hostname

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

int check_cert(const uint8_t *der, size_t der_len, int64_t now) {
  fio_x509_cert_s cert;
  if (fio_x509_parse(&cert, der, der_len))
    return -1;
  if (fio_x509_check_validity(&cert, now))
    return -1;
  if (fio_x509_match_hostname(&cert, "example.com", 11))
    return -1;
  return 0;
}
```

## Practical Notes

- Define the crypto modules you need before including the STL: RSA, P-256, P-384, Ed25519, SHA-2, and ASN.1 may all matter depending on certificate algorithms.
- `trust_store == NULL` skips root trust checking in `fio_x509_verify_chain`; useful for structural tests, not for real trust decisions.
- Hostname checks are DNS-name focused. IP address SAN handling is not a full replacement for platform certificate validation.
- Parsed strings and DNs are raw certificate data. Normalize policy elsewhere if your application needs browser-grade PKI behavior.

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