@bsv/sdk
Version:
BSV Blockchain Software Development Kit
222 lines (201 loc) • 6.86 kB
text/typescript
import {
PrivateKey,
PublicKey,
SymmetricKey,
Utils,
Hash
} from '../../primitives/index'
import KeyDeriver from '../../wallet/KeyDeriver'
describe('KeyDeriver', () => {
const rootPrivateKey = new PrivateKey(42)
const rootPublicKey = rootPrivateKey.toPublicKey()
const counterpartyPrivateKey = new PrivateKey(69)
const counterpartyPublicKey = counterpartyPrivateKey.toPublicKey()
const anyonePublicKey = new PrivateKey(1).toPublicKey()
const protocolID: [0 | 1 | 2, string] = [0, 'testprotocol']
const keyID = '12345'
let keyDeriver: KeyDeriver
beforeEach(() => {
keyDeriver = new KeyDeriver(rootPrivateKey)
})
test('should compute the correct invoice number', () => {
const invoiceNumber = (keyDeriver as any).computeInvoiceNumber(
protocolID,
keyID
)
expect(invoiceNumber).toBe('0-testprotocol-12345')
})
test('should throw if no co counterparty given', () => {
expect(() => (keyDeriver as any).normalizeCounterparty()).toThrow()
})
test('should normalize counterparty correctly for self', () => {
const normalized = (keyDeriver as any).normalizeCounterparty('self')
expect(normalized.toString()).toBe(rootPublicKey.toString())
})
test('should normalize counterparty correctly for anyone', () => {
const normalized = (keyDeriver as any).normalizeCounterparty('anyone')
expect(normalized.toString()).toBe(anyonePublicKey.toString())
})
test('should normalize counterparty correctly when given as a hex string', () => {
const normalized = (keyDeriver as any).normalizeCounterparty(
counterpartyPublicKey.toString()
)
expect(normalized.toString()).toBe(counterpartyPublicKey.toString())
})
test('should normalize counterparty correctly when given as a public key', () => {
const normalized = (keyDeriver as any).normalizeCounterparty(
counterpartyPublicKey
)
expect(normalized.toString()).toBe(counterpartyPublicKey.toString())
})
test('should allow public key derivation as anyone', () => {
const anyoneDeriver = new KeyDeriver('anyone')
const derivedPublicKey = anyoneDeriver.derivePublicKey(
protocolID,
keyID,
counterpartyPublicKey
)
expect(derivedPublicKey).toBeInstanceOf(PublicKey)
expect(derivedPublicKey.toString()).toEqual(
counterpartyPublicKey
.deriveChild(new PrivateKey(1), '0-testprotocol-12345')
.toString()
)
})
test('should derive the correct public key for counterparty', () => {
const derivedPublicKey = keyDeriver.derivePublicKey(
protocolID,
keyID,
counterpartyPublicKey
)
expect(derivedPublicKey).toBeInstanceOf(PublicKey)
expect(derivedPublicKey.toString()).toEqual(
counterpartyPublicKey
.deriveChild(rootPrivateKey, '0-testprotocol-12345')
.toString()
)
})
test('should derive the correct public key for self', () => {
const derivedPublicKey = keyDeriver.derivePublicKey(
protocolID,
keyID,
counterpartyPublicKey,
true
)
expect(derivedPublicKey).toBeInstanceOf(PublicKey)
expect(derivedPublicKey.toString()).toEqual(
rootPrivateKey
.deriveChild(counterpartyPublicKey, '0-testprotocol-12345')
.toPublicKey()
.toString()
)
})
test('should derive the correct private key', () => {
const derivedPrivateKey = keyDeriver.derivePrivateKey(
protocolID,
keyID,
counterpartyPublicKey
)
expect(derivedPrivateKey).toBeInstanceOf(PrivateKey)
expect(derivedPrivateKey.toString()).toEqual(
rootPrivateKey
.deriveChild(counterpartyPublicKey, '0-testprotocol-12345')
.toString()
)
})
test('should derive the correct symmetric key', () => {
const derivedSymmetricKey = keyDeriver.deriveSymmetricKey(
protocolID,
keyID,
counterpartyPublicKey
)
expect(derivedSymmetricKey).toBeInstanceOf(SymmetricKey)
const priv = rootPrivateKey.deriveChild(
counterpartyPublicKey,
'0-testprotocol-12345'
)
const pub = counterpartyPublicKey.deriveChild(
rootPrivateKey,
'0-testprotocol-12345'
)
expect(derivedSymmetricKey.toHex()).toEqual(
new SymmetricKey(priv.deriveSharedSecret(pub).x?.toArray()).toHex()
)
})
test('should be able to derive symmetric key with anyone', () => {
expect(() =>
keyDeriver.deriveSymmetricKey(protocolID, keyID, 'anyone')
).not.toThrow()
})
test('should reveal the correct counterparty shared secret', () => {
const sharedSecret = keyDeriver.revealCounterpartySecret(
counterpartyPublicKey
)
expect(sharedSecret).toBeInstanceOf(Array)
expect(sharedSecret.length).toBeGreaterThan(0)
expect(sharedSecret).toEqual(
rootPrivateKey.deriveSharedSecret(counterpartyPublicKey).encode(true)
)
})
test('should not reveal shared secret for self', () => {
expect(() => keyDeriver.revealCounterpartySecret('self')).toThrow()
expect(() => keyDeriver.revealCounterpartySecret(rootPublicKey)).toThrow()
})
test('should reveal the specific key association', () => {
const specificSecret = keyDeriver.revealSpecificSecret(
counterpartyPublicKey,
protocolID,
keyID
)
expect(specificSecret).toBeInstanceOf(Array)
expect(specificSecret.length).toBeGreaterThan(0)
const sharedSecret = rootPrivateKey.deriveSharedSecret(
counterpartyPublicKey
)
const invoiceNumberBin = Utils.toArray(
(keyDeriver as any).computeInvoiceNumber(protocolID, keyID),
'utf8'
)
expect(specificSecret).toEqual(
Hash.sha256hmac(sharedSecret.encode(true), invoiceNumberBin)
)
})
test('should throw an error for invalid protocol names', () => {
expect(() =>
(keyDeriver as any).computeInvoiceNumber(
protocolID,
'long' + 'a'.repeat(800)
)
).toThrow()
expect(() =>
(keyDeriver as any).computeInvoiceNumber(protocolID, '')
).toThrow()
expect(() =>
(keyDeriver as any).computeInvoiceNumber([-3, 'otherwise valid'], keyID)
).toThrow()
expect(() =>
(keyDeriver as any).computeInvoiceNumber([2, 'double space'], keyID)
).toThrow()
expect(() =>
(keyDeriver as any).computeInvoiceNumber([0, ''], keyID)
).toThrow()
expect(() =>
(keyDeriver as any).computeInvoiceNumber([0, ' a'], keyID)
).toThrow()
expect(() =>
(keyDeriver as any).computeInvoiceNumber(
[0, 'long' + 'a'.repeat(400)],
keyID
)
).toThrow()
expect(() =>
(keyDeriver as any).computeInvoiceNumber(
[2, 'redundant protocol protocol'],
keyID
)
).toThrow()
expect(() =>
(keyDeriver as any).computeInvoiceNumber([2, 'üñî√é®sål ©0på'], keyID)
).toThrow()
})
})