Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
jxom committed Nov 4, 2024
1 parent b49521c commit 894cf19
Show file tree
Hide file tree
Showing 4 changed files with 225 additions and 1 deletion.
147 changes: 147 additions & 0 deletions src/Bls.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import { bls12_381 as bls } from '@noble/curves/bls12-381'

import type * as Bytes from './Bytes.js'
import type * as Errors from './Errors.js'
import * as Hex from './Hex.js'

export type Fp = bigint
export type Fp2 = { c0: Fp; c1: Fp }

// TODO: move to a `BlsPoint` module?
export type Point<F = Fp> = {
x: F
y: F
z: F
}

export type G1Point = Point<Fp>
export type G2Point = Point<Fp2>

export type Size = 'short' | 'long'

/** Re-export of @noble/curves BLS12-381 utilities. */

Check warning on line 22 in src/Bls.ts

View workflow job for this annotation

GitHub Actions / Verify / Checks

tsdoc-characters-after-block-tag: The token "@noble" looks like a TSDoc tag but contains an invalid character "/"; if it is not a tag, use a backslash to escape the "@"
export const noble = bls

/**
* Computes the BLS12-381 public key from a provided private key.
*
* Public Keys can be derived as a point on one of the BLS12-381 groups:
*
* - G1 Point (Default):
* - short (48 bytes)
* - computes longer G2 Signatures (96 bytes)
* - G2 Point:
* - long (96 bytes)
* - computes short G1 Signatures (48 bytes)
*
* @example
* ### Short G1 Public Keys (Default)
*
* ```ts twoslash
* import { Bls } from 'ox'
*
* const publicKey = Bls.getPublicKey({ privateKey: '0x...' })
* // ^?
*
*
*
*
* ```
*
* @example
* ### Long G2 Public Keys
*
* A G2 Public Key can be derived as a G2 point (96 bytes) using `size: 'long'`.
*
* This will allow you to compute G1 Signatures (48 bytes) with {@link Bls.sign}.
*
* ```ts twoslash
* import { Bls } from 'ox'
*
* const publicKey = Bls.getPublicKey({
* privateKey: '0x...',
* size: 'long',
* })
*
* publicKey
* // ^?
*
*
*
*
*
* ```
*
* @param options - The options to compute the public key.
* @returns The computed public key.
*/
export function getPublicKey<size extends Size = 'short'>(
options: getPublicKey.Options<size>,
): size extends 'short' ? G1Point : G2Point
// eslint-disable-next-line jsdoc/require-jsdoc
export function getPublicKey(options: getPublicKey.Options): Point<Fp | Fp2> {
const { privateKey, size = 'short' } = options
const group = size === 'short' ? bls.G1 : bls.G2
const { px, py, pz } = group.ProjectivePoint.fromPrivateKey(
Hex.from(privateKey).slice(2),
)
return { x: px, y: py, z: pz }
}

export declare namespace getPublicKey {
type Options<size extends Size = 'short'> = {
/**
* Private key to compute the public key from.
*/
privateKey: Hex.Hex | Bytes.Bytes
/**
* Size of the public key to compute.
*
* - `'short'`: 48 bytes; computes long signatures (96 bytes)
* - `'long'`: 96 bytes; computes short signatures (48 bytes)
*
* @default 'short'
*/
size?: size | Size | undefined
}

type ErrorType = Hex.from.ErrorType | Errors.GlobalErrorType
}

/**
* Generates a random BLS12-381 private key.
*
* @example
* ```ts twoslash
* import { Bls } from 'ox'
*
* const privateKey = Bls.randomPrivateKey()
* ```
*
* @param options - The options to generate the private key.
* @returns The generated private key.
*/
export function randomPrivateKey<as extends 'Hex' | 'Bytes' = 'Hex'>(
options: randomPrivateKey.Options<as> = {},
): randomPrivateKey.ReturnType<as> {
const { as = 'Hex' } = options
const bytes = bls.utils.randomPrivateKey()
if (as === 'Hex') return Hex.fromBytes(bytes) as never
return bytes as never
}

export declare namespace randomPrivateKey {
type Options<as extends 'Hex' | 'Bytes' = 'Hex'> = {
/**
* Format of the returned private key.
* @default 'Hex'
*/
as?: as | 'Hex' | 'Bytes' | undefined
}

type ReturnType<as extends 'Hex' | 'Bytes'> =
| (as extends 'Bytes' ? Bytes.Bytes : never)
| (as extends 'Hex' ? Hex.Hex : never)

type ErrorType = Hex.fromBytes.ErrorType | Errors.GlobalErrorType
}
2 changes: 1 addition & 1 deletion src/PublicKey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as Hex from './Hex.js'
import * as Json from './Json.js'
import type { Compute, ExactPartial } from './internal/types.js'

/** Root type for a Public Key. */
/** Root type for an ECDSA Public Key. */
export type PublicKey<
compressed extends boolean = false,
bigintType = bigint,
Expand Down
52 changes: 52 additions & 0 deletions src/_test/Bls.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Bls } from 'ox'
import { describe, expect, it } from 'vitest'

const privateKey =
'0x527f85c60ed7402247da21f1835cea651d0954fc15b7288f096d3608400cb6ac'

describe('getPublicKey', () => {
it('default', () => {
const publicKey = Bls.getPublicKey({ privateKey })
expect(publicKey).toMatchInlineSnapshot(`
{
"x": 1952783380189056174522580903352347766701573809635723835268303492562286913265402164399416172997666069723894699105894n,
"y": 3394089175947417419526317884165437122243448720528225119792553064107324599006426161993648623279289675312316462718429n,
"z": 1n,
}
`)
})

it('size: "long"', () => {
const publicKey = Bls.getPublicKey({ privateKey, size: 'long' })
expect(publicKey).toMatchInlineSnapshot(`
{
"x": {
"c0": 355700073819052008684778820175963255205495140126954969787089018774753222070622698188835174184164179369078130885244n,
"c1": 3141747483469678201696152507180044107401052508149828985947848597238369234167677882679751100991611433759974704216489n,
},
"y": {
"c0": 2066498625632373741693121319338450383866133445054897600869581552265032944423583015562782022208222353927807243866749n,
"c1": 2094957017561088565638483770249057751981351081887405244515911122357724535677090041039038848651564427361620547334206n,
},
"z": {
"c0": 1n,
"c1": 0n,
},
}
`)
})
})

describe('randomPrivateKey', () => {
it('default', () => {
const privateKey = Bls.randomPrivateKey()
expect(privateKey).toBeDefined()
expect(privateKey.length).toBe(66)
})

it('as: bytes', () => {
const privateKey = Bls.randomPrivateKey({ as: 'Bytes' })
expect(privateKey).toBeDefined()
expect(privateKey.length).toBe(32)
})
})
25 changes: 25 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -989,6 +989,31 @@ export * as Block from './Block.js'
*/
export * as Bloom from './Bloom.js'

/**
* Utility functions for [BLS12-381](https://hackmd.io/@benjaminion/bls12-381) cryptography.

Check warning on line 993 in src/index.ts

View workflow job for this annotation

GitHub Actions / Verify / Checks

tsdoc-at-sign-in-word: The "@" character looks like part of a TSDoc tag; use a backslash to escape it
*
* :::info
*
* The `Bls` module is a friendly wrapper over [`@noble/curves/bls12-381`](https://github.com/paulmillr/noble-curves), an **audited** implementation of BLS12-381.
*
* :::
*
* @example
* ### Computing a Random Private Key
*
* A random private key can be computed using {@link ox#Bls.(randomPrivateKey:function)}:
*
* ```ts twoslash
* import { Bls } from 'ox'
*
* const privateKey = Bls.randomPrivateKey()
* // @log: '0x...'
* ```
*
* @category Crypto
*/
export * as Bls from './Bls.js'

/**
* A set of Ethereum-related utility functions for working with [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) instances.
*
Expand Down

0 comments on commit 894cf19

Please sign in to comment.