Skip to main content

multisig HMAC

Project description

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()

# generate keys which need to be stored securely and need to be shared securely with each party
k0 = m.keygen(0)
k1 = m.keygen(1)
k2 = m.keygen(2)

# sign by each client
data = b'hello world'

s0 = m.sign(k0, data)
s2 = m.sign(k2, data)

# combine the used signatures
out = m.combine([s0, s2])

sent = (out[0], base64.urlsafe_b64encode(out[1]))

# --- network ---

received = (sent[0], base64.urlsafe_b64decode(sent[1]))

# verify on the server
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()

# generate a master seed which needs to be stored securely
# this seed must NOT be shared with any other party
seed = m.seedgen()

k0 = m.deriveKey(seed, 0)
k1 = m.deriveKey(seed, 1)
k2 = m.deriveKey(seed, 2)

# sign by each client
data = b'hello world'

s0 = m.sign(k0, data)
s2 = m.sign(k2, data)

# combine the used signatures
out = m.combine([s0, s2])

sent = (out[0], base64.urlsafe_b64encode(out[1]))

# --- network ---

received = (sent[0], base64.urlsafe_b64decode(sent[1]))

# verify on the server, but now keys are dynamically derived
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 bytes
  • MultisigHMAC.SHA256_KEYBYTES key length in bytes
  • MultisigHMAC.SHA256_PRIMITIVE is sha256
  • MultisigHMAC.SHA512_BYTES signature length in bytes
  • MultisigHMAC.SHA512_KEYBYTES key length in bytes
  • MultisigHMAC.SHA512_PRIMITIVE is sha512
  • MultisigHMAC.SHA384_BYTES signature length in bytes
  • MultisigHMAC.SHA384_KEYBYTES key length in bytes
  • MultisigHMAC.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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

multisig-hmac-0.2.4.tar.gz (6.1 kB view hashes)

Uploaded Source

Built Distribution

multisig_hmac-0.2.4-py3-none-any.whl (8.5 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page