---
title: "brainpoolP512r1"
description: "brainpoolP512r1 point math and ECDSA verification for OpenPGP keys and Brainpool certificates"
canonical_url: "https://curves.agntn.dev/math/brainpoolp512r1"
---
# brainpoolP512r1

> brainpoolP512r1 point math and ECDSA verification for OpenPGP keys and Brainpool certificates

## brainpoolP512r1, from RFC 5639

This entry handles SEC1 point math and ECDSA verification. It verifies OpenPGP and TLS Brainpool signatures. RFC 9580 lists the curve for ECDSA and ECDH. RFC 7027 specifies Brainpool curves for TLS. The package doesn't include twisted brainpoolP512t1. This entry doesn't sign, recover keys or provide scalar math.

RFC 5639 section 3.7 defines brainpoolP512r1. The equation is y² = x³ + ax + b. It uses a 512-bit prime field and cofactor 1. The coefficient a is neither 0 nor -3:

```text
7830a3318b603b89e2327145ac234cc594cbdd8d3df91610a83441caea9863bc2ded5d5aa8253aa10a2ef1c98b9ac8b57f1117a72bf2c7b9e7c1ac4d77fc94ca
```

What does a point look like here? Try the generator and two x values:

```ts
import { isOnCurve, multiplyGenerator } from "@agntn/curves/brainpoolp512r1";

multiplyGenerator(1n); // "0281aee4bdd82ed9…bcb9f822"
isOnCurve(`02${"0".repeat(128)}`); // false, x = 0
isOnCurve(`02${"0".repeat(127)}3`); // true, x = 3
```

The generator's y is even, so its compressed form starts with `02`. Coordinates and scalars take 64 bytes, or 128 hex digits. Compressed points take 65 bytes. Uncompressed points take 129.

No other curve in this package shares that width. That's a welcome clue when checking a key.

## Verify a brainpoolP512r1 ECDSA signature

Have a digest and a SEC1 public key? Give both to `verify` with the signature:

```ts
import { verify } from "@agntn/curves/brainpoolp512r1";

const publicKey =
  "0467cea1bedf84cbdcba69a05bb2ce3a2d1c9d911d236c480929a16ad697b45a6ca127079fe8d7868671e28ef33bdf9319e2e51c84b190ac5c91b51baf0a980ba5" +
  "00a7e79006194b5378f65cbe625ef2c47c64e56040d873b995b5b1ebaa4a6ce971da164391ff619af3bcfc71c5e1ad27ee0e859c2943e2de8ef7c43d3c976e9b";
const digest =
  "43f800fbeaf9238c58af795bcdad04bc49cd850c394d3382953356b023210281757b30e19218a37cbd612086fbc158caa8b4e1acb2ec00837e5d941f342fb3cc"; // SHA-512 of 313233343030
const signature =
  "0bd2593447cc6c02caf99d60418dd42e9a194c910e6755ed0c7059acac656b04ccfe1e8348462ee43066823aee2fed7ca012e9890dfb69866d7ae88b6506f9c7" +
  "44b42304e693796618d090dbcb2a2551c3cb78534611e61fd9d1a5c0938b5b8ec6ed53d2d28999eabbd8e7792d167fcf582492403a6a0f7cc94c73a28fb76b71";

verify(signature, { digest, publicKey }); // true
verify(signature, { digest: `00${digest.slice(2)}`, publicKey }); // false
```

This is Wycheproof's SHA-512 vector tcId 1. The six-byte message is `313233343030` in hex. In ASCII, that's `123400`. The second call replaces the digest's first byte. It doesn't add a byte.

The signature is r followed by s. That's 128 bytes, or 256 hex digits. The public key can be compressed or uncompressed SEC1 hex.

### What about DER and the digest?

X.509 DER and OpenPGP MPIs need conversion first. Extract r and s. Pad each to 128 hex digits. Join them without `0x`.

The digest must be hex without `0x`. It can be 1 to 64 bytes. The library doesn't hash the message.

OpenPGP requires a digest at least as large as the curve's 64-byte field size. SHA-512 meets that requirement. Its 512 bits fit the 512-bit order exactly. Every bit gets a seat.

### What does it refuse?

```text
verify("00", { digest, publicKey })                 // Signature must be 256 hex digits without 0x, r then s
verify(signature, { digest: "0x00", publicKey })    // Digest must be hex without 0x, 1 to 64 bytes
verify(signature, { digest: digest + "00", publicKey }) // Digest must be hex without 0x, 1 to 64 bytes
verify(signature, { digest, publicKey: p521G })     // Invalid SEC1 brainpoolP512r1 public key
liftX("0".repeat(128))                              // No point of brainpoolP512r1 has this x
```

The third call supplies a 65-byte digest. It's refused, not truncated. Here `p521G` is P-521's uncompressed generator, at 133 bytes. Its length alone rules it out. Malformed inputs and points off this curve throw.

An r or s outside 1 to n - 1 returns `false`. The optional `lowS` defaults to `false`. Set it to `true` to reject s above n / 2.

### CLI and agent tool

Prefer a terminal? The CLI uses `curves brainpoolp512r1 <operation>`:

```bash
curves brainpoolp512r1 verify --point 0467cea1bedf84cbdcba69a05bb2ce3a2d1c9d911d236c480929a16ad697b45a6ca127079fe8d7868671e28ef33bdf9319e2e51c84b190ac5c91b51baf0a980ba500a7e79006194b5378f65cbe625ef2c47c64e56040d873b995b5b1ebaa4a6ce971da164391ff619af3bcfc71c5e1ad27ee0e859c2943e2de8ef7c43d3c976e9b --digest 43f800fbeaf9238c58af795bcdad04bc49cd850c394d3382953356b023210281757b30e19218a37cbd612086fbc158caa8b4e1acb2ec00837e5d941f342fb3cc --signature 0bd2593447cc6c02caf99d60418dd42e9a194c910e6755ed0c7059acac656b04ccfe1e8348462ee43066823aee2fed7ca012e9890dfb69866d7ae88b6506f9c744b42304e693796618d090dbcb2a2551c3cb78534611e61fd9d1a5c0938b5b8ec6ed53d2d28999eabbd8e7792d167fcf582492403a6a0f7cc94c73a28fb76b71
```

It returns:

```text
{"operation":"verify","valid":true}
```

The operations are `add`, `subtract`, `negate`, `multiply`, `lift`, `check` and `verify`. The agent tool `curves_brainpoolp512r1_compute` offers the same operations. Its arguments are `point`, `other`, `scalar`, `x`, `digest`, `signature`, `lowS` and `compressed`.

## brainpoolP512r1 point math

The `@agntn/curves/brainpoolp512r1` entry exports:

- `addPoints`, `subtractPoints` and `negatePoint`
- `multiplyPoint` and `multiplyGenerator`
- `liftX`, `isOnCurve` and `convertPoint`
- `verify`, `brainpoolP512r1` and `INVALID_POINT`

`brainpoolP512r1` holds the domain parameters. The other export names match the [P-521](https://curves.agntn.dev/math/p521) entry. Pass `liftX` exactly 128 hex digits for x. Scalars run from 1 to n - 1. Use a bigint or hex with at most 128 digits.

Why do those small x values matter? At x = 0, b isn't a square modulo p. So there's no point. Neither x = 1 nor x = 2 has one. Three misses, then x = 3 hits.

Length is a useful clue here, unlike the other Brainpool curves. [P-256](https://curves.agntn.dev/math/p256) and [brainpoolP256r1](https://curves.agntn.dev/math/brainpoolp256r1) both use 32-byte coordinates. [P-384](https://curves.agntn.dev/math/p384) and [brainpoolP384r1](https://curves.agntn.dev/math/brainpoolp384r1) both use 48. P-521 uses 66, with 67-byte compressed or 133-byte uncompressed points.

A P-384 or P-521 key fails on length alone. But the right length doesn't prove a point is valid. Use `isOnCurve` to check the point itself.

## Written from the specs

Brainpool's parameters are pseudo-random and reproducible. They're verifiable rather than chosen as special primes for speed. Neither the prime nor a has a shortcut form.

This curve shares `src/core/sec1.ts` with six others. They are [secp256k1](https://curves.agntn.dev/math/secp256k1), P-256, P-384, P-521, brainpoolP256r1 and brainpoolP384r1. Seven curves share one implementation.

The a = 0 and a = -3 formulas refuse this curve. They report `These formulas are for another a`. Addition and doubling instead use algorithms 1 and 3 from Renes, Costello and Batina (2016). These complete formulas work for any a. They cost a few more multiplications. That's a real price for generality.

The other two Brainpool curves use those formulas too. This curve changes the widths. Both n and p have 512 bits. Since p is 3 mod 4, a square root takes one power.

Multiplication walks 128 hex digits. The secret comb has four rows, 32 steps apart. The verification table has 128 rows of 15 points. Verification reads G from that faster table. Its scalars are public.

A secret multiply takes the same steps for every scalar. `test/brainpoolp512r1.test.ts` times 1 against n - 1. JavaScript bigints don't promise constant time. The library isn't audited. Keep real money away.

In a local run, G multiplication took about 2.1 ms. Another point took about 5.7 ms. Verification took about 6.3 ms. Generic code for curves you define took about 41 ms per multiplication. These timings aren't test assertions.

All 337 Wycheproof SHA-512 P1363 vectors pass. That's 251 valid and 86 invalid signatures. RFC 7027 appendix A.3 gives two key pairs and their shared secret. Both match.

`@noble/curves` has no Brainpool. Multiples of G are frozen from OpenSSL 3.6.5 through `node:crypto`. In a local run, OpenSSL agreed on 200 random keys. The library also verified 20 signatures made by OpenSSL.


## Sitemap

See the full [sitemap](https://curves.agntn.dev/sitemap.md) for all pages.
