brainpool

package module
v1.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 3, 2026 License: EUPL-1.2 Imports: 16 Imported by: 12

README

brainpool

A Go library for the Brainpool elliptic curves (RFC 5639) as used across the gematik telematics infrastructure (SMC-B, HBA, eGK, IDP-Dienst, ePA): curve definitions, X.509 certificate and key parsing that the Go standard library cannot perform for these curves, a self-contained JOSE layer (JWS/JWE/JWK), and a constant-time, formally-verified core for brainpoolP256r1 covering the secret-scalar operations (ECDSA signing and ECDH).

Security posture

The security-critical design follows the principle that only operations on a secret scalar require constant-time implementations; operations on public data do not. Accordingly:

Operation Secret input Implementation
ECDSA sign (brainpoolP256r1) nonce k, key d Constant-time core (internal/bp256)
ECDH (brainpoolP256r1) key d Constant-time core (internal/bp256)
Public key from private key (parsing, GenerateKey) (brainpoolP256r1) key d Constant-time core (internal/bp256)
ECDSA verify none (public) Standard library crypto/ecdsa.Verify
Certificate / key parsing none (public) crypto/x509 for the certificate body, this package for the Brainpool key (x509.go, keys.go)
brainpoolP384r1 / P512r1 sign/verify/ECDH — Standard library crypto/ecdsa over elliptic.Curve
Why a constant-time core for brainpoolP256r1

Go's generic crypto/elliptic custom-curve path (the only path available for non-NIST curves) is deprecated and documented as "not guaranteed to provide any security property." Its scalar multiplication is a variable-time big.Int double-and-add whose timing correlates with the secret scalar — the class of leak exploited by Minerva (2019) and TPM-Fail (2019) to recover ECDSA private keys via lattice attacks. brainpoolP256r1 is the curve gematik uses for software-held signing keys, so it receives the same constant-time treatment the Go standard library already gives the NIST curves.

The constant-time core is built exactly as the Go standard library builds its NIST curve support (crypto/internal/fips140/nistec):

  1. Field arithmetic — machine-checked code generated by Fiat Cryptography (word-by-word Montgomery), committed verbatim. See internal/bp256/fiat/PROVENANCE.md.
  2. Point arithmetic — the exception-free complete addition formulas of Renes–Costello–Batina over homogeneous projective coordinates. brainpoolP256r1 has a ≠ −3, so the general formula (Algorithm 1) is used.
  3. Scalar multiplication — a constant-time Montgomery ladder with no scalar-bit-dependent branches.
  4. Scalar-field arithmetic (mod n, for k⁻¹ and z + r·d) — a second Fiat-Cryptography field, fiat.Scalar, in the same package.
  5. Deterministic nonces — RFC 6979 (HMAC-SHA256), default for software signing, removing the weak-RNG key-recovery failure mode.
  6. Low-s normalisation — signatures are canonicalised to low-s (SEC 1 §4.1.3 / BIP-0062) to remove signature malleability.
Residual notes
  • brainpoolP384r1 and brainpoolP512r1 remain on the standard library's non-constant-time generic path; they are used for certificate handling and are not the software-signing curves. Hardware-token (HBA/SMC-B) signing is opaque to this library and unaffected.
  • This library has not undergone an independent third-party audit. It is structured to support one: the field layer is machine-checked, the point layer is differentially tested against an independent implementation and OpenSSL, and every algorithm cites its specification.

Validation

The constant-time core is validated by:

  • Differential testing of point addition, doubling, scalar multiplication, ECDSA, and ECDH against the package's independent big.Int curve implementation (the oracle) and against the Go standard library.
  • Project Wycheproof — all 485 ecdsa_brainpoolP256r1_sha256 edge-case vectors (zero/overflow r,s, malformed DER, …).
  • RFC 7027 §A.1 ECDH known-answer vectors (cross-implementation; the expected values are independently confirmed with OpenSSL).
  • Field arithmetic checked against math/big modular arithmetic.
  • OpenSSL cross-tests (build tag openssl_cross): our deterministic signatures verify under OpenSSL, and OpenSSL signatures verify under the standard-library path — bidirectional interoperability.
  • RFC 6979 determinism validated by reproducibility (identical inputs → identical signature; distinct messages → distinct nonces) and by verification under the standard library.

Domain parameters (brainpoolP256r1, RFC 5639 §3.4)

p  = A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377
a  = 7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9
b  = 26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6
Gx = 8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262
Gy = 547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997
n  = A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7
h  = 1
OID = 1.3.36.3.3.2.8.1.1.7  (brainpoolP256r1)

References

Curves and elliptic-curve cryptography
  • RFC 5639 — Elliptic Curve Cryptography (ECC) Brainpool Standard Curves and Curve Generation (M. Lochter, J. Merkle), March 2010. Domain parameters.
  • RFC 7027 — Elliptic Curve Cryptography (ECC) Brainpool Curves for Transport Layer Security (TLS) (J. Merkle, M. Lochter), October 2013. ECDH test vectors (Appendix A).
  • SEC 1: Elliptic Curve Cryptography, Version 2.0 (Certicom Research, 2009). Point encodings (§2.3), public-key validation (§3.2.2), signature encoding and low-s (§4.1.3).
  • Renes, Costello, Batina, Complete addition formulas for prime order elliptic curves, EUROCRYPT 2016 (IACR ePrint 2015/1060). Algorithm 1 (general a) is used for point addition.
Signatures and key agreement
  • FIPS 186-5 — Digital Signature Standard (DSS) (NIST, February 2023). ECDSA signing (§6.4.1) and verification (§6.4.2).
  • RFC 6979 — Deterministic Usage of the DSA and ECDSA (T. Pornin), August 2013. Deterministic nonce generation (§3.2).
  • BIP-0062 — low-s signature canonicalisation (malleability).
BSI Technical Guidelines
  • BSI TR-03111 — Elliptic Curve Cryptography, Version 2.10 (Bundesamt für Sicherheit in der Informationstechnik). Point encoding (§3.1), public-key validation (§3.2.2), ECDH (§3.5.1), ECDSA plain signatures (§5.2.1).
  • BSI TR-03110 — Advanced Security Mechanisms for Machine Readable Travel Documents and eIDAS Token (PACE / Chip Authentication context for eGK/eMRTD).
JOSE (the josebp subpackage)
  • RFC 7515 — JSON Web Signature (JWS).
  • RFC 7516 — JSON Web Encryption (JWE).
  • RFC 7517 — JSON Web Key (JWK).
  • RFC 7518 — JSON Web Algorithms (JWA); raw r‖s signatures (§3.4), ECDH-ES + Concat KDF (§4.6).
  • RFC 7519 — JSON Web Token (JWT).
Formal verification and code generation
  • Fiat Cryptography — A. Erbsen, J. Philipoom, J. Gross, R. Sloan, A. Chlipala, Simple High-Level Code for Cryptographic Arithmetic — With Proofs, Without Compromises, IEEE S&P 2019. https://github.com/mit-plv/fiat-crypto. The field arithmetic in internal/bp256/fiat is generated by Fiat Cryptography and is machine-checked in Coq. Provenance (pinned image, commit, exact invocation) is recorded in internal/bp256/fiat/PROVENANCE.md.
  • addchain — M. McLoughlin, https://github.com/mmcloughlin/addchain. Generates the constant-time field-inversion addition chains.
Test corpora
Standard library precedent
  • Go crypto/internal/fips140/nistec — the NIST-curve implementation whose structure (Fiat field + projective complete formulas + constant-time ladder) this library mirrors for brainpoolP256r1.

Package layout

brainpool/                 Curves, certificate/key parsing, SignFunc, ECDH, GenerateKey
  josebp/                  JOSE: JWS/JWE/JWK over Brainpool
  internal/bp256/          Constant-time brainpoolP256r1 core (Sign, ECDH, PublicKey)
    fiat/                  Generated arithmetic mod p and mod n (Fiat Cryptography)
  testdata/wycheproof/     Vendored Wycheproof vectors

doc.go has the constant-time posture table and a file index.

  • Root: curves.go (P256r1/P384r1/P512r1 as an isomorphism onto the twisted t1 curves over the deprecated big.Int crypto/elliptic path; the curves table is the only place a curve is identified by name or OID), x509.go (ParseCertificate swaps the SPKI algorithm OID for an unassigned one of the same length so crypto/x509 parses everything and leaves the key nil, then decodes the Brainpool key and restores the Raw* slices; ValidatePublicKey; MarshalPKIXPublicKey), keys.go (SEC1/PKCS#8 keys, public point recomputed from d), sign.go (SignFunc, SignFuncPrivateKey, ECDH, GenerateKey: the only callers of internal/bp256).
  • internal/bp256: the constant-time brainpoolP256r1 core: Sign (RFC 6979, low-s), ECDH, PublicKey, all on fixed-width bytes. point.go is RCB complete addition and a Montgomery ladder; scalar.go and ecdsa.go the mod-n arithmetic; fiat/ the generated fields (Element mod p, Scalar mod n): four generated files, never hand-edited, checksums in fiat/PROVENANCE.md.
  • josebp: JWS/JWE/JWK over these curves for the IDP-Dienst; its exported surface is what idpd calls, helpers are unexported.

Development

The module is active: go/tipki's trust path parses and verifies through it (decision-10, amendment of 2026-11-02). The repository's rules apply (docs); in addition:

  • A secret scalar on brainpoolP256r1 never goes through elliptic.Curve methods: signing, ECDH and public-key derivation route through hasConstantTimeCore to internal/bp256. Verification and parsing are public data and use the standard library.
  • Every key that arrives from outside (certificate, JWK, ECDH peer) passes ValidatePublicKey before any arithmetic.
  • Exported only if a consumer calls it (in this repository: idpd, and josebp itself). Breaking changes ship as a minor bump; in-repo consumers pin explicitly.
  • Tests use the standard testing package; the differential tests in internal/bp256 use brainpool.P256r1() (big.Int) as the oracle; Wycheproof vectors live in testdata/.
go build ./... && go vet ./... && go test ./...
go test -tags openssl_cross ./...        # cross-checks against the openssl CLI (skips if absent)
go test -run x -fuzz FuzzParseCertificate -fuzztime 30s
cd .. && just golangci                    # golangci-lint with the workspace configuration
cd internal/bp256/fiat && go run generate.go -field p   # regenerate; needs the docker image from PROVENANCE.md

License and provenance of generated code

Generated files (*_fiat64.go, *_invert.go) carry a DO NOT EDIT header and are reproducible from the pinned toolchain described in the PROVENANCE.md files. Do not hand-edit them; regenerate with the documented commands.

Documentation

Overview

Package brainpool implements the Brainpool elliptic curves (RFC 5639) as the gematik Telematikinfrastruktur uses them: the curves themselves, certificate and key parsing the standard library refuses for these curves, and the signing and key-agreement entry points behind the josebp JOSE layer.

Constant-time posture

Only operations on a secret scalar need a constant-time implementation, and only brainpoolP256r1 — the curve gematik issues software-held keys on — has one, the formally-verified core in internal/bp256:

operation                              secret   implementation
ECDSA sign (SignFuncPrivateKey)        k, d     internal/bp256, RFC 6979 nonce, low-s
ECDH                                   d        internal/bp256
public key from private (parse, GenerateKey)  d  internal/bp256
ECDSA verify, certificate/JWK parsing  —        standard library
brainpoolP384r1 / P512r1, everything   —        standard library generic path

The generic path is the deprecated big.Int arithmetic of crypto/elliptic, reached through an isomorphism onto the twisted t1 curves; it is fine for public data and for the curves that never hold a software key.

Where to look

curves.go   P256r1/P384r1/P512r1, the curve table, the rcurve isomorphism
x509.go     ParseCertificate, ValidatePublicKey, MarshalPKIXPublicKey
keys.go     ParsePrivateKeyPEM, ParsePKCS8PrivateKey, ParseECPrivateKey
sign.go     SignFunc, SignFuncPrivateKey, ECDH, GenerateKey
josebp/     JWS, JWE (ECDH-ES), JWK over these curves
internal/bp256/  the constant-time brainpoolP256r1 core and its fiat fields

README.md carries the full security posture and references.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ECDH added in v1.1.0

func ECDH(priv *ecdsa.PrivateKey, pub *ecdsa.PublicKey) ([]byte, error)

ECDH returns the shared secret of priv and pub: the x-coordinate of priv·pub as a fixed-width big-endian value (BSI TR-03111 §3.5.1), which is the Z input of the JOSE Concat KDF. pub is validated before any arithmetic. brainpoolP256r1 runs in the constant-time core; other curves on the generic path.

func GenerateKey added in v1.1.0

func GenerateKey(curve elliptic.Curve, rng io.Reader) (*ecdsa.PrivateKey, error)

GenerateKey returns a fresh private key on curve. Brainpool keys derive their public point through derivePublicKey, so a brainpoolP256r1 secret is never multiplied by variable-time code; any other curve is handed to the standard library. A nil rng means crypto/rand.

func MarshalPKIXPublicKey

func MarshalPKIXPublicKey(pub any) ([]byte, error)

MarshalPKIXPublicKey is x509.MarshalPKIXPublicKey with Brainpool support: the standard library cannot name these curves in an AlgorithmIdentifier.

func P256r1

func P256r1() elliptic.Curve

P256r1 returns a Curve which implements Brainpool P256r1 (see RFC 5639, section 3.4)

func P384r1

func P384r1() elliptic.Curve

P384r1 returns a Curve which implements Brainpool P384r1 (see RFC 5639, section 3.6)

func P512r1

func P512r1() elliptic.Curve

P512r1 returns a Curve which implements Brainpool P512r1 (see RFC 5639, section 3.7)

func ParseCertificate

func ParseCertificate(der []byte) (*x509.Certificate, error)

ParseCertificate parses a DER certificate, accepting Brainpool public keys that crypto/x509 rejects with "unsupported elliptic curve".

crypto/x509 parses every field of a certificate whose public key algorithm it does not recognise and leaves PublicKey nil — it only refuses the EC algorithm with a curve it does not have. So for a Brainpool certificate the SPKI algorithm OID is swapped for an unknown one of the same length, the standard library parses the whole certificate, and only the public key is decoded here. The Raw* fields are restored from the original bytes.

func ParseCertificatePEM

func ParseCertificatePEM(pemBytes []byte) (*x509.Certificate, error)

ParseCertificatePEM parses the first PEM block of pemBytes as a certificate.

func ParseECPrivateKey

func ParseECPrivateKey(der []byte) (*ecdsa.PrivateKey, error)

ParseECPrivateKey parses a SEC 1 EC private key; keys on other curves are handed to x509.ParseECPrivateKey.

func ParsePKCS8PrivateKey

func ParsePKCS8PrivateKey(der []byte) (any, error)

ParsePKCS8PrivateKey parses a PKCS#8 private key; anything but an EC key on a Brainpool curve is handed to x509.ParsePKCS8PrivateKey.

func ParsePrivateKeyPEM

func ParsePrivateKeyPEM(pemBytes []byte) (*ecdsa.PrivateKey, error)

ParsePrivateKeyPEM parses a PKCS#8 ("PRIVATE KEY") or SEC 1 ("EC PRIVATE KEY") PEM block holding an ECDSA key.

func ValidatePublicKey added in v1.1.0

func ValidatePublicKey(pub *ecdsa.PublicKey) error

ValidatePublicKey reports whether pub is a valid affine point on its curve: both coordinates canonical in [0, p-1] and the point on the curve, which also rejects the point at infinity (SEC 1 §3.2.2.1, BSI TR-03111 §3.2.2). Keys that arrive from the network — certificates, JWKs, ECDH peers — go through this before any arithmetic touches them.

Types

type SignFunc

type SignFunc func(hash []byte) ([]byte, error)

SignFunc signs a digest and returns the raw fixed-width r‖s ECDSA signature (RFC 7518 §3.4). It abstracts over software keys and hardware tokens (smartcards via the gematik connector), where the private key is not extractable. The JOSE layer lives in brainpool/josebp.

func SignFuncPrivateKey

func SignFuncPrivateKey(key *ecdsa.PrivateKey) SignFunc

SignFuncPrivateKey returns a SignFunc backed by a software ECDSA private key. For brainpoolP256r1 it signs in the constant-time core with an RFC 6979 deterministic nonce and low-s normalisation; every other curve signs with the standard library, which for the remaining Brainpool curves is the generic variable-time path — acceptable because the TI does not issue software-held keys on them.

Directories

Path Synopsis
internal
bp256
Package bp256 implements constant-time arithmetic for the brainpoolP256r1 elliptic curve (RFC 5639 §3.4), used by the parent brainpool package for the secret-scalar operations that must resist timing side channels: ECDSA signing and ECDH.
Package bp256 implements constant-time arithmetic for the brainpoolP256r1 elliptic curve (RFC 5639 §3.4), used by the parent brainpool package for the secret-scalar operations that must resist timing side channels: ECDSA signing and ECDH.
bp256/fiat
Package fiat provides constant-time arithmetic in the two fields of brainpoolP256r1 (RFC 5639 §3.4): Element is an integer modulo the field prime p, Scalar an integer modulo the group order n.
Package fiat provides constant-time arithmetic in the two fields of brainpoolP256r1 (RFC 5639 §3.4): Element is an integer modulo the field prime p, Scalar an integer modulo the group order n.
Package josebp is a self-contained JOSE (JWS/JWT/JWK/JWE) implementation for the Brainpool elliptic curves used by the gematik telematik infrastructure (IDP-Dienst, SMC-B, ePA).
Package josebp is a self-contained JOSE (JWS/JWT/JWK/JWE) implementation for the Brainpool elliptic curves used by the gematik telematik infrastructure (IDP-Dienst, SMC-B, ePA).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL