-
Notifications
You must be signed in to change notification settings - Fork 0
/
KeyTrackerB.ts
128 lines (111 loc) · 4.59 KB
/
KeyTrackerB.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// AKM -> Advanced Key Managagement
import { ethers } from "ethers"
import { BaseKeyTracker, KeyPair, pubFromPri, PubPair, RandPair } from "./Common"
export type CompressedKeyPair = {
secret: string,
pkh: string,
}
export type AdvancedKeyPair = CompressedKeyPair & KeyPair
// FOR EASY READING
const COMBINE = (a: string, b: string) => ethers.utils.solidityPack(['uint256', 'uint256'], [a, b])
const HASH = (a: string) => ethers.utils.keccak256(a)
const GENERATE_INITIAL_SECRET = () => ethers.utils.keccak256(ethers.utils.toUtf8Bytes(ethers.BigNumber.from(ethers.utils.randomBytes(32)).toHexString()))
const dropFirstTwoChars = (a: string) => a.slice(2)
/**
* @name uncompressLamport
* @description Uncompresses a compressed key pair
* @date Febuary 15th 2023
* @author William Doyle
*/
export function uncompressLamport(compressed: CompressedKeyPair): AdvancedKeyPair {
// 1. generate 512 intermediate secrets
const intermediate_secrets: string[] = Array.from({ length: 512 }).map((_, index: number) => HASH(COMBINE(compressed.secret, index.toString())))
// const intermediate_secrets: string[] = Array.from({ length: 512 }).map((_, index: number) => dropFirstTwoChars(HASH(COMBINE(compressed.secret, index.toString()))))
// 2. pair them up
const leftIntermediateSecrets: string[] = intermediate_secrets.filter((_, i) => i % 2 === 0)
const rightIntermediateSecrets: string[] = intermediate_secrets.filter((_, i) => i % 2 === 1)
const pri: RandPair[] = leftIntermediateSecrets.map((l, i) => [l, rightIntermediateSecrets[i]]) as RandPair[]
// 3. derive public key
const pub: PubPair[] = pubFromPri(pri.map(p => [p[0], p[1]]))
// 4. derive hash of public key
const pkh = BaseKeyTracker.pkhFromPublicKey(pub)
// 5. verify hash matches
if (pkh !== compressed.pkh)
throw new Error('Public Key Hash Does Not Match Secret')
// 6. return key pair
return {
...compressed,
pri,
pub
} as AdvancedKeyPair
}
/**
* @name compressLamport
* @description Compresses a key pair to only the secret and the public key hash
* @date Febuary 15th 2023
* @author William Doyle
*/
export function compressLamport(keyPair: AdvancedKeyPair): CompressedKeyPair {
return {
secret: keyPair.secret,
pkh: keyPair.pkh
} as CompressedKeyPair
}
export function mk_compressed_key_pair(): AdvancedKeyPair {
// generate single 32 bytes secret
const secret: string = GENERATE_INITIAL_SECRET()
// derive 512 intermediate secrets
const intermediate_secrets: string[] = Array.from({ length: 512 }).map((_, index: number) => HASH(COMBINE(secret, index.toString())))
// const intermediate_secrets: string[] = Array.from({ length: 512 }).map((_, index: number) => dropFirstTwoChars(HASH(COMBINE(secret, index.toString()))))
// pair them up
const leftIntermediateSecrets: string[] = intermediate_secrets.filter((_, i) => i % 2 === 0)
const rightIntermediateSecrets: string[] = intermediate_secrets.filter((_, i) => i % 2 === 1)
// zip them up
const pri: RandPair[] = leftIntermediateSecrets.map((l, i) => [l, rightIntermediateSecrets[i]]) as RandPair[]
// derive public key
const pub: PubPair[] = pubFromPri(pri.map(p => [p[0], p[1]]))
// derive hash of public key
const pkh = BaseKeyTracker.pkhFromPublicKey(pub)
return {
pri,
pub,
secret,
pkh
} as AdvancedKeyPair
}
/**
* @name KeyTrackerB
* @description A class that keeps track of keys and allows you to get them
* @date Febuary 15th 2023
* @author William Doyle
*/
export default class KeyTrackerB extends BaseKeyTracker {
keys: CompressedKeyPair[] = []
expendedKeys: string[] = []
get count() {
return this.keys.length
}
get exhausted(): boolean {
return this.count === 0
}
more(amount: number = 2): AdvancedKeyPair[] {
const keys = Array.from({ length: amount }, () => mk_compressed_key_pair())
const asCompressed = keys.map(k => compressLamport(k))
this.keys.push(...asCompressed) // save as compressed
return keys // return as uncompressed
}
getOne() {
const returnValue = this.keys.shift()
if (returnValue === undefined)
throw new Error('No keys left')
this.expendedKeys.push(returnValue.pkh)
return uncompressLamport(returnValue)
}
getN(amount: number) {
// return this.keys.splice(0, amount).map(k => uncompressLamport(k))
return this.keys.splice(0, amount).map(k => {
this.expendedKeys.push(k.pkh)
return uncompressLamport(k)
})
}
}