See #60
This implements keystore primitives for EIP-2333 (draft)
ethereum/EIPs#2333
This will unblock status-im/nim-beacon-chain#1093
cc @zah
Ready:
Note that the spec requires over 16KB of stack for parent_SK_to_Lamport_PK
the lamport_0
and lamport_1
are of size 255 * 32 bytes, in the code we reorder the spec and reuse the same lamport
buffer.
|
# 2. lamport_0 = IKM_to_lamport_SK(IKM, salt) |
|
# TODO: this uses 8KB and has a high stack-overflow potential |
|
var lamport {.noInit.}: array[255, array[32, byte]] |
|
ikm.ikm_to_lamport_SK(salt, lamport) |
|
|
|
# TODO: unclear inclusive/exclusive ranges in spec |
|
# assuming exclusive: |
|
# https://github.com/ethereum/EIPs/issues/2337#issuecomment-637521421 |
|
# 6. for i = 0 to 255 |
|
# lamport_PK = lamport_PK | SHA256(lamport_0[i]) |
|
for i in 0 ..< 255: |
|
ctx.update(sha256.digest(lamport[i]).data) |
|
|
|
# 3. not_IKM = flip_bits(parent_SK) |
|
# We can flip the bit of the IKM instead |
|
# as flipping bits of milagro representation (Montgomery) |
|
# doesn't make sense |
|
var not_ikm {.noInit.}: array[32, byte] |
|
for i in 0 ..< 32: |
|
not_ikm[i] = not ikm[i] |
|
|
|
# 4. lamport_1 = IKM_to_lamport_SK(not_IKM, salt) |
|
# We reuse the previous buffer to limit stack usage |
|
not_ikm.ikm_to_lamport_SK(salt, lamport) |
|
|
|
# TODO: inclusive/exclusive range? |
|
# 7. for i = 0 to 255 |
|
# lamport_PK = lamport_PK | SHA256(lamport_1[i]) |
|
for i in 0 ..< 255: |
|
ctx.update(sha256.digest(lamport[i]).data) |
We could further divide stack usage by 255 (!) if we have an alternative HKDF Expand iterator:
|
let oArray = cast[ptr UncheckedArray[byte]](output) |
|
|
|
for i in 0 .. N: |
|
ctx.init(prk.data) |
|
# T(0) = empty string |
|
if i != 0: |
|
ctx.update(t.data) |
|
ctx.update(info) |
|
ctx.update([uint8(i+1)]) |
|
discard ctx.finish(t.data) |
|
|
|
let iStart = i * HashLen |
|
let size = min(HashLen, output.len - iStart) |
|
copyMem(oArray[iStart].addr, t.data.addr, size) |
Note: the tree might change to 32 instead of 255 as per ethereum/EIPs#2337