- Consolidated duplicate UndoManagers to single instance - Fixed connection promise to only resolve on 'connected' status - Fixed WebSocketProvider import (WebsocketProvider) - Added proper doc.destroy() cleanup - Renamed isPresenceInitialized property to avoid conflict Co-Authored-By: Paperclip <noreply@paperclip.ing>
245 lines
6.5 KiB
TypeScript
245 lines
6.5 KiB
TypeScript
import { secp256r1 } from '@noble/curves/p256'
|
|
import * as Bytes from './Bytes.js'
|
|
import type * as Errors from './Errors.js'
|
|
import * as Hex from './Hex.js'
|
|
import * as PublicKey from './PublicKey.js'
|
|
import type * as Signature from './Signature.js'
|
|
import * as Entropy from './internal/entropy.js'
|
|
|
|
/** Re-export of noble/curves P256 utilities. */
|
|
export const noble = secp256r1
|
|
|
|
/**
|
|
* Computes the P256 ECDSA public key from a provided private key.
|
|
*
|
|
* @example
|
|
* ```ts twoslash
|
|
* import { P256 } from 'ox'
|
|
*
|
|
* const publicKey = P256.getPublicKey({ privateKey: '0x...' })
|
|
* ```
|
|
*
|
|
* @param options - The options to compute the public key.
|
|
* @returns The computed public key.
|
|
*/
|
|
export function getPublicKey(
|
|
options: getPublicKey.Options,
|
|
): PublicKey.PublicKey {
|
|
const { privateKey } = options
|
|
const point = secp256r1.ProjectivePoint.fromPrivateKey(
|
|
typeof privateKey === 'string'
|
|
? privateKey.slice(2)
|
|
: Hex.fromBytes(privateKey).slice(2),
|
|
)
|
|
return PublicKey.from(point)
|
|
}
|
|
|
|
export declare namespace getPublicKey {
|
|
type Options = {
|
|
/**
|
|
* Private key to compute the public key from.
|
|
*/
|
|
privateKey: Hex.Hex | Bytes.Bytes
|
|
}
|
|
|
|
type ErrorType = Errors.GlobalErrorType
|
|
}
|
|
|
|
/**
|
|
* Generates a random P256 ECDSA private key.
|
|
*
|
|
* @example
|
|
* ```ts twoslash
|
|
* import { P256 } from 'ox'
|
|
*
|
|
* const privateKey = P256.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 = secp256r1.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
|
|
}
|
|
|
|
/**
|
|
* Recovers the signing public key from the signed payload and signature.
|
|
*
|
|
* @example
|
|
* ```ts twoslash
|
|
* import { P256 } from 'ox'
|
|
*
|
|
* const signature = P256.sign({ payload: '0xdeadbeef', privateKey: '0x...' })
|
|
*
|
|
* const publicKey = P256.recoverPublicKey({ // [!code focus]
|
|
* payload: '0xdeadbeef', // [!code focus]
|
|
* signature, // [!code focus]
|
|
* }) // [!code focus]
|
|
* ```
|
|
*
|
|
* @param options - The recovery options.
|
|
* @returns The recovered public key.
|
|
*/
|
|
export function recoverPublicKey(
|
|
options: recoverPublicKey.Options,
|
|
): PublicKey.PublicKey {
|
|
const { payload, signature } = options
|
|
const { r, s, yParity } = signature
|
|
const signature_ = new secp256r1.Signature(
|
|
BigInt(r),
|
|
BigInt(s),
|
|
).addRecoveryBit(yParity)
|
|
const payload_ =
|
|
payload instanceof Uint8Array ? Hex.fromBytes(payload) : payload
|
|
const point = signature_.recoverPublicKey(payload_.substring(2))
|
|
return PublicKey.from(point)
|
|
}
|
|
|
|
export declare namespace recoverPublicKey {
|
|
type Options = {
|
|
/** Payload that was signed. */
|
|
payload: Hex.Hex | Bytes.Bytes
|
|
/** Signature of the payload. */
|
|
signature: Signature.Signature
|
|
}
|
|
|
|
type ErrorType =
|
|
| PublicKey.from.ErrorType
|
|
| Hex.fromBytes.ErrorType
|
|
| Errors.GlobalErrorType
|
|
}
|
|
|
|
/**
|
|
* Signs the payload with the provided private key and returns a P256 signature.
|
|
*
|
|
* @example
|
|
* ```ts twoslash
|
|
* import { P256 } from 'ox'
|
|
*
|
|
* const signature = P256.sign({ // [!code focus]
|
|
* payload: '0xdeadbeef', // [!code focus]
|
|
* privateKey: '0x...' // [!code focus]
|
|
* }) // [!code focus]
|
|
* ```
|
|
*
|
|
* @param options - The signing options.
|
|
* @returns The ECDSA {@link ox#Signature.Signature}.
|
|
*/
|
|
export function sign(options: sign.Options): Signature.Signature {
|
|
const {
|
|
extraEntropy = Entropy.extraEntropy,
|
|
hash,
|
|
payload,
|
|
privateKey,
|
|
} = options
|
|
const { r, s, recovery } = secp256r1.sign(
|
|
payload instanceof Uint8Array ? payload : Bytes.fromHex(payload),
|
|
privateKey instanceof Uint8Array ? privateKey : Bytes.fromHex(privateKey),
|
|
{
|
|
extraEntropy:
|
|
typeof extraEntropy === 'boolean'
|
|
? extraEntropy
|
|
: Hex.from(extraEntropy).slice(2),
|
|
lowS: true,
|
|
...(hash ? { prehash: true } : {}),
|
|
},
|
|
)
|
|
return {
|
|
r,
|
|
s,
|
|
yParity: recovery,
|
|
}
|
|
}
|
|
|
|
export declare namespace sign {
|
|
type Options = {
|
|
/**
|
|
* Extra entropy to add to the signing process. Setting to `false` will disable it.
|
|
* @default true
|
|
*/
|
|
extraEntropy?: boolean | Hex.Hex | Bytes.Bytes | undefined
|
|
/**
|
|
* If set to `true`, the payload will be hashed (sha256) before being signed.
|
|
*/
|
|
hash?: boolean | undefined
|
|
/**
|
|
* Payload to sign.
|
|
*/
|
|
payload: Hex.Hex | Bytes.Bytes
|
|
/**
|
|
* ECDSA private key.
|
|
*/
|
|
privateKey: Hex.Hex | Bytes.Bytes
|
|
}
|
|
|
|
type ErrorType = Bytes.fromHex.ErrorType | Errors.GlobalErrorType
|
|
}
|
|
|
|
/**
|
|
* Verifies a payload was signed by the provided public key.
|
|
*
|
|
* @example
|
|
*
|
|
* ```ts twoslash
|
|
* import { P256 } from 'ox'
|
|
*
|
|
* const privateKey = P256.randomPrivateKey()
|
|
* const publicKey = P256.getPublicKey({ privateKey })
|
|
* const signature = P256.sign({ payload: '0xdeadbeef', privateKey })
|
|
*
|
|
* const verified = P256.verify({ // [!code focus]
|
|
* publicKey, // [!code focus]
|
|
* payload: '0xdeadbeef', // [!code focus]
|
|
* signature, // [!code focus]
|
|
* }) // [!code focus]
|
|
* ```
|
|
*
|
|
* @param options - The verification options.
|
|
* @returns Whether the payload was signed by the provided public key.
|
|
*/
|
|
export function verify(options: verify.Options): boolean {
|
|
const { hash, payload, publicKey, signature } = options
|
|
return secp256r1.verify(
|
|
signature,
|
|
payload instanceof Uint8Array ? payload : Bytes.fromHex(payload),
|
|
PublicKey.toHex(publicKey).substring(2),
|
|
...(hash ? [{ prehash: true, lowS: true }] : []),
|
|
)
|
|
}
|
|
|
|
export declare namespace verify {
|
|
type Options = {
|
|
/** If set to `true`, the payload will be hashed (sha256) before being verified. */
|
|
hash?: boolean | undefined
|
|
/** Payload that was signed. */
|
|
payload: Hex.Hex | Bytes.Bytes
|
|
/** Public key that signed the payload. */
|
|
publicKey: PublicKey.PublicKey<boolean>
|
|
/** Signature of the payload. */
|
|
signature: Signature.Signature<boolean>
|
|
}
|
|
|
|
type ErrorType = Errors.GlobalErrorType
|
|
}
|