UNPKG

ox

Version:

Ethereum Standard Library

439 lines (377 loc) 14 kB
import { Bytes, Ed25519, Hex, X25519 } from 'ox' import { describe, expect, test } from 'vp/test' describe('createKeyPair', () => { test('default', () => { const keyPair = Ed25519.createKeyPair() expect(keyPair).toHaveProperty('privateKey') expect(keyPair).toHaveProperty('publicKey') expect(typeof keyPair.privateKey).toBe('string') expect(typeof keyPair.publicKey).toBe('string') expect(keyPair.privateKey).toMatch(/^0x[0-9a-fA-F]{64}$/) expect(keyPair.publicKey).toMatch(/^0x[0-9a-fA-F]{64}$/) }) test('with as: Hex', () => { const keyPair = Ed25519.createKeyPair({ as: 'Hex' }) expect(keyPair).toHaveProperty('privateKey') expect(keyPair).toHaveProperty('publicKey') expect(typeof keyPair.privateKey).toBe('string') expect(typeof keyPair.publicKey).toBe('string') expect(keyPair.privateKey).toMatch(/^0x[0-9a-fA-F]{64}$/) expect(keyPair.publicKey).toMatch(/^0x[0-9a-fA-F]{64}$/) }) test('with as: Bytes', () => { const keyPair = Ed25519.createKeyPair({ as: 'Bytes' }) expect(keyPair).toHaveProperty('privateKey') expect(keyPair).toHaveProperty('publicKey') expect(keyPair.privateKey).toBeInstanceOf(Uint8Array) expect(keyPair.publicKey).toBeInstanceOf(Uint8Array) expect(keyPair.privateKey).toHaveLength(32) expect(keyPair.publicKey).toHaveLength(32) }) test('unique keys', () => { const keyPair1 = Ed25519.createKeyPair() const keyPair2 = Ed25519.createKeyPair() expect(keyPair1.privateKey).not.toBe(keyPair2.privateKey) expect(keyPair1.publicKey).not.toBe(keyPair2.publicKey) }) }) describe('getPublicKey', () => { const privateKey = '0x9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60' const expectedPublicKey = '0xd75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a' test('default (Hex)', () => { const publicKey = Ed25519.getPublicKey({ privateKey }) expect(publicKey).toBe(expectedPublicKey) }) test('with as: Hex', () => { const publicKey = Ed25519.getPublicKey({ privateKey, as: 'Hex' }) expect(publicKey).toBe(expectedPublicKey) }) test('with as: Bytes', () => { const publicKey = Ed25519.getPublicKey({ privateKey, as: 'Bytes' }) expect(publicKey).toBeInstanceOf(Uint8Array) expect(publicKey).toHaveLength(32) expect(Hex.fromBytes(publicKey)).toBe(expectedPublicKey) }) test('with Bytes private key', () => { const privateKeyBytes = Bytes.fromHex(privateKey) const publicKey = Ed25519.getPublicKey({ privateKey: privateKeyBytes }) expect(publicKey).toBe(expectedPublicKey) }) test('with Bytes private key, as: Bytes', () => { const privateKeyBytes = Bytes.fromHex(privateKey) const publicKey = Ed25519.getPublicKey({ privateKey: privateKeyBytes, as: 'Bytes', }) expect(publicKey).toBeInstanceOf(Uint8Array) expect(publicKey).toHaveLength(32) expect(Hex.fromBytes(publicKey)).toBe(expectedPublicKey) }) }) describe('randomPrivateKey', () => { test('default (Hex)', () => { const privateKey = Ed25519.randomPrivateKey() expect(typeof privateKey).toBe('string') expect(privateKey).toMatch(/^0x[0-9a-fA-F]{64}$/) }) test('with as: Hex', () => { const privateKey = Ed25519.randomPrivateKey({ as: 'Hex' }) expect(typeof privateKey).toBe('string') expect(privateKey).toMatch(/^0x[0-9a-fA-F]{64}$/) }) test('with as: Bytes', () => { const privateKey = Ed25519.randomPrivateKey({ as: 'Bytes' }) expect(privateKey).toBeInstanceOf(Uint8Array) expect(privateKey).toHaveLength(32) }) test('unique keys', () => { const privateKey1 = Ed25519.randomPrivateKey() const privateKey2 = Ed25519.randomPrivateKey() expect(privateKey1).not.toBe(privateKey2) }) }) describe('sign', () => { const privateKey = '0x9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60' const payload = '0xdeadbeef' test('default (Hex)', () => { const signature = Ed25519.sign({ payload, privateKey }) expect(typeof signature).toBe('string') expect(signature).toMatch(/^0x[0-9a-fA-F]{128}$/) }) test('with as: Hex', () => { const signature = Ed25519.sign({ payload, privateKey, as: 'Hex' }) expect(typeof signature).toBe('string') expect(signature).toMatch(/^0x[0-9a-fA-F]{128}$/) }) test('with as: Bytes', () => { const signature = Ed25519.sign({ payload, privateKey, as: 'Bytes' }) expect(signature).toBeInstanceOf(Uint8Array) expect(signature).toHaveLength(64) }) test('with Bytes payload', () => { const payloadBytes = Bytes.fromHex(payload) const signature = Ed25519.sign({ payload: payloadBytes, privateKey }) expect(typeof signature).toBe('string') expect(signature).toMatch(/^0x[0-9a-fA-F]{128}$/) }) test('with Bytes private key', () => { const privateKeyBytes = Bytes.fromHex(privateKey) const signature = Ed25519.sign({ payload, privateKey: privateKeyBytes }) expect(typeof signature).toBe('string') expect(signature).toMatch(/^0x[0-9a-fA-F]{128}$/) }) test('with all Bytes', () => { const payloadBytes = Bytes.fromHex(payload) const privateKeyBytes = Bytes.fromHex(privateKey) const signature = Ed25519.sign({ payload: payloadBytes, privateKey: privateKeyBytes, as: 'Bytes', }) expect(signature).toBeInstanceOf(Uint8Array) expect(signature).toHaveLength(64) }) test('deterministic', () => { const signature1 = Ed25519.sign({ payload, privateKey }) const signature2 = Ed25519.sign({ payload, privateKey }) expect(signature1).toBe(signature2) }) test('known test vector', () => { // From RFC 8032 test vectors const testPrivateKey = '0x9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60' const testPayload = '0x' // empty message const signature = Ed25519.sign({ payload: testPayload, privateKey: testPrivateKey, }) expect(signature).toBe( '0xe5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b', ) }) describe('integration', () => { test('sign and verify cycle', () => { const { privateKey, publicKey } = Ed25519.createKeyPair() const payload = '0x48656c6c6f2c20576f726c6421' const signature = Ed25519.sign({ payload, privateKey }) const isValid = Ed25519.verify({ payload, publicKey, signature }) expect(isValid).toBe(true) }) test('sign and verify cycle with Bytes', () => { const { privateKey, publicKey } = Ed25519.createKeyPair({ as: 'Bytes' }) const payload = Bytes.fromString('Hello, World!') const signature = Ed25519.sign({ payload, privateKey, as: 'Bytes' }) const isValid = Ed25519.verify({ payload, publicKey, signature }) expect(isValid).toBe(true) }) test('multiple signatures with same key', () => { const { privateKey, publicKey } = Ed25519.createKeyPair() const payloads = [ '0x48656c6c6f', '0x576f726c64', '0x546573744d657373616765', ] as const for (const payload of payloads) { const signature = Ed25519.sign({ payload, privateKey }) const isValid = Ed25519.verify({ payload, publicKey, signature }) expect(isValid).toBe(true) } }) }) }) describe('verify', () => { const privateKey = '0x9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60' const publicKey = '0xd75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a' const payload = '0xdeadbeef' test('valid signature', () => { const signature = Ed25519.sign({ payload, privateKey }) const isValid = Ed25519.verify({ payload, publicKey, signature }) expect(isValid).toBe(true) }) test('invalid signature', () => { const signature = ('0x' + '00'.repeat(64)) as Hex.Hex const isValid = Ed25519.verify({ payload, publicKey, signature }) expect(isValid).toBe(false) }) test('wrong payload', () => { const signature = Ed25519.sign({ payload, privateKey }) const isValid = Ed25519.verify({ payload: '0xcafebabe', publicKey, signature, }) expect(isValid).toBe(false) }) test('wrong public key', () => { const signature = Ed25519.sign({ payload, privateKey }) const wrongPublicKey = ('0x' + '11'.repeat(32)) as Hex.Hex const isValid = Ed25519.verify({ payload, publicKey: wrongPublicKey, signature, }) expect(isValid).toBe(false) }) test('with Bytes payload', () => { const payloadBytes = Bytes.fromHex(payload) const signature = Ed25519.sign({ payload, privateKey }) const isValid = Ed25519.verify({ payload: payloadBytes, publicKey, signature, }) expect(isValid).toBe(true) }) test('with Bytes public key', () => { const publicKeyBytes = Bytes.fromHex(publicKey) const signature = Ed25519.sign({ payload, privateKey }) const isValid = Ed25519.verify({ payload, publicKey: publicKeyBytes, signature, }) expect(isValid).toBe(true) }) test('with Bytes signature', () => { const signatureHex = Ed25519.sign({ payload, privateKey }) const signatureBytes = Bytes.fromHex(signatureHex) const isValid = Ed25519.verify({ payload, publicKey, signature: signatureBytes, }) expect(isValid).toBe(true) }) test('with all Bytes', () => { const payloadBytes = Bytes.fromHex(payload) const publicKeyBytes = Bytes.fromHex(publicKey) const signatureBytes = Ed25519.sign({ payload: payloadBytes, privateKey, as: 'Bytes', }) const isValid = Ed25519.verify({ payload: payloadBytes, publicKey: publicKeyBytes, signature: signatureBytes, }) expect(isValid).toBe(true) }) test('known test vector', () => { // From RFC 8032 test vectors const testPublicKey = '0xd75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a' const testPayload = '0x' // empty message const testSignature = '0xe5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b' const isValid = Ed25519.verify({ payload: testPayload, publicKey: testPublicKey, signature: testSignature, }) expect(isValid).toBe(true) }) }) describe('toX25519PublicKey', () => { test('default (Hex)', () => { const { publicKey } = Ed25519.createKeyPair() const x25519PublicKey = Ed25519.toX25519PublicKey({ publicKey }) expect(typeof x25519PublicKey).toBe('string') expect(x25519PublicKey).toMatch(/^0x[0-9a-fA-F]{64}$/) }) test('with as: Bytes', () => { const { publicKey } = Ed25519.createKeyPair() const x25519PublicKey = Ed25519.toX25519PublicKey({ publicKey, as: 'Bytes', }) expect(x25519PublicKey).toBeInstanceOf(Uint8Array) expect(x25519PublicKey).toHaveLength(32) }) test('with Bytes public key', () => { const { publicKey } = Ed25519.createKeyPair({ as: 'Bytes' }) const x25519PublicKey = Ed25519.toX25519PublicKey({ publicKey }) expect(typeof x25519PublicKey).toBe('string') expect(x25519PublicKey).toMatch(/^0x[0-9a-fA-F]{64}$/) }) test('deterministic', () => { const { publicKey } = Ed25519.createKeyPair() const a = Ed25519.toX25519PublicKey({ publicKey }) const b = Ed25519.toX25519PublicKey({ publicKey }) expect(a).toBe(b) }) test('different from ed25519 public key', () => { const { publicKey } = Ed25519.createKeyPair() const x25519PublicKey = Ed25519.toX25519PublicKey({ publicKey }) expect(x25519PublicKey).not.toBe(publicKey) }) }) describe('toX25519PrivateKey', () => { test('default (Hex)', () => { const { privateKey } = Ed25519.createKeyPair() const x25519PrivateKey = Ed25519.toX25519PrivateKey({ privateKey }) expect(typeof x25519PrivateKey).toBe('string') expect(x25519PrivateKey).toMatch(/^0x[0-9a-fA-F]{64}$/) }) test('with as: Bytes', () => { const { privateKey } = Ed25519.createKeyPair() const x25519PrivateKey = Ed25519.toX25519PrivateKey({ privateKey, as: 'Bytes', }) expect(x25519PrivateKey).toBeInstanceOf(Uint8Array) expect(x25519PrivateKey).toHaveLength(32) }) test('with Bytes private key', () => { const { privateKey } = Ed25519.createKeyPair({ as: 'Bytes' }) const x25519PrivateKey = Ed25519.toX25519PrivateKey({ privateKey }) expect(typeof x25519PrivateKey).toBe('string') expect(x25519PrivateKey).toMatch(/^0x[0-9a-fA-F]{64}$/) }) test('deterministic', () => { const { privateKey } = Ed25519.createKeyPair() const a = Ed25519.toX25519PrivateKey({ privateKey }) const b = Ed25519.toX25519PrivateKey({ privateKey }) expect(a).toBe(b) }) }) describe('toX25519 integration', () => { test('converted keys produce valid X25519 shared secret', () => { const alice = Ed25519.createKeyPair() const bob = Ed25519.createKeyPair() const aliceX25519Priv = Ed25519.toX25519PrivateKey({ privateKey: alice.privateKey, }) const bobX25519Pub = Ed25519.toX25519PublicKey({ publicKey: bob.publicKey, }) const bobX25519Priv = Ed25519.toX25519PrivateKey({ privateKey: bob.privateKey, }) const aliceX25519Pub = Ed25519.toX25519PublicKey({ publicKey: alice.publicKey, }) const sharedA = X25519.getSharedSecret({ privateKey: aliceX25519Priv, publicKey: bobX25519Pub, }) const sharedB = X25519.getSharedSecret({ privateKey: bobX25519Priv, publicKey: aliceX25519Pub, }) expect(sharedA).toBe(sharedB) }) }) describe('noble export', () => { test('exports noble curves ed25519', () => { expect(Ed25519.noble).toBeDefined() expect(Ed25519.noble.sign).toBeDefined() expect(Ed25519.noble.verify).toBeDefined() expect(Ed25519.noble.getPublicKey).toBeDefined() }) })