multisig-hmac
Multisig scheme for HMAC authentication. Python implementation of multisig-hmac.
Usage
Key management can happen in either of two modes, either by storing every of the component keys, or by storing a single master seed and using that to derive keys ad hoc.
The following two examples return true
when they are executed, for example inside a virtual environment.
Using stored keys:
import multisig_hmac
from multisig_hmac.multisig_hmac import MultisigHMAC
import base64
m = MultisigHMAC()
k0 = m.keygen(0)
k1 = m.keygen(1)
k2 = m.keygen(2)
data = b'hello world'
s0 = m.sign(k0, data)
s2 = m.sign(k2, data)
out = m.combine([s0, s2])
sent = (out[0], base64.urlsafe_b64encode(out[1]))
received = (sent[0], base64.urlsafe_b64decode(sent[1]))
threshold = 2
keys = [k0, k1, k2]
signature = received
print(m.verify(keys, signature, data, threshold))
Using a derived master key:
import multisig_hmac
from multisig_hmac.multisig_hmac import MultisigHMAC
import base64
m = MultisigHMAC()
seed = m.seedgen()
k0 = m.deriveKey(seed, 0)
k1 = m.deriveKey(seed, 1)
k2 = m.deriveKey(seed, 2)
data = b'hello world'
s0 = m.sign(k0, data)
s2 = m.sign(k2, data)
out = m.combine([s0, s2])
sent = (out[0], base64.urlsafe_b64encode(out[1]))
received = (sent[0], base64.urlsafe_b64decode(sent[1]))
threshold = 2
signature = received
print(m.verifyDerived(seed, signature, data, threshold))
API
Constants
MultisigHMAC.BYTES
signature length in bytes (default)MultisigHMAC.KEYBYTES
key length in bytes (default)MultisigHMAC.PRIMITIVE
is sha256
(default)
So far, the implementation supports the following specific algorithms:
MultisigHMAC.SHA256_BYTES
signature length in bytesMultisigHMAC.SHA256_KEYBYTES
key length in bytesMultisigHMAC.SHA256_PRIMITIVE
is sha256
MultisigHMAC.SHA512_BYTES
signature length in bytesMultisigHMAC.SHA512_KEYBYTES
key length in bytesMultisigHMAC.SHA512_PRIMITIVE
is sha512
MultisigHMAC.SHA384_BYTES
signature length in bytesMultisigHMAC.SHA384_KEYBYTES
key length in bytesMultisigHMAC.SHA384_PRIMITIVE
is sha384
n = MultisigHMAC.popcount(bitfield)
Returns the number of keys (i.e. high bits) in bitfield
. bitfield
must be a 32-bit unsigned integer. Example:
assert MultisigHMAC.popcount(5) == 2
xs = MultisigHMAC.keyIndexes(bitfield)
Returns the indexes of the keys (i.e. high bits) in bitfield
as a list. bitfield
must be a 32-bit unsigned integer. Example:
assert MultisigHMAC.keyIndexes(5) == [0,2]
m = MultisigHMAC([alg = MultisigHMAC.PRIMITIVE])
Creates a new instance of MultisigHMAC
which can be used as a global singleton. Just sets the algorithm to be used for subsequent methods and associated constants. Example:
m = MultisigHMAC()
assert (m.popcount(5) == 2 and m.keyIndexes(5) == [0,2])
key = MultisigHMAC.keygen(index)
Generates a new cryptographically random key. The function returns { index: 32-bit unsigned integer, key: bytes of length KEYBYTES }
.
Note: index
should be counted from 0.
masterSeed = MultisigHMAC.seedgen()
Generates a new cryptographically random master seed.
key = MultisigHMAC.deriveKey(masterSeed, index)
Derives a new subkey from a master seed. index
must be a 32-bit unsigned integer, but in practice you want to keep a much lower number, as the bitfield used with the signature has as many bits as the largest index. The function returns { index: 32-bit unsigned integer, key: bytes of length KEYBYTES }
.
Note: index
should be counted from 0.
Keys are derived using a KDF based on HMAC:
b[0...BYTES] = HMAC(Key = masterSeed, data = 'derive' || U32LE(index) || 0x00)
b[BYTES...] = HMAC(Key = masterSeed, b[0...BYTES] || 0x01)
signature = MultisigHMAC.sign(key, data)
Independently signs data
with key
. The function returns { bitfield: 32-bit unsigned integer, signature: bytes of length BYTES }
. This object can be passed to the combine()
function explained below.
signature = MultisigHMAC.combine([signatures...])
Combines a list of signatures which have all been signed independently. Only include each signature once, otherwise they will cancel out. Signatures can be combined in any order. The function returns { bitfield: 32-bit unsigned integer, signature: bytearray of length BYTES }
.
valid = MultisigHMAC.verify(keys, signature, data, threshold)
Verifies a signature
of data
against a list of keys
, over a given threshold
. keys
must be an array of keys. The function returns True
or False
.
valid = MultisigHMAC.verifyDerived(masterSeed, signature, data, threshold)
Verifies a signature
of data
against dynamically derived keys from masterSeed
, over a given threshold
. masterSeed
must be bytes of length KEYBYTES
. The function returns True
or False
.
Installation
$ pip install multisig-hmac
Running tests
$ pip install -U pytest
$ py.test
License
ISC