You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
284 lines
7.6 KiB
284 lines
7.6 KiB
4 years ago
|
package bls
|
||
|
|
||
|
import (
|
||
|
"github.com/harmony-one/bls/ffi/go/bls"
|
||
|
lru "github.com/hashicorp/golang-lru"
|
||
|
"github.com/pkg/errors"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
blsPubKeyCacheSize = 1024
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
// BLSPubKeyCache is the Cache of the Deserialized BLS PubKey
|
||
|
BLSPubKeyCache, _ = lru.New(blsPubKeyCacheSize)
|
||
|
)
|
||
|
|
||
|
func init() {
|
||
|
bls.Init(bls.BLS12_381)
|
||
|
}
|
||
|
|
||
|
// RandPrivateKey returns a random private key.
|
||
|
func RandPrivateKey() *bls.SecretKey {
|
||
|
sec := bls.SecretKey{}
|
||
|
sec.SetByCSPRNG()
|
||
|
return &sec
|
||
|
}
|
||
|
|
||
|
var (
|
||
|
errEmptyInput = errors.New("BytesToBLSPublicKey: empty input")
|
||
|
errPubKeyCast = errors.New("BytesToBLSPublicKey: cast error")
|
||
|
)
|
||
|
|
||
|
// BytesToBLSPublicKey converts bytes into bls.PublicKey pointer.
|
||
|
func BytesToBLSPublicKey(bytes []byte) (*bls.PublicKey, error) {
|
||
|
if len(bytes) == 0 {
|
||
|
return nil, errEmptyInput
|
||
|
}
|
||
|
kkey := string(bytes)
|
||
|
if k, ok := BLSPubKeyCache.Get(kkey); ok {
|
||
|
if pk, ok := k.(bls.PublicKey); ok {
|
||
|
return &pk, nil
|
||
|
}
|
||
|
return nil, errPubKeyCast
|
||
|
}
|
||
|
pubKey := &bls.PublicKey{}
|
||
|
err := pubKey.Deserialize(bytes)
|
||
|
|
||
|
if err == nil {
|
||
|
BLSPubKeyCache.Add(kkey, *pubKey)
|
||
|
return pubKey, nil
|
||
|
}
|
||
|
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
// AggregateSig aggregates all the BLS signature into a single multi-signature.
|
||
|
func AggregateSig(sigs []*bls.Sign) *bls.Sign {
|
||
|
var aggregatedSig bls.Sign
|
||
|
for _, sig := range sigs {
|
||
|
aggregatedSig.Add(sig)
|
||
|
}
|
||
|
return &aggregatedSig
|
||
|
}
|
||
|
|
||
|
// Mask represents a cosigning participation bitmask.
|
||
|
type Mask struct {
|
||
|
Bitmap []byte
|
||
|
Publics []*bls.PublicKey
|
||
|
AggregatePublic *bls.PublicKey
|
||
|
}
|
||
|
|
||
|
// NewMask returns a new participation bitmask for cosigning where all
|
||
|
// cosigners are disabled by default. If a public key is given it verifies that
|
||
|
// it is present in the list of keys and sets the corresponding index in the
|
||
|
// bitmask to 1 (enabled).
|
||
|
func NewMask(publics []*bls.PublicKey, myKey *bls.PublicKey) (*Mask, error) {
|
||
|
m := &Mask{
|
||
|
Publics: publics,
|
||
|
}
|
||
|
m.Bitmap = make([]byte, m.Len())
|
||
|
m.AggregatePublic = &bls.PublicKey{}
|
||
|
if myKey != nil {
|
||
|
found := false
|
||
|
for i, key := range publics {
|
||
|
if key.IsEqual(myKey) {
|
||
|
m.SetBit(i, true)
|
||
|
found = true
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
if !found {
|
||
|
return nil, errors.New("key not found")
|
||
|
}
|
||
|
}
|
||
|
return m, nil
|
||
|
}
|
||
|
|
||
|
// Clear clears the existing bits and aggregate public keys.
|
||
|
func (m *Mask) Clear() {
|
||
|
m.Bitmap = make([]byte, m.Len())
|
||
|
m.AggregatePublic = &bls.PublicKey{}
|
||
|
}
|
||
|
|
||
|
// Mask returns a copy of the participation bitmask.
|
||
|
func (m *Mask) Mask() []byte {
|
||
|
clone := make([]byte, len(m.Bitmap))
|
||
|
copy(clone[:], m.Bitmap)
|
||
|
return clone
|
||
|
}
|
||
|
|
||
|
// Len returns the Bitmap length in bytes.
|
||
|
func (m *Mask) Len() int {
|
||
|
return (len(m.Publics) + 7) >> 3
|
||
|
}
|
||
|
|
||
|
// SetMask sets the participation bitmask according to the given byte slice
|
||
|
// interpreted in little-endian order, i.e., bits 0-7 of byte 0 correspond to
|
||
|
// cosigners 0-7, bits 0-7 of byte 1 correspond to cosigners 8-15, etc.
|
||
|
func (m *Mask) SetMask(mask []byte) error {
|
||
|
if m.Len() != len(mask) {
|
||
|
return errors.Errorf(
|
||
|
"mismatching bitmap lengths expectedBitmapLength %d providedBitmapLength %d",
|
||
|
m.Len(),
|
||
|
len(mask),
|
||
|
)
|
||
|
}
|
||
|
for i := range m.Publics {
|
||
|
byt := i >> 3
|
||
|
msk := byte(1) << uint(i&7)
|
||
|
if ((m.Bitmap[byt] & msk) == 0) && ((mask[byt] & msk) != 0) {
|
||
|
m.Bitmap[byt] ^= msk // flip bit in Bitmap from 0 to 1
|
||
|
m.AggregatePublic.Add(m.Publics[i])
|
||
|
}
|
||
|
if ((m.Bitmap[byt] & msk) != 0) && ((mask[byt] & msk) == 0) {
|
||
|
m.Bitmap[byt] ^= msk // flip bit in Bitmap from 1 to 0
|
||
|
m.AggregatePublic.Sub(m.Publics[i])
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// SetBit enables (enable: true) or disables (enable: false) the bit
|
||
|
// in the participation Bitmap of the given cosigner.
|
||
|
func (m *Mask) SetBit(i int, enable bool) error {
|
||
|
if i >= len(m.Publics) {
|
||
|
return errors.New("index out of range")
|
||
|
}
|
||
|
byt := i >> 3
|
||
|
msk := byte(1) << uint(i&7)
|
||
|
if ((m.Bitmap[byt] & msk) == 0) && enable {
|
||
|
m.Bitmap[byt] ^= msk // flip bit in Bitmap from 0 to 1
|
||
|
m.AggregatePublic.Add(m.Publics[i])
|
||
|
}
|
||
|
if ((m.Bitmap[byt] & msk) != 0) && !enable {
|
||
|
m.Bitmap[byt] ^= msk // flip bit in Bitmap from 1 to 0
|
||
|
m.AggregatePublic.Sub(m.Publics[i])
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// GetPubKeyFromMask will return pubkeys which masked either zero or one depending on the flag
|
||
|
// it is used to show which signers are signed or not in the cosign message
|
||
|
func (m *Mask) GetPubKeyFromMask(flag bool) []*bls.PublicKey {
|
||
|
pubKeys := []*bls.PublicKey{}
|
||
|
for i := range m.Publics {
|
||
|
byt := i >> 3
|
||
|
msk := byte(1) << uint(i&7)
|
||
|
if flag {
|
||
|
if (m.Bitmap[byt] & msk) != 0 {
|
||
|
pubKeys = append(pubKeys, m.Publics[i])
|
||
|
}
|
||
|
} else {
|
||
|
if (m.Bitmap[byt] & msk) == 0 {
|
||
|
pubKeys = append(pubKeys, m.Publics[i])
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return pubKeys
|
||
|
}
|
||
|
|
||
|
// IndexEnabled checks whether the given index is enabled in the Bitmap or not.
|
||
|
func (m *Mask) IndexEnabled(i int) (bool, error) {
|
||
|
if i >= len(m.Publics) {
|
||
|
return false, errors.New("index out of range")
|
||
|
}
|
||
|
byt := i >> 3
|
||
|
msk := byte(1) << uint(i&7)
|
||
|
return ((m.Bitmap[byt] & msk) != 0), nil
|
||
|
}
|
||
|
|
||
|
// KeyEnabled checks whether the index, corresponding to the given key, is
|
||
|
// enabled in the Bitmap or not.
|
||
|
func (m *Mask) KeyEnabled(public *bls.PublicKey) (bool, error) {
|
||
|
for i, key := range m.Publics {
|
||
|
if key.IsEqual(public) {
|
||
|
return m.IndexEnabled(i)
|
||
|
}
|
||
|
}
|
||
|
return false, errors.New("key not found")
|
||
|
}
|
||
|
|
||
|
// SetKey set the bit in the Bitmap for the given cosigner
|
||
|
func (m *Mask) SetKey(public *bls.PublicKey, enable bool) error {
|
||
|
for i, key := range m.Publics {
|
||
|
if key.IsEqual(public) {
|
||
|
return m.SetBit(i, enable)
|
||
|
}
|
||
|
}
|
||
|
return errors.New("key not found")
|
||
|
}
|
||
|
|
||
|
// CountEnabled returns the number of enabled nodes in the CoSi participation
|
||
|
// Bitmap.
|
||
|
func (m *Mask) CountEnabled() int {
|
||
|
// hw is hamming weight
|
||
|
hw := 0
|
||
|
for i := range m.Publics {
|
||
|
byt := i >> 3
|
||
|
msk := byte(1) << uint(i&7)
|
||
|
if (m.Bitmap[byt] & msk) != 0 {
|
||
|
hw++
|
||
|
}
|
||
|
}
|
||
|
return hw
|
||
|
}
|
||
|
|
||
|
// CountTotal returns the total number of nodes this CoSi instance knows.
|
||
|
func (m *Mask) CountTotal() int {
|
||
|
return len(m.Publics)
|
||
|
}
|
||
|
|
||
|
// AggregateMasks computes the bitwise OR of the two given participation masks.
|
||
|
func AggregateMasks(a, b []byte) ([]byte, error) {
|
||
|
if len(a) != len(b) {
|
||
|
return nil, errors.New("mismatching Bitmap lengths")
|
||
|
}
|
||
|
m := make([]byte, len(a))
|
||
|
for i := range m {
|
||
|
m[i] = a[i] | b[i]
|
||
|
}
|
||
|
return m, nil
|
||
|
}
|
||
|
|
||
|
// Policy represents a fully customizable cosigning policy deciding what
|
||
|
// cosigner sets are and aren't sufficient for a collective signature to be
|
||
|
// considered acceptable to a verifier. The Check method may inspect the set of
|
||
|
// participants that cosigned by invoking cosi.Mask and/or cosi.MaskBit, and may
|
||
|
// use any other relevant contextual information (e.g., how security-critical
|
||
|
// the operation relying on the collective signature is) in determining whether
|
||
|
// the collective signature was produced by an acceptable set of cosigners.
|
||
|
type Policy interface {
|
||
|
Check(m *Mask) bool
|
||
|
}
|
||
|
|
||
|
// CompletePolicy is the default policy requiring that all participants have
|
||
|
// cosigned to make a collective signature valid.
|
||
|
type CompletePolicy struct {
|
||
|
}
|
||
|
|
||
|
// Check verifies that all participants have contributed to a collective
|
||
|
// signature.
|
||
|
func (p CompletePolicy) Check(m *Mask) bool {
|
||
|
return m.CountEnabled() == m.CountTotal()
|
||
|
}
|
||
|
|
||
|
// ThresholdPolicy allows to specify a simple t-of-n policy requring that at
|
||
|
// least the given threshold number of participants t have cosigned to make a
|
||
|
// collective signature valid.
|
||
|
type ThresholdPolicy struct {
|
||
|
thold int
|
||
|
}
|
||
|
|
||
|
// NewThresholdPolicy returns a new ThresholdPolicy with the given threshold.
|
||
|
func NewThresholdPolicy(thold int) *ThresholdPolicy {
|
||
|
return &ThresholdPolicy{thold: thold}
|
||
|
}
|
||
|
|
||
|
// Check verifies that at least a threshold number of participants have
|
||
|
// contributed to a collective signature.
|
||
|
func (p ThresholdPolicy) Check(m *Mask) bool {
|
||
|
return m.CountEnabled() >= p.thold
|
||
|
}
|