Skip to content

Authentication

The server verifies the protection of every message before your code sees it. It has no credential store of its own. You supply two lookups:

  • WithSecretLookup finds the shared secret of a MAC-protected message.
  • WithCertificateLookup finds the certificate of a signature-protected message.

Without a lookup, messages with that kind of protection are refused: MAC-protected ones with badMessageCheck and signed ones with signerNotTrusted.

Shared secrets

type SecretLookup interface {
    LookupSecret(sender pkix.Name, senderKID []byte) ([]byte, error)
}

RFC 9810 section 5.1.1 covers two cases:

  • Known sender. The sender field carries the name and senderKID should be set. Either or both may locate the secret.
  • Unknown sender, usually a first enrollment. The sender is a NULL-DN and senderKID carries the reference number of the secret (RFC 4210 section 5.1.3.1).

The reference and the secret work like a user name and a password. Two clients that share a reference cannot be told apart and one could obtain certificates in the other's name. Enforce unique references when you provision secrets:

func Register(senderKID, secret []byte) error {
    if exists(senderKID) {
        return errors.New("senderKID already registered")
    }
    store[string(senderKID)] = secret
    return nil
}

The server keeps a MAC client's transactions and its share of WithMaxTransactionsPerCredential apart from other clients by the shared secret itself, whichever field the lookup used to find it. The sender name and senderKID a request carries do not select the quota, so one secret cannot get a second quota by naming a different sender. Clients that share one secret share one quota. Clients that send only a name, as RFC 4210 allows, keep separate transactions as long as their secrets differ.

The client chooses the MAC iteration count and the server runs every iteration before it can check the MAC. WithMACIterationLimits bounds that work, 100 to 1000000 by default. For PBMAC1 the maximum bounds the whole PBKDF2 derivation, the iterations multiplied by the number of PRF output blocks the requested key length needs, so a long key from a short PRF cannot multiply the work. A request outside the range is refused with badMessageCheck and the text "MAC protection parameters not accepted" before any key derivation. The minimum is the 100 that RFC 4211 section 4.4 requires. Lower it only for a peer that sends fewer: a small count makes an offline guess of a weak secret cheap.

Certificates

type CertificateLookup interface {
    LookupCertificate(issuer pkix.Name, subject pkix.Name, senderKID []byte) (*x509.Certificate, error)
}

issuer is the recipient in the header, subject the sender and senderKID the key identifier. Any of them may be empty. The names are lookup keys only: the server verifies the signature against the certificate you return, so a client that lies about its name either finds nothing or fails the signature check. Trust comes from your store and proof of the private key.

A senderKID that is present must name the key that signed the message (RFC 9483 section 3.5). The server compares it with the subject key identifier of the certificate you return and refuses a mismatch with badMessageCheck. This holds also when your lookup ignores senderKID, for example because it takes the signer from extraCerts. A certificate without a subject key identifier is not compared.

Return only certificates you issued or otherwise trust and do not return revoked ones.

The sender identity

A verified message reaches your code with a SenderIdentity:

Field Content
Certificate The signer certificate of a signature-protected message
Sender The sender name. For a signed message it equals the certificate subject, so you can authorize on it
SenderKID The reference of a MAC-protected message
MACVerified True for a MAC-protected message
RA The registration authority that forwarded the request, see Registration authorities

For a MAC-protected message the sender name is whatever resolved the secret. Authorize on SenderKID or on the name the secret is registered to.

Algorithms

A request protected with an algorithm the server does not implement is refused with badAlg. This includes SHA-1 signatures, which RFC 9481 section 7.1 deprecates. WithSHA1Signatures accepts them from RFC 4210 era devices, in the protection and in the proof of possession.

Response protection

The server answers a MAC-protected request with a MAC from the same secret and algorithm suite, with a fresh salt, as RFC 9483 section 3.2 requires. It repeats the client's senderKID. Responses to signed requests and to messages whose protection failed are signed with the WithSigner key.

Signing every refused request costs one signature per unauthenticated message, which matters with a slow signer such as SLH-DSA-SHA2-128s. WithUnprotectedErrorsBeforeAuthentication leaves errors raised before authentication unprotected, as RFC 9483 section 3.6.4 allows when the recipient has no credential to verify them with. A request forwarded by an authenticated registration authority is still answered with a protected error. The client of this module reports an unprotected error as UnverifiedStatusError. OpenSSL reports a missing protection instead of the status unless it runs with -unprotected_errors.