ox
Version:
Ethereum Standard Library
362 lines (303 loc) • 11.7 kB
text/typescript
import { describe, expect, test } from 'vp/test'
import * as Bytes from '../Bytes.js'
import * as Hex from '../Hex.js'
import * as X25519 from '../X25519.js'
describe('noble', () => {
test('exports noble curves x25519', () => {
expect(X25519.noble).toBeDefined()
expect(typeof X25519.noble.getPublicKey).toBe('function')
expect(typeof X25519.noble.getSharedSecret).toBe('function')
expect(typeof X25519.noble.utils.randomSecretKey).toBe('function')
})
})
describe('createKeyPair', () => {
test('creates key pair with default format (Hex)', () => {
const keyPair = X25519.createKeyPair()
expect(keyPair.privateKey).toMatch(/^0x[0-9a-f]{64}$/i)
expect(keyPair.publicKey).toMatch(/^0x[0-9a-f]{64}$/i)
})
test('creates key pair with Hex format', () => {
const keyPair = X25519.createKeyPair({ as: 'Hex' })
expect(keyPair.privateKey).toMatch(/^0x[0-9a-f]{64}$/i)
expect(keyPair.publicKey).toMatch(/^0x[0-9a-f]{64}$/i)
})
test('creates key pair with Bytes format', () => {
const keyPair = X25519.createKeyPair({ as: 'Bytes' })
expect(keyPair.privateKey).toBeInstanceOf(Uint8Array)
expect(keyPair.publicKey).toBeInstanceOf(Uint8Array)
expect(keyPair.privateKey).toHaveLength(32)
expect(keyPair.publicKey).toHaveLength(32)
})
test('creates different key pairs on each call', () => {
const keyPair1 = X25519.createKeyPair()
const keyPair2 = X25519.createKeyPair()
expect(keyPair1.privateKey).not.toBe(keyPair2.privateKey)
expect(keyPair1.publicKey).not.toBe(keyPair2.publicKey)
})
test('public key is derived from private key', () => {
const keyPair = X25519.createKeyPair()
const publicKey = X25519.getPublicKey({ privateKey: keyPair.privateKey })
expect(publicKey).toBe(keyPair.publicKey)
})
})
describe('getPublicKey', () => {
test('computes public key from private key (Hex input)', () => {
const privateKey =
'0x7777777777777777777777777777777777777777777777777777777777777777'
const publicKey = X25519.getPublicKey({ privateKey })
expect(publicKey).toMatch(/^0x[0-9a-f]{64}$/i)
})
test('computes public key from private key (Bytes input)', () => {
const privateKey = new Uint8Array(32).fill(0x77)
const publicKey = X25519.getPublicKey({ privateKey })
expect(publicKey).toMatch(/^0x[0-9a-f]{64}$/i)
})
test('returns public key in Hex format by default', () => {
const privateKey = X25519.randomPrivateKey()
const publicKey = X25519.getPublicKey({ privateKey })
expect(publicKey).toMatch(/^0x[0-9a-f]{64}$/i)
})
test('returns public key in Hex format when specified', () => {
const privateKey = X25519.randomPrivateKey()
const publicKey = X25519.getPublicKey({ privateKey, as: 'Hex' })
expect(publicKey).toMatch(/^0x[0-9a-f]{64}$/i)
})
test('returns public key in Bytes format when specified', () => {
const privateKey = X25519.randomPrivateKey()
const publicKey = X25519.getPublicKey({ privateKey, as: 'Bytes' })
expect(publicKey).toBeInstanceOf(Uint8Array)
expect(publicKey).toHaveLength(32)
})
test('is deterministic - same private key produces same public key', () => {
const privateKey =
'0x7777777777777777777777777777777777777777777777777777777777777777'
const publicKey1 = X25519.getPublicKey({ privateKey })
const publicKey2 = X25519.getPublicKey({ privateKey })
expect(publicKey1).toBe(publicKey2)
})
test('different private keys produce different public keys', () => {
const privateKey1 =
'0x7777777777777777777777777777777777777777777777777777777777777777'
const privateKey2 =
'0x8888888888888888888888888888888888888888888888888888888888888888'
const publicKey1 = X25519.getPublicKey({ privateKey: privateKey1 })
const publicKey2 = X25519.getPublicKey({ privateKey: privateKey2 })
expect(publicKey1).not.toBe(publicKey2)
})
})
describe('getSharedSecret', () => {
test('computes shared secret between two key pairs', () => {
const keyPairA = X25519.createKeyPair()
const keyPairB = X25519.createKeyPair()
const sharedSecretA = X25519.getSharedSecret({
privateKey: keyPairA.privateKey,
publicKey: keyPairB.publicKey,
})
const sharedSecretB = X25519.getSharedSecret({
privateKey: keyPairB.privateKey,
publicKey: keyPairA.publicKey,
})
expect(sharedSecretA).toBe(sharedSecretB)
expect(sharedSecretA).toMatch(/^0x[0-9a-f]{64}$/i)
})
test('computes shared secret with Hex inputs', () => {
const privateKeyA =
'0x7777777777777777777777777777777777777777777777777777777777777777'
const publicKeyB = X25519.getPublicKey({
privateKey:
'0x8888888888888888888888888888888888888888888888888888888888888888',
})
const sharedSecret = X25519.getSharedSecret({
privateKey: privateKeyA,
publicKey: publicKeyB,
})
expect(sharedSecret).toMatch(/^0x[0-9a-f]{64}$/i)
})
test('computes shared secret with Bytes inputs', () => {
const privateKeyA = new Uint8Array(32).fill(0x77)
const publicKeyB = X25519.getPublicKey({
privateKey: new Uint8Array(32).fill(0x88),
})
const sharedSecret = X25519.getSharedSecret({
privateKey: privateKeyA,
publicKey: publicKeyB,
})
expect(sharedSecret).toMatch(/^0x[0-9a-f]{64}$/i)
})
test('returns shared secret in Hex format by default', () => {
const keyPairA = X25519.createKeyPair()
const keyPairB = X25519.createKeyPair()
const sharedSecret = X25519.getSharedSecret({
privateKey: keyPairA.privateKey,
publicKey: keyPairB.publicKey,
})
expect(sharedSecret).toMatch(/^0x[0-9a-f]{64}$/i)
})
test('returns shared secret in Hex format when specified', () => {
const keyPairA = X25519.createKeyPair()
const keyPairB = X25519.createKeyPair()
const sharedSecret = X25519.getSharedSecret({
privateKey: keyPairA.privateKey,
publicKey: keyPairB.publicKey,
as: 'Hex',
})
expect(sharedSecret).toMatch(/^0x[0-9a-f]{64}$/i)
})
test('returns shared secret in Bytes format when specified', () => {
const keyPairA = X25519.createKeyPair()
const keyPairB = X25519.createKeyPair()
const sharedSecret = X25519.getSharedSecret({
privateKey: keyPairA.privateKey,
publicKey: keyPairB.publicKey,
as: 'Bytes',
})
expect(sharedSecret).toBeInstanceOf(Uint8Array)
expect(sharedSecret).toHaveLength(32)
})
test('is deterministic - same inputs produce same shared secret', () => {
const privateKeyA =
'0x7777777777777777777777777777777777777777777777777777777777777777'
const publicKeyB = X25519.getPublicKey({
privateKey:
'0x8888888888888888888888888888888888888888888888888888888888888888',
})
const sharedSecret1 = X25519.getSharedSecret({
privateKey: privateKeyA,
publicKey: publicKeyB,
})
const sharedSecret2 = X25519.getSharedSecret({
privateKey: privateKeyA,
publicKey: publicKeyB,
})
expect(sharedSecret1).toBe(sharedSecret2)
})
test('different key pairs produce different shared secrets', () => {
const keyPairA = X25519.createKeyPair()
const keyPairB = X25519.createKeyPair()
const keyPairC = X25519.createKeyPair()
const sharedSecretAB = X25519.getSharedSecret({
privateKey: keyPairA.privateKey,
publicKey: keyPairB.publicKey,
})
const sharedSecretAC = X25519.getSharedSecret({
privateKey: keyPairA.privateKey,
publicKey: keyPairC.publicKey,
})
expect(sharedSecretAB).not.toBe(sharedSecretAC)
})
test('mixed format inputs work correctly', () => {
const privateKeyHex =
'0x7777777777777777777777777777777777777777777777777777777777777777'
const publicKeyBytes = X25519.getPublicKey({
privateKey:
'0x8888888888888888888888888888888888888888888888888888888888888888',
as: 'Bytes',
})
const sharedSecret = X25519.getSharedSecret({
privateKey: privateKeyHex,
publicKey: publicKeyBytes,
})
expect(sharedSecret).toMatch(/^0x[0-9a-f]{64}$/i)
})
})
describe('randomPrivateKey', () => {
test('generates private key in Hex format by default', () => {
const privateKey = X25519.randomPrivateKey()
expect(privateKey).toMatch(/^0x[0-9a-f]{64}$/i)
})
test('generates private key in Hex format when specified', () => {
const privateKey = X25519.randomPrivateKey({ as: 'Hex' })
expect(privateKey).toMatch(/^0x[0-9a-f]{64}$/i)
})
test('generates private key in Bytes format when specified', () => {
const privateKey = X25519.randomPrivateKey({ as: 'Bytes' })
expect(privateKey).toBeInstanceOf(Uint8Array)
expect(privateKey).toHaveLength(32)
})
test('generates different private keys on each call', () => {
const privateKey1 = X25519.randomPrivateKey()
const privateKey2 = X25519.randomPrivateKey()
expect(privateKey1).not.toBe(privateKey2)
})
test('generated private keys are valid (can derive public keys)', () => {
const privateKey = X25519.randomPrivateKey()
const publicKey = X25519.getPublicKey({ privateKey })
expect(publicKey).toMatch(/^0x[0-9a-f]{64}$/i)
})
test('generated private keys work for ECDH', () => {
const privateKeyA = X25519.randomPrivateKey()
const privateKeyB = X25519.randomPrivateKey()
const publicKeyA = X25519.getPublicKey({ privateKey: privateKeyA })
const publicKeyB = X25519.getPublicKey({ privateKey: privateKeyB })
const sharedSecretA = X25519.getSharedSecret({
privateKey: privateKeyA,
publicKey: publicKeyB,
})
const sharedSecretB = X25519.getSharedSecret({
privateKey: privateKeyB,
publicKey: publicKeyA,
})
expect(sharedSecretA).toBe(sharedSecretB)
})
})
describe('error handling', () => {
test('handles invalid private key format', () => {
expect(() => {
X25519.getPublicKey({ privateKey: 'invalid' as any })
}).toThrow()
})
test('handles invalid public key format in getSharedSecret', () => {
const privateKey = X25519.randomPrivateKey()
expect(() => {
X25519.getSharedSecret({
privateKey,
publicKey: 'invalid' as any,
})
}).toThrow()
})
test('handles short private key', () => {
expect(() => {
X25519.getPublicKey({ privateKey: '0x1234' })
}).toThrow()
})
test('handles short public key in getSharedSecret', () => {
const privateKey = X25519.randomPrivateKey()
expect(() => {
X25519.getSharedSecret({
privateKey,
publicKey: '0x1234',
})
}).toThrow()
})
})
describe('format conversion consistency', () => {
test('Hex and Bytes formats represent same values', () => {
const privateKeyHex =
'0x7777777777777777777777777777777777777777777777777777777777777777'
const privateKeyBytes = Bytes.from(privateKeyHex)
const publicKeyHex = X25519.getPublicKey({
privateKey: privateKeyHex,
as: 'Hex',
})
const publicKeyBytes = X25519.getPublicKey({
privateKey: privateKeyBytes,
as: 'Bytes',
})
expect(Hex.fromBytes(publicKeyBytes)).toBe(publicKeyHex)
})
test('shared secret format conversion consistency', () => {
const keyPairA = X25519.createKeyPair()
const keyPairB = X25519.createKeyPair()
const sharedSecretHex = X25519.getSharedSecret({
privateKey: keyPairA.privateKey,
publicKey: keyPairB.publicKey,
as: 'Hex',
})
const sharedSecretBytes = X25519.getSharedSecret({
privateKey: keyPairA.privateKey,
publicKey: keyPairB.publicKey,
as: 'Bytes',
})
expect(Hex.fromBytes(sharedSecretBytes)).toBe(sharedSecretHex)
})
})