From c13377af2b0fe9f64b2eb553c83f523a2c997d3a Mon Sep 17 00:00:00 2001 From: dev Date: Sat, 26 Sep 2026 01:15:02 +0200 Subject: [PATCH] Contrast bls12381.ts with the Go reference and an audited library Our checkCompressedPoint stays: it matches the Go reference on every edge case, is 1.3 KB gzip and adds no runtime dependency. Its assurance now comes from a contrast test run on every test pass: - 41 frozen edge-case encodings with the Go reference verdict (drand crypto KeyGroup over kyber-bls12381 and kilic/bls12-381, as profile.Validate uses it), reproducible with scripts/bls12381-go-verdicts.go; - the audited @noble/curves 2.4.0 on the same edge cases and on a fixed-seed corpus of valid points, negations, bit flips, random x and G1 points on the curve outside the subgroup. Breaking the G1 or the G2 subgroup check makes the test fail. @noble/curves 2.4.0 is a development dependency only; a test fails if anything that is not a test imports it, and the build contains none of it. Co-Authored-By: Claude Opus 5.5 --- README.md | 1 + docs/PLAN_codec_cbor_y_pagina_svelte.md | 1 + package-lock.json | 30 +++ package.json | 1 + scripts/bls12381-go-verdicts.go | 63 ++++++ src/lib/dkc/bls12381.contrast.test.ts | 177 +++++++++++++++ src/lib/dkc/testing/bls12381-vectors.json | 251 ++++++++++++++++++++++ 7 files changed, 524 insertions(+) create mode 100644 scripts/bls12381-go-verdicts.go create mode 100644 src/lib/dkc/bls12381.contrast.test.ts create mode 100644 src/lib/dkc/testing/bls12381-vectors.json diff --git a/README.md b/README.md index 107173f..d0690a4 100644 --- a/README.md +++ b/README.md @@ -156,6 +156,7 @@ Umbrales de cobertura (`vitest.config.ts`): `cbor.ts` al 100 % en líneas, ramas | `@sveltejs/vite-plugin-svelte` | 7.3.0 | en `package.json`; paso 5 | | `svelte-check` | 4.7.6 | en `package.json`; paso 5, `npm run check` | | `@sveltejs/adapter-static` | 3.0.10 | en `package.json`; paso 5: la página es estática y no necesita servidor | +| `@noble/curves` | 2.4.0 | solo desarrollo: oráculo auditado de `bls12381.contrast.test.ts`, que compara nuestro `bls12381.ts` con la referencia Go y con noble; un test falla si algo que no sea un test la importa, así que nunca llega al sitio. Arrastra `@noble/hashes` 2.4.0 | Todas las versiones se fijan exactas y `package-lock.json` se versiona. `.npmrc` activa `legacy-peer-deps` porque npm 11.5.2 falla al resolver los peers opcionales de `vitest` 5.0.1 (`Cannot read properties of null (reading 'edgesOut')`); con esa opción npm no instala peers, así que el peer obligatorio `vite` está declarado explícitamente. diff --git a/docs/PLAN_codec_cbor_y_pagina_svelte.md b/docs/PLAN_codec_cbor_y_pagina_svelte.md index 3b61c3a..cd9b02c 100644 --- a/docs/PLAN_codec_cbor_y_pagina_svelte.md +++ b/docs/PLAN_codec_cbor_y_pagina_svelte.md @@ -343,5 +343,6 @@ Todos en un único cambio normativo, con la justificación §76 de la sección 3 | `@types/node` | 24.13.6 | en `package.json`; tests que leen `testdata/` | | `svelte`, `@sveltejs/kit`, `@sveltejs/vite-plugin-svelte`, `vite`, `svelte-check` | 5.57.1, 2.70.3, 7.3.0, 8.3.0, 4.7.6 | en `package.json`; paso 5 | | `@sveltejs/adapter-static` | 3.0.10 | en `package.json`; paso 5, sitio estático | +| `@noble/curves` | 2.4.0 | solo desarrollo; oráculo auditado del test de contraste de `bls12381.ts` (decidido el 26-09-2026: se mantiene nuestra implementación y se contrasta en cada ejecución). En la fase 2 pasará a ser la dependencia BLS de ejecución, fijada a 2.3.0 o posterior | Todas las versiones se fijan exactas y `package-lock.json` se versiona. `.npmrc` activa `legacy-peer-deps` porque npm 11.5.2 falla al resolver los peers opcionales de `vitest` 5.0.1 (`Cannot read properties of null (reading 'edgesOut')`). diff --git a/package-lock.json b/package-lock.json index 794aa06..57f9a4b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "datekeys-app", "version": "0.0.0", "devDependencies": { + "@noble/curves": "2.4.0", "@sveltejs/adapter-static": "3.0.10", "@sveltejs/kit": "2.70.3", "@sveltejs/vite-plugin-svelte": "7.3.0", @@ -133,6 +134,35 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@noble/curves": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-2.4.0.tgz", + "integrity": "sha512-P4/62zrgfH33CneE3Dn4WhJVA22YUU0eR51wKIan4NVRvwsA0YnPTwWGpNbpuacSujmSFLvyzpyuR30+fbq2Ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "2.4.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.4.0.tgz", + "integrity": "sha512-X5XaVWZIBCT7HHZGm5I7ZQXDwLG+bGXuSrMQAW+7Zvl87h1kmc1ZB1VSRJcpUfoUrGQp4Fkoxm5kZ+Ms+aW+eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@oxc-project/types": { "version": "0.151.0", "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.151.0.tgz", diff --git a/package.json b/package.json index 739320d..069bde7 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "testdata:check": "node scripts/sync-testdata.mjs check --against ../datekeys-go" }, "devDependencies": { + "@noble/curves": "2.4.0", "@sveltejs/adapter-static": "3.0.10", "@sveltejs/kit": "2.70.3", "@sveltejs/vite-plugin-svelte": "7.3.0", diff --git a/scripts/bls12381-go-verdicts.go b/scripts/bls12381-go-verdicts.go new file mode 100644 index 0000000..dc48344 --- /dev/null +++ b/scripts/bls12381-go-verdicts.go @@ -0,0 +1,63 @@ +//go:build ignore + +// Prints the Go reference verdict for every encoding in +// src/lib/dkc/testing/bls12381-vectors.json, as a JSON object from label to +// "point", "identity" or "invalid". The decoding is the one profile.Validate +// uses: the KeyGroup of the drand crypto scheme (G1 for the unchained scheme, +// G2 for bls-unchained-g1-rfc9380, Quicknet), whose UnmarshalBinary is +// kyber-bls12381 over kilic/bls12-381 FromCompressed, and Equal(Null()) for the +// identity. Run it from a scratch module that requires the reference +// implementation (replace g.activething.com/go/DateKeys => ../datekeys-go and +// GOFLAGS=-mod=mod), passing the path of the vectors file: +// +// go run bls12381-go-verdicts.go path/to/bls12381-vectors.json +package main + +import ( + "encoding/hex" + "encoding/json" + "fmt" + "os" + + "github.com/drand/drand/v2/crypto" +) + +func main() { + raw, err := os.ReadFile(os.Args[1]) + if err != nil { + panic(err) + } + var file struct { + Vectors []struct{ Label, Group, Hex string } + } + if err := json.Unmarshal(raw, &file); err != nil { + panic(err) + } + g1, _ := crypto.GetSchemeByID(crypto.UnchainedSchemeID) + g2, _ := crypto.GetSchemeByID(crypto.SigsOnG1ID) + out := map[string]string{} + for _, v := range file.Vectors { + s := g1 + if v.Group == "G2" { + s = g2 + } + b, err := hex.DecodeString(v.Hex) + if err != nil { + panic(err) + } + k := s.KeyGroup.Point() + switch { + case k.UnmarshalBinary(b) != nil: + out[v.Label] = "invalid" + case k.Equal(k.Null()): + out[v.Label] = "identity" + default: + out[v.Label] = "point" + } + } + j, err := json.MarshalIndent(out, "", " ") + if err != nil { + panic(err) + } + fmt.Println(string(j)) +} diff --git a/src/lib/dkc/bls12381.contrast.test.ts b/src/lib/dkc/bls12381.contrast.test.ts new file mode 100644 index 0000000..13a5d79 --- /dev/null +++ b/src/lib/dkc/bls12381.contrast.test.ts @@ -0,0 +1,177 @@ +// Contrast test of bls12381.ts against an audited implementation. +// +// checkCompressedPoint is our own code. It is kept because it matches the Go +// reference on every edge case, is 1.3 KB gzip and adds no runtime +// dependency. Its assurance comes from this file: on every run it is compared +// with the Go reference on frozen edge cases and with @noble/curves 2.4.0 +// (Cure53 2024; Trail of Bits 2026 review, whose BLS findings were fixed in +// 2.3.0) on a deterministic corpus. noble is a development dependency only: +// the last test fails if anything outside a test imports it, so it never +// reaches the site. + +import { bls12_381 } from '@noble/curves/bls12-381.js'; +import { readdirSync, readFileSync } from 'node:fs'; +import { join } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { describe, expect, it } from 'vitest'; +import { checkCompressedPoint, type Group } from './bls12381.ts'; + +type Verdict = 'point' | 'identity' | 'invalid'; + +const SIZE: Record = { G1: 48, G2: 96 }; +const P = bls12_381.fields.Fp.ORDER; +const R = bls12_381.fields.Fr.ORDER; + +// The audited oracle. noble also decodes uncompressed encodings, which the +// protocol never uses for a public key, so the length is checked first, as +// the Go reference does; with that check noble >= 2.3.0 matches Go on every +// frozen edge case. +function noble(group: Group, bytes: Uint8Array): Verdict { + if (bytes.length !== SIZE[group]) return 'invalid'; + try { + const point = bls12_381[group].Point.fromBytes(bytes); + point.assertValidity(); + return point.is0() ? 'identity' : 'point'; + } catch { + return 'invalid'; + } +} + +const hex = (s: string): Uint8Array => Uint8Array.from(s.match(/../g) ?? [], (b) => parseInt(b, 16)); +const toHex = (b: Uint8Array): string => Array.from(b, (v) => v.toString(16).padStart(2, '0')).join(''); + +// splitmix64: a fixed-seed generator, so every run checks the same corpus. +function rng(seed: bigint): (n: number) => Uint8Array { + let state = seed; + const M = (1n << 64n) - 1n; + const next = (): bigint => { + state = (state + 0x9e3779b97f4a7c15n) & M; + let z = state; + z = ((z ^ (z >> 30n)) * 0xbf58476d1ce4e5b9n) & M; + z = ((z ^ (z >> 27n)) * 0x94d049bb133111ebn) & M; + return z ^ (z >> 31n); + }; + return (n) => { + const out = new Uint8Array(n); + for (let i = 0; i < n; i += 8) { + let w = next(); + for (let j = 0; j < 8 && i + j < n; j++, w >>= 8n) out[i + j] = Number(w & 0xffn); + } + return out; + }; +} + +const big = (b: Uint8Array): bigint => b.reduce((acc, v) => (acc << 8n) | BigInt(v), 0n); + +function be48(x: bigint): Uint8Array { + const out = new Uint8Array(48); + for (let i = 47; i >= 0; i--, x >>= 8n) out[i] = Number(x & 0xffn); + return out; +} + +function modPow(base: bigint, exp: bigint): bigint { + let result = 1n; + let b = base % P; + for (let e = exp; e > 0n; e >>= 1n) { + if (e & 1n) result = (result * b) % P; + b = (b * b) % P; + } + return result; +} + +// A G1 encoding of a point on y^2 = x^3 + 4 chosen from a random x: the +// cofactor is about 2^126, so it is outside the prime-order subgroup. +function onCurveOutsideSubgroupG1(random: (n: number) => Uint8Array): Uint8Array { + for (;;) { + const x = big(random(48)) % P; + const rhs = (x * x * x + 4n) % P; + if (modPow(rhs, (P - 1n) / 2n) !== 1n) continue; + const y = modPow(rhs, (P + 1n) / 4n); + const out = be48(x); + out[0]! |= 0x80 | (y > (P - 1n) / 2n ? 0x20 : 0); + return out; + } +} + +interface Case { + label: string; + group: Group; + bytes: Uint8Array; +} + +function corpus(): Case[] { + const random = rng(20260926n); + const cases: Case[] = []; + const scalar = (): bigint => (big(random(40)) % (R - 1n)) + 1n; + for (const [group, valid, other] of [ + ['G1', 60, 120], + ['G2', 20, 40], + ] as const) { + const G = bls12_381[group].Point; + for (let i = 0; i < valid; i++) { + const bytes = G.BASE.multiply(scalar()).toBytes(true); + cases.push({ label: `${group} valid ${i}`, group, bytes }); + const negated = new Uint8Array(bytes); + negated[0]! ^= 0x20; + cases.push({ label: `${group} valid ${i} negated`, group, bytes: negated }); + const flipped = new Uint8Array(bytes); + const bit = Number(big(random(2)) % BigInt(SIZE[group] * 8 - 3)) + 3; + flipped[bit >> 3]! ^= 0x80 >> (bit & 7); + cases.push({ label: `${group} valid ${i} bit ${bit} flipped`, group, bytes: flipped }); + } + for (let i = 0; i < other; i++) { + const bytes = random(SIZE[group]); + bytes[0] = 0x80 | (bytes[0]! & 0x3f); + cases.push({ label: `${group} random x ${i}`, group, bytes }); + } + } + for (let i = 0; i < 60; i++) { + cases.push({ label: `G1 on the curve, outside the subgroup ${i}`, group: 'G1', bytes: onCurveOutsideSubgroupG1(random) }); + } + return cases; +} + +describe('bls12381.ts against the Go reference and @noble/curves 2.4.0', () => { + const file = JSON.parse(readFileSync(new URL('./testing/bls12381-vectors.json', import.meta.url), 'utf8')) as { + vectors: { label: string; group: Group; hex: string; go: Verdict }[]; + }; + + it('matches the Go reference on every frozen edge case', () => { + expect(file.vectors.length).toBe(41); + for (const v of file.vectors) expect(checkCompressedPoint(v.group, hex(v.hex)), v.label).toBe(v.go); + }); + + it('noble agrees with the Go reference on every frozen edge case', () => { + for (const v of file.vectors) expect(noble(v.group, hex(v.hex)), v.label).toBe(v.go); + }); + + it('matches noble on a deterministic corpus', { timeout: 120_000 }, () => { + const cases = corpus(); + const seen = { point: 0, identity: 0, invalid: 0 }; + for (const c of cases) { + const want = noble(c.group, c.bytes); + expect(checkCompressedPoint(c.group, c.bytes), `${c.label}: ${toHex(c.bytes)}`).toBe(want); + seen[want]++; + } + // Both classes that matter are exercised: valid points (and their + // negations) and encodings that decode to no subgroup point. + expect(seen.point).toBeGreaterThanOrEqual(160); + expect(seen.invalid).toBeGreaterThanOrEqual(200); + }); + + it('only tests import @noble/curves', () => { + const root = fileURLToPath(new URL('../../', import.meta.url)); + const offenders: string[] = []; + const walk = (dir: string): void => { + for (const entry of readdirSync(dir, { withFileTypes: true })) { + const path = join(dir, entry.name); + if (entry.isDirectory()) walk(path); + else if (/\.(ts|js|svelte)$/.test(entry.name) && !/\.test\.ts$/.test(entry.name)) { + if (readFileSync(path, 'utf8').includes('@noble/')) offenders.push(path); + } + } + }; + walk(root); + expect(offenders).toEqual([]); + }); +}); diff --git a/src/lib/dkc/testing/bls12381-vectors.json b/src/lib/dkc/testing/bls12381-vectors.json new file mode 100644 index 0000000..ac324c8 --- /dev/null +++ b/src/lib/dkc/testing/bls12381-vectors.json @@ -0,0 +1,251 @@ +{ + "description": "Edge-case compressed BLS12-381 encodings (valid points, sort-bit flips, identity encodings with stray flags or payload, missing compression flag, wrong lengths, uncompressed forms, x + p, points on the curve outside the subgroup) with the verdict of the Go reference: the KeyGroup of the drand/drand/v2 crypto schemes (G1 for the unchained scheme, G2 for bls-unchained-g1-rfc9380), backed by github.com/drand/kyber-bls12381 v0.3.4 over github.com/kilic/bls12-381 v0.1.0, as profile.Validate uses it. Encodings generated with @noble/curves 2.4.0 arithmetic; verdicts from scripts/bls12381-go-verdicts.go.", + "vectors": [ + { + "label": "g1_generator", + "group": "G1", + "hex": "97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", + "go": "point" + }, + { + "label": "g1_7G", + "group": "G1", + "hex": "b928f3beb93519eecf0145da903b40a4c97dca00b21f12ac0df3be9116ef2ef27b2ae6bcd4c5bc2d54ef5a70627efcb7", + "go": "point" + }, + { + "label": "g2_generator", + "group": "G2", + "hex": "93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8", + "go": "point" + }, + { + "label": "g2_quicknet_pk", + "group": "G2", + "hex": "83cf0f2896adee7eb8b5f01fcad3912212c437e0073e911fb90022d3e760183c8c4b450b6a0a6c3ac6a5776a2d1064510d1fec758c921cc22b0e17e63aaf4bcb5ed66304de9cf809bd274ca73bab4af5a6e9c76a4bc09e76eae8991ef5ece45a", + "go": "point" + }, + { + "label": "g2_7G", + "group": "G2", + "hex": "8d0273f6bf31ed37c3b8d68083ec3d8e20b5f2cc170fa24b9b5be35b34ed013f9a921f1cad1644d4bdb14674247234c8049cd1dbb2d2c3581e54c088135fef36505a6823d61b859437bfc79b617030dc8b40e32bad1fa85b9c0f368af6d38d3c", + "go": "point" + }, + { + "label": "g1_gen_sort_flipped", + "group": "G1", + "hex": "b7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", + "go": "point" + }, + { + "label": "g2_gen_sort_flipped", + "group": "G2", + "hex": "b3e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8", + "go": "point" + }, + { + "label": "g1_identity_c0", + "group": "G1", + "hex": "c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "go": "identity" + }, + { + "label": "g1_identity_sort_e0", + "group": "G1", + "hex": "e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "go": "invalid" + }, + { + "label": "g1_identity_payload_1", + "group": "G1", + "hex": "c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", + "go": "invalid" + }, + { + "label": "g1_identity_payload_p_low", + "group": "G1", + "hex": "da0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab", + "go": "invalid" + }, + { + "label": "g1_identity_payload_p_first", + "group": "G1", + "hex": "da0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab", + "go": "invalid" + }, + { + "label": "g1_infinity_without_compression_40", + "group": "G1", + "hex": "400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "go": "invalid" + }, + { + "label": "g1_all_zero", + "group": "G1", + "hex": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "go": "invalid" + }, + { + "label": "g1_compressed_x0", + "group": "G1", + "hex": "800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "go": "invalid" + }, + { + "label": "g2_identity_c0", + "group": "G2", + "hex": "c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "go": "identity" + }, + { + "label": "g2_identity_sort_e0", + "group": "G2", + "hex": "e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "go": "invalid" + }, + { + "label": "g2_identity_payload_1", + "group": "G2", + "hex": "c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", + "go": "invalid" + }, + { + "label": "g2_identity_payload_p_low", + "group": "G2", + "hex": "c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab", + "go": "invalid" + }, + { + "label": "g2_identity_payload_p_first", + "group": "G2", + "hex": "da0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "go": "invalid" + }, + { + "label": "g2_infinity_without_compression_40", + "group": "G2", + "hex": "400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "go": "invalid" + }, + { + "label": "g2_all_zero", + "group": "G2", + "hex": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "go": "invalid" + }, + { + "label": "g2_compressed_x0", + "group": "G2", + "hex": "800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "go": "invalid" + }, + { + "label": "g1_gen_no_compression_flag", + "group": "G1", + "hex": "17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", + "go": "invalid" + }, + { + "label": "g2_gen_no_compression_flag", + "group": "G2", + "hex": "13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8", + "go": "invalid" + }, + { + "label": "g1_len47", + "group": "G1", + "hex": "97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6", + "go": "invalid" + }, + { + "label": "g2_len95", + "group": "G2", + "hex": "93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bd", + "go": "invalid" + }, + { + "label": "g1_uncompressed_96", + "group": "G1", + "hex": "17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1", + "go": "invalid" + }, + { + "label": "g2_uncompressed_192", + "group": "G2", + "hex": "13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb80606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801", + "go": "invalid" + }, + { + "label": "g1_noncanonical_x_plus_p", + "group": "G1", + "hex": "bf73ddd4c9cd4de0d32470a193f4f1e3fb9926b584ad13e4aac0ffabba099c4f013b75ba40707c427d998c5529beb9f9", + "go": "invalid" + }, + { + "label": "g2_noncanonical_c1_plus_p", + "group": "G2", + "hex": "9afc95623e5b8ebb7e4582fca3d718e9820e7ee8b4a85d4644490e50e7c366c1181c96c49af5a770a89c7dc641a83f810411a5de6730ffece671a9f21d65028cc0f1102378de124562cb1ff49db6f004fcd14d683024b0548eff3d1468df2688", + "go": "invalid" + }, + { + "label": "g2_noncanonical_c0_plus_p", + "group": "G2", + "hex": "80fb837804dba8213329db46608b6c121d973363c1234a86dd183baff112709cf97096c5e9a1a770ee9d7dc641a894d61e12b7c8a0b0e687318d51a860b0af6425685ba86c632504c9fbf2959467e6291b7d4d66e178b05448fe3d1468ded133", + "go": "invalid" + }, + { + "label": "g1_x_equals_p", + "group": "G1", + "hex": "9a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab", + "go": "invalid" + }, + { + "label": "g2_c1_equals_p", + "group": "G2", + "hex": "9a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "go": "invalid" + }, + { + "label": "g2_c0_equals_p", + "group": "G2", + "hex": "8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab", + "go": "invalid" + }, + { + "label": "g1_not_on_curve", + "group": "G1", + "hex": "800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007", + "go": "invalid" + }, + { + "label": "g1_on_curve_not_in_subgroup", + "group": "G1", + "hex": "800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005", + "go": "invalid" + }, + { + "label": "g2_not_on_curve", + "group": "G2", + "hex": "800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006", + "go": "invalid" + }, + { + "label": "g2_on_curve_not_in_subgroup", + "group": "G2", + "hex": "800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", + "go": "invalid" + }, + { + "label": "g1_x_all_ones", + "group": "G1", + "hex": "9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "go": "invalid" + }, + { + "label": "g2_x_all_ones", + "group": "G2", + "hex": "9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "go": "invalid" + } + ] +}