crypto = require 'crypto'
hash = require './hash'
k-Time Signature schemes are digital signature mechanisms where each keypair is used at most k-times. See OTS. Assumes loose time synchronization.
Implemented here is a Merkle-Winternitz Chain, which can sign a fixed number of bits with an extremely low storage overhead and extremely efficient generation, signing, and verification.
crypto = require 'crypto'
hash = require './hash'
Manages the signing of values. The default sizes allow the signing of a SHA1 hash
k
is the number of times the scheme should be valid for. (Number)privateKey
is a private key to use. If one isn’t supplied, a random one
will be generated. (String)class exports.Signer
constructor: (k, privateKey) ->
@heads = []
if privateKey? then @heads.push privateKey
else @heads.push crypto.randomBytes(20).toString 'hex'
until @heads.length is (k + 2)
[vi, head] = [@heads[@heads.length - 1], '']
i = 0 # Create signature branches.
until i is 20
head += hash.chain vi + 's' + (i + 1), 256, 'sha1'
++i
i = 0 # Create checksum branches.
until i is 2
head += hash.chain vi + 'c' + (i + 1), 256, 'sha1'
++i
head = hash.chain head, 1, 'sha1'
@heads.push head
Get the current public key. Should be published.
getPublicKey: -> @heads[@heads.length - 1]
Get the private key. Good for allowing the Signer object to be destroyed without loosing the ability to use this keypair in the future. Returns (k, privateKey), where k is the number of remaining uses and privateKey is the private key.
getPrivateKey: -> [@heads.length - 2, @heads[0]]
Signs a message. Every time this function is called, it counts as one use of the signature scheme.
msg
is the message to be signed. sign: (msg) ->
if not @heads[@heads.length - 3]? then return false
checksum = 0
h = hash.chain msg, 1, 'sha1'
sig = []
i = 0 # Create signature branches.
until i is h.length
n = parseInt(h[i] + h[i + 1], 16) + 1
serial = (i / 2) + 1
vi = hash.chain @heads[@heads.length - 3] + 's' + serial, n, 'sha1'
sig.push vi
checksum += n
i = i + 2
i = 0 # Create checksum branches.
checksum = ('00' + ((256 * 20) - checksum).toString(16)).substr(-3)
until i is 2
n = parseInt(checksum[i], 16) + 1
vi = hash.chain @heads[@heads.length - 3] + 'c' + (i + 1), n, 'sha1'
sig.push vi
++i
@heads.pop()
return sig
class exports.Verifier
constructor: (@publicKey) ->
forward: (msg, sig) ->
h = hash.chain msg, 1, 'sha1'
candPubKey = ''
checksum = 0
i = 0 # Verify signature branches.
until i is h.length
n = parseInt(h[i] + h[i + 1], 16) + 1
candPubKey += hash.chain sig[i / 2], 256 - n, 'sha1'
checksum += n
i = i + 2
i = 0 # Verify checksum branches.
checksum = ('00' + ((256 * 20) - checksum).toString(16)).substr(-3)
until i is 2
n = parseInt(checksum[i], 16) + 1
candPubKey += hash.chain sig[i + 20], 256 - n, 'sha1'
++i
candPubKey = hash.chain candPubKey, 1, 'sha1'
candFinal = ''
i = 0 # Create verification signature branches.
until i is 20
candFinal += hash.chain candPubKey + 's' + (i + 1), 256, 'sha1'
++i
i = 0 # Create verification checksum branches.
until i is 2
candFinal += hash.chain candPubKey + 'c' + (i + 1), 256, 'sha1'
++i
candFinal = hash.chain candFinal, 1, 'sha1'
[candPubKey, candFinal]
Attempt to verify a signature.
msg
is the received message.sig
is the candidate signature provided as authentication. verify: (msg, sig) ->
[candPubKey, candFinal] = @forward msg, sig
if candFinal is @publicKey
@publicKey = candPubKey
return true
else return false