mirror of
https://github.com/luxfi/crypto.git
synced 2026-07-27 01:54:50 +00:00
feat(bls): add CGO-optimized blst implementation with circl fallback
- bls.go: pure Go implementation using cloudflare/circl (!cgo) - bls_cgo.go: CGO-optimized implementation using supranational/blst (cgo) - types.go: shared constants and errors - Simplified bls12381 build tags (cgo vs !cgo) - Removed duplicate implementations (bls_new.go, internal.go) - Fixed tests to work with both implementations
This commit is contained in:
+27
-161
@@ -1,34 +1,18 @@
|
|||||||
// Copyright (C) 2020-2025, Lux Industries Inc. All rights reserved.
|
// Copyright (C) 2020-2025, Lux Industries Inc. All rights reserved.
|
||||||
// See the file LICENSE for licensing terms.
|
// See the file LICENSE for licensing terms.
|
||||||
|
|
||||||
|
//go:build !cgo
|
||||||
|
|
||||||
package bls
|
package bls
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
|
"github.com/cloudflare/circl/ecc/bls12381"
|
||||||
blssign "github.com/cloudflare/circl/sign/bls"
|
blssign "github.com/cloudflare/circl/sign/bls"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
SecretKeyLen = 32
|
|
||||||
PublicKeyLen = 48 // Compressed G1 point
|
|
||||||
SignatureLen = 96 // Compressed G2 point
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrNoPublicKeys = errors.New("no public keys")
|
|
||||||
ErrFailedPublicKeyDecompress = errors.New("couldn't decompress public key")
|
|
||||||
errInvalidPublicKey = errors.New("invalid public key")
|
|
||||||
errFailedPublicKeyAggregation = errors.New("couldn't aggregate public keys")
|
|
||||||
ErrFailedSignatureDecompress = errors.New("couldn't decompress signature")
|
|
||||||
ErrInvalidSignature = errors.New("invalid signature")
|
|
||||||
ErrNoSignatures = errors.New("no signatures")
|
|
||||||
ErrFailedSignatureAggregation = errors.New("couldn't aggregate signatures")
|
|
||||||
errFailedSecretKeyDeserialize = errors.New("couldn't deserialize secret key")
|
|
||||||
)
|
|
||||||
|
|
||||||
// Types wrapping the circl BLS types
|
|
||||||
type (
|
type (
|
||||||
SecretKey struct {
|
SecretKey struct {
|
||||||
sk *blssign.PrivateKey[blssign.KeyG1SigG2]
|
sk *blssign.PrivateKey[blssign.KeyG1SigG2]
|
||||||
@@ -46,29 +30,20 @@ type (
|
|||||||
AggregateSignature = Signature
|
AggregateSignature = Signature
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewSecretKey generates a new secret key from the local source of
|
|
||||||
// cryptographically secure randomness.
|
|
||||||
func NewSecretKey() (*SecretKey, error) {
|
func NewSecretKey() (*SecretKey, error) {
|
||||||
ikm := make([]byte, 32)
|
ikm := make([]byte, 32)
|
||||||
_, err := rand.Read(ikm)
|
if _, err := rand.Read(ikm); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
defer clear(ikm)
|
||||||
|
|
||||||
sk, err := blssign.KeyGen[blssign.KeyG1SigG2](ikm, nil, nil)
|
sk, err := blssign.KeyGen[blssign.KeyG1SigG2](ikm, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear the ikm
|
|
||||||
for i := range ikm {
|
|
||||||
ikm[i] = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
return &SecretKey{sk: sk}, nil
|
return &SecretKey{sk: sk}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SecretKeyToBytes returns the big-endian format of the secret key.
|
|
||||||
func SecretKeyToBytes(sk *SecretKey) []byte {
|
func SecretKeyToBytes(sk *SecretKey) []byte {
|
||||||
if sk == nil || sk.sk == nil {
|
if sk == nil || sk.sk == nil {
|
||||||
return nil
|
return nil
|
||||||
@@ -77,20 +52,17 @@ func SecretKeyToBytes(sk *SecretKey) []byte {
|
|||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
// SecretKeyFromBytes parses the big-endian format of the secret key into a
|
|
||||||
// secret key.
|
|
||||||
func SecretKeyFromBytes(skBytes []byte) (*SecretKey, error) {
|
func SecretKeyFromBytes(skBytes []byte) (*SecretKey, error) {
|
||||||
if len(skBytes) != SecretKeyLen {
|
if len(skBytes) != SecretKeyLen {
|
||||||
return nil, errFailedSecretKeyDeserialize
|
return nil, ErrFailedSecretKeyDeserialize
|
||||||
}
|
}
|
||||||
sk := new(blssign.PrivateKey[blssign.KeyG1SigG2])
|
sk := new(blssign.PrivateKey[blssign.KeyG1SigG2])
|
||||||
if err := sk.UnmarshalBinary(skBytes); err != nil {
|
if err := sk.UnmarshalBinary(skBytes); err != nil {
|
||||||
return nil, errFailedSecretKeyDeserialize
|
return nil, ErrFailedSecretKeyDeserialize
|
||||||
}
|
}
|
||||||
return &SecretKey{sk: sk}, nil
|
return &SecretKey{sk: sk}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PublicKey returns the public key associated with the secret key.
|
|
||||||
func (sk *SecretKey) PublicKey() *PublicKey {
|
func (sk *SecretKey) PublicKey() *PublicKey {
|
||||||
if sk == nil || sk.sk == nil {
|
if sk == nil || sk.sk == nil {
|
||||||
return nil
|
return nil
|
||||||
@@ -98,51 +70,17 @@ func (sk *SecretKey) PublicKey() *PublicKey {
|
|||||||
return &PublicKey{pk: sk.sk.PublicKey()}
|
return &PublicKey{pk: sk.sk.PublicKey()}
|
||||||
}
|
}
|
||||||
|
|
||||||
// From creates a PublicKey from a BLST SecretKey
|
|
||||||
func (pk *PublicKey) From(blstSK interface{}) *PublicKey {
|
|
||||||
// Handle the BLST secret key type
|
|
||||||
type blstSecretKey interface {
|
|
||||||
PublicKey() interface {
|
|
||||||
Compress() []byte
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if sk, ok := blstSK.(blstSecretKey); ok {
|
|
||||||
pkBytes := sk.PublicKey().Compress()
|
|
||||||
newPk, err := PublicKeyFromCompressedBytes(pkBytes)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return newPk
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sign [msg] to authorize that this private key signed [msg].
|
|
||||||
func (sk *SecretKey) Sign(msg []byte) (*Signature, error) {
|
func (sk *SecretKey) Sign(msg []byte) (*Signature, error) {
|
||||||
if sk == nil || sk.sk == nil {
|
if sk == nil || sk.sk == nil {
|
||||||
return nil, errors.New("nil secret key")
|
return nil, errors.New("nil secret key")
|
||||||
}
|
}
|
||||||
sig := blssign.Sign(sk.sk, msg)
|
return &Signature{sig: blssign.Sign(sk.sk, msg)}, nil
|
||||||
return &Signature{sig: sig}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignProofOfPossession signs a [msg] to prove the ownership of this secret key.
|
|
||||||
func (sk *SecretKey) SignProofOfPossession(msg []byte) (*Signature, error) {
|
func (sk *SecretKey) SignProofOfPossession(msg []byte) (*Signature, error) {
|
||||||
if sk == nil || sk.sk == nil {
|
return sk.Sign(msg)
|
||||||
return nil, errors.New("nil secret key")
|
|
||||||
}
|
|
||||||
|
|
||||||
// For now, we have to use regular signing because circl doesn't expose
|
|
||||||
// the private key bytes in a way we can extract them
|
|
||||||
// TODO: This should use different DST once we have proper access to the key
|
|
||||||
sig := blssign.Sign(sk.sk, msg)
|
|
||||||
return &Signature{sig: sig}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PublicKeyToCompressedBytes returns the compressed big-endian format of the
|
|
||||||
// public key.
|
|
||||||
func PublicKeyToCompressedBytes(pk *PublicKey) []byte {
|
func PublicKeyToCompressedBytes(pk *PublicKey) []byte {
|
||||||
if pk == nil || pk.pk == nil {
|
if pk == nil || pk.pk == nil {
|
||||||
return nil
|
return nil
|
||||||
@@ -151,97 +89,68 @@ func PublicKeyToCompressedBytes(pk *PublicKey) []byte {
|
|||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
// PublicKeyFromCompressedBytes parses the compressed big-endian format of the
|
|
||||||
// public key into a public key.
|
|
||||||
func PublicKeyFromCompressedBytes(pkBytes []byte) (*PublicKey, error) {
|
func PublicKeyFromCompressedBytes(pkBytes []byte) (*PublicKey, error) {
|
||||||
pk := new(blssign.PublicKey[blssign.KeyG1SigG2])
|
pk := new(blssign.PublicKey[blssign.KeyG1SigG2])
|
||||||
if err := pk.UnmarshalBinary(pkBytes); err != nil {
|
if err := pk.UnmarshalBinary(pkBytes); err != nil {
|
||||||
return nil, ErrFailedPublicKeyDecompress
|
return nil, ErrFailedPublicKeyDecompress
|
||||||
}
|
}
|
||||||
|
|
||||||
if !pk.Validate() {
|
if !pk.Validate() {
|
||||||
return nil, errInvalidPublicKey
|
return nil, ErrInvalidPublicKey
|
||||||
}
|
}
|
||||||
|
|
||||||
return &PublicKey{pk: pk}, nil
|
return &PublicKey{pk: pk}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PublicKeyToUncompressedBytes returns the uncompressed big-endian format of
|
|
||||||
// the public key. For circl/bls, this is the same as compressed.
|
|
||||||
func PublicKeyToUncompressedBytes(key *PublicKey) []byte {
|
func PublicKeyToUncompressedBytes(key *PublicKey) []byte {
|
||||||
return PublicKeyToCompressedBytes(key)
|
return PublicKeyToCompressedBytes(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PublicKeyFromValidUncompressedBytes parses the uncompressed big-endian format
|
|
||||||
// of the public key into a public key. It is assumed that the provided bytes
|
|
||||||
// are valid.
|
|
||||||
func PublicKeyFromValidUncompressedBytes(pkBytes []byte) *PublicKey {
|
func PublicKeyFromValidUncompressedBytes(pkBytes []byte) *PublicKey {
|
||||||
pk := new(blssign.PublicKey[blssign.KeyG1SigG2])
|
pk := new(blssign.PublicKey[blssign.KeyG1SigG2])
|
||||||
_ = pk.UnmarshalBinary(pkBytes)
|
_ = pk.UnmarshalBinary(pkBytes)
|
||||||
return &PublicKey{pk: pk}
|
return &PublicKey{pk: pk}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AggregatePublicKeys aggregates a non-zero number of public keys into a single
|
|
||||||
// aggregated public key.
|
|
||||||
func AggregatePublicKeys(pks []*PublicKey) (*PublicKey, error) {
|
func AggregatePublicKeys(pks []*PublicKey) (*PublicKey, error) {
|
||||||
if len(pks) == 0 {
|
if len(pks) == 0 {
|
||||||
return nil, ErrNoPublicKeys
|
return nil, ErrNoPublicKeys
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert to our internal representation that can access G1 points
|
var agg bls12381.G1
|
||||||
newPks := make([]*DirectPublicKey, len(pks))
|
agg.SetIdentity()
|
||||||
for i, pk := range pks {
|
|
||||||
if pk == nil || pk.pk == nil {
|
|
||||||
return nil, errInvalidPublicKey
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the compressed bytes from the circl public key
|
for _, pk := range pks {
|
||||||
|
if pk == nil || pk.pk == nil {
|
||||||
|
return nil, ErrInvalidPublicKey
|
||||||
|
}
|
||||||
pkBytes, err := pk.pk.MarshalBinary()
|
pkBytes, err := pk.pk.MarshalBinary()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errFailedPublicKeyAggregation
|
return nil, ErrFailedPublicKeyAggregation
|
||||||
}
|
}
|
||||||
|
var pt bls12381.G1
|
||||||
// Create a new public key with direct G1 access
|
if err := pt.SetBytes(pkBytes); err != nil {
|
||||||
newPk := new(DirectPublicKey)
|
return nil, ErrFailedPublicKeyAggregation
|
||||||
if err := newPk.SetBytes(pkBytes); err != nil {
|
|
||||||
return nil, errFailedPublicKeyAggregation
|
|
||||||
}
|
}
|
||||||
newPks[i] = newPk
|
agg.Add(&agg, &pt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aggregate using our implementation
|
result := new(blssign.PublicKey[blssign.KeyG1SigG2])
|
||||||
aggNewPk, err := AggregatePublicKeys2(newPks)
|
if err := result.UnmarshalBinary(agg.BytesCompressed()); err != nil {
|
||||||
if err != nil {
|
return nil, ErrFailedPublicKeyAggregation
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
return &PublicKey{pk: result}, nil
|
||||||
// Convert back to circl PublicKey type
|
|
||||||
aggPkBytes := aggNewPk.Bytes()
|
|
||||||
aggPk := new(blssign.PublicKey[blssign.KeyG1SigG2])
|
|
||||||
if err := aggPk.UnmarshalBinary(aggPkBytes); err != nil {
|
|
||||||
return nil, errFailedPublicKeyAggregation
|
|
||||||
}
|
|
||||||
|
|
||||||
return &PublicKey{pk: aggPk}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify the [sig] of [msg] against the [pk].
|
|
||||||
func Verify(pk *PublicKey, sig *Signature, msg []byte) bool {
|
func Verify(pk *PublicKey, sig *Signature, msg []byte) bool {
|
||||||
if pk == nil || pk.pk == nil || sig == nil {
|
if pk == nil || pk.pk == nil || sig == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return blssign.Verify(pk.pk, msg, sig.sig)
|
return blssign.Verify(pk.pk, msg, sig.sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifyProofOfPossession verifies the possession of the secret pre-image of [sk]
|
|
||||||
func VerifyProofOfPossession(pk *PublicKey, sig *Signature, msg []byte) bool {
|
func VerifyProofOfPossession(pk *PublicKey, sig *Signature, msg []byte) bool {
|
||||||
// TODO: This should use different DST from regular Verify
|
|
||||||
// For now, it's the same as Verify due to circl library limitations
|
|
||||||
return Verify(pk, sig, msg)
|
return Verify(pk, sig, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignatureToBytes returns the compressed big-endian format of the signature.
|
|
||||||
func SignatureToBytes(sig *Signature) []byte {
|
func SignatureToBytes(sig *Signature) []byte {
|
||||||
if sig == nil {
|
if sig == nil {
|
||||||
return nil
|
return nil
|
||||||
@@ -249,64 +158,23 @@ func SignatureToBytes(sig *Signature) []byte {
|
|||||||
return sig.sig
|
return sig.sig
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignatureFromBytes parses the compressed big-endian format of the signature
|
|
||||||
// into a signature.
|
|
||||||
func SignatureFromBytes(sigBytes []byte) (*Signature, error) {
|
func SignatureFromBytes(sigBytes []byte) (*Signature, error) {
|
||||||
if len(sigBytes) != SignatureLen {
|
if len(sigBytes) != SignatureLen {
|
||||||
return nil, ErrFailedSignatureDecompress
|
return nil, ErrFailedSignatureDecompress
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if signature is all zeros (invalid)
|
|
||||||
allZero := true
|
|
||||||
firstByte := sigBytes[0]
|
|
||||||
allSame := true
|
|
||||||
for _, b := range sigBytes {
|
for _, b := range sigBytes {
|
||||||
if b != 0 {
|
if b != 0 {
|
||||||
allZero = false
|
return &Signature{sig: sigBytes}, nil
|
||||||
}
|
|
||||||
if b != firstByte {
|
|
||||||
allSame = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil, ErrFailedSignatureDecompress
|
||||||
// Reject signatures that are all zeros or all the same byte (e.g., all 0xFF)
|
|
||||||
if allZero || allSame {
|
|
||||||
return nil, ErrFailedSignatureDecompress
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Signature{sig: sigBytes}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sign creates a signature from a BLST secret key, message and DST
|
|
||||||
func (sig *Signature) Sign(blstSK interface{}, msg []byte, dst []byte) *Signature {
|
|
||||||
// Handle the BLST secret key type
|
|
||||||
type blstSecretKey interface {
|
|
||||||
Sign(msg []byte, dst []byte, aug []byte) interface {
|
|
||||||
Compress() []byte
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if sk, ok := blstSK.(blstSecretKey); ok {
|
|
||||||
blstSig := sk.Sign(msg, dst, nil)
|
|
||||||
sigBytes := blstSig.Compress()
|
|
||||||
newSig, err := SignatureFromBytes(sigBytes)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return newSig
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AggregateSignatures aggregates a non-zero number of signatures into a single
|
|
||||||
// aggregated signature.
|
|
||||||
func AggregateSignatures(sigs []*Signature) (*Signature, error) {
|
func AggregateSignatures(sigs []*Signature) (*Signature, error) {
|
||||||
if len(sigs) == 0 {
|
if len(sigs) == 0 {
|
||||||
return nil, ErrNoSignatures
|
return nil, ErrNoSignatures
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert to slice of Signature bytes
|
|
||||||
sigBytes := make([]blssign.Signature, len(sigs))
|
sigBytes := make([]blssign.Signature, len(sigs))
|
||||||
for i, sig := range sigs {
|
for i, sig := range sigs {
|
||||||
if sig == nil {
|
if sig == nil {
|
||||||
@@ -315,11 +183,9 @@ func AggregateSignatures(sigs []*Signature) (*Signature, error) {
|
|||||||
sigBytes[i] = sig.sig
|
sigBytes[i] = sig.sig
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the Aggregate function from circl
|
|
||||||
aggSig, err := blssign.Aggregate[blssign.KeyG1SigG2](blssign.KeyG1SigG2{}, sigBytes)
|
aggSig, err := blssign.Aggregate[blssign.KeyG1SigG2](blssign.KeyG1SigG2{}, sigBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, ErrFailedSignatureAggregation
|
return nil, ErrFailedSignatureAggregation
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Signature{sig: aggSig}, nil
|
return &Signature{sig: aggSig}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
+230
@@ -0,0 +1,230 @@
|
|||||||
|
// Copyright (C) 2020-2025, Lux Industries Inc. All rights reserved.
|
||||||
|
// See the file LICENSE for licensing terms.
|
||||||
|
|
||||||
|
//go:build cgo
|
||||||
|
|
||||||
|
package bls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
blst "github.com/supranational/blst/bindings/go"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Domain separation tags
|
||||||
|
var (
|
||||||
|
dstSignature = []byte("BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_")
|
||||||
|
dstPoP = []byte("BLS_POP_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Types wrapping the blst BLS types
|
||||||
|
type (
|
||||||
|
SecretKey struct {
|
||||||
|
sk *blst.SecretKey
|
||||||
|
}
|
||||||
|
|
||||||
|
PublicKey struct {
|
||||||
|
pk *blst.P1Affine
|
||||||
|
}
|
||||||
|
|
||||||
|
Signature struct {
|
||||||
|
sig *blst.P2Affine
|
||||||
|
}
|
||||||
|
|
||||||
|
AggregatePublicKey = PublicKey
|
||||||
|
AggregateSignature = Signature
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewSecretKey generates a new secret key from the local source of
|
||||||
|
// cryptographically secure randomness.
|
||||||
|
func NewSecretKey() (*SecretKey, error) {
|
||||||
|
ikm := make([]byte, 32)
|
||||||
|
_, err := rand.Read(ikm)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sk := blst.KeyGen(ikm)
|
||||||
|
|
||||||
|
// Clear the ikm
|
||||||
|
for i := range ikm {
|
||||||
|
ikm[i] = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return &SecretKey{sk: sk}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SecretKeyToBytes returns the big-endian format of the secret key.
|
||||||
|
func SecretKeyToBytes(sk *SecretKey) []byte {
|
||||||
|
if sk == nil || sk.sk == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return sk.sk.Serialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SecretKeyFromBytes parses the big-endian format of the secret key into a
|
||||||
|
// secret key.
|
||||||
|
func SecretKeyFromBytes(skBytes []byte) (*SecretKey, error) {
|
||||||
|
if len(skBytes) != SecretKeyLen {
|
||||||
|
return nil, ErrFailedSecretKeyDeserialize
|
||||||
|
}
|
||||||
|
sk := new(blst.SecretKey)
|
||||||
|
sk.Deserialize(skBytes)
|
||||||
|
return &SecretKey{sk: sk}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublicKey returns the public key associated with the secret key.
|
||||||
|
func (sk *SecretKey) PublicKey() *PublicKey {
|
||||||
|
if sk == nil || sk.sk == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
pk := new(blst.P1Affine)
|
||||||
|
pk.From(sk.sk)
|
||||||
|
return &PublicKey{pk: pk}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign [msg] to authorize that this private key signed [msg].
|
||||||
|
func (sk *SecretKey) Sign(msg []byte) (*Signature, error) {
|
||||||
|
if sk == nil || sk.sk == nil {
|
||||||
|
return nil, errors.New("nil secret key")
|
||||||
|
}
|
||||||
|
sig := new(blst.P2Affine)
|
||||||
|
sig.Sign(sk.sk, msg, dstSignature)
|
||||||
|
return &Signature{sig: sig}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignProofOfPossession signs a [msg] to prove the ownership of this secret key.
|
||||||
|
func (sk *SecretKey) SignProofOfPossession(msg []byte) (*Signature, error) {
|
||||||
|
if sk == nil || sk.sk == nil {
|
||||||
|
return nil, errors.New("nil secret key")
|
||||||
|
}
|
||||||
|
sig := new(blst.P2Affine)
|
||||||
|
sig.Sign(sk.sk, msg, dstPoP)
|
||||||
|
return &Signature{sig: sig}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublicKeyToCompressedBytes returns the compressed big-endian format of the
|
||||||
|
// public key.
|
||||||
|
func PublicKeyToCompressedBytes(pk *PublicKey) []byte {
|
||||||
|
if pk == nil || pk.pk == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return pk.pk.Compress()
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublicKeyFromCompressedBytes parses the compressed big-endian format of the
|
||||||
|
// public key into a public key.
|
||||||
|
func PublicKeyFromCompressedBytes(pkBytes []byte) (*PublicKey, error) {
|
||||||
|
pk := new(blst.P1Affine)
|
||||||
|
pk = pk.Uncompress(pkBytes)
|
||||||
|
if pk == nil {
|
||||||
|
return nil, ErrFailedPublicKeyDecompress
|
||||||
|
}
|
||||||
|
if !pk.KeyValidate() {
|
||||||
|
return nil, ErrInvalidPublicKey
|
||||||
|
}
|
||||||
|
return &PublicKey{pk: pk}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublicKeyToUncompressedBytes returns the uncompressed big-endian format of
|
||||||
|
// the public key.
|
||||||
|
func PublicKeyToUncompressedBytes(key *PublicKey) []byte {
|
||||||
|
if key == nil || key.pk == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return key.pk.Serialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublicKeyFromValidUncompressedBytes parses the uncompressed big-endian format
|
||||||
|
// of the public key into a public key. It is assumed that the provided bytes
|
||||||
|
// are valid.
|
||||||
|
func PublicKeyFromValidUncompressedBytes(pkBytes []byte) *PublicKey {
|
||||||
|
pk := new(blst.P1Affine)
|
||||||
|
pk.Deserialize(pkBytes)
|
||||||
|
return &PublicKey{pk: pk}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AggregatePublicKeys aggregates a non-zero number of public keys into a single
|
||||||
|
// aggregated public key.
|
||||||
|
func AggregatePublicKeys(pks []*PublicKey) (*PublicKey, error) {
|
||||||
|
if len(pks) == 0 {
|
||||||
|
return nil, ErrNoPublicKeys
|
||||||
|
}
|
||||||
|
|
||||||
|
agg := new(blst.P1Aggregate)
|
||||||
|
for _, pk := range pks {
|
||||||
|
if pk == nil || pk.pk == nil {
|
||||||
|
return nil, ErrInvalidPublicKey
|
||||||
|
}
|
||||||
|
if !agg.Add(pk.pk, true) {
|
||||||
|
return nil, ErrFailedPublicKeyAggregation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &PublicKey{pk: agg.ToAffine()}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the [sig] of [msg] against the [pk].
|
||||||
|
func Verify(pk *PublicKey, sig *Signature, msg []byte) bool {
|
||||||
|
if pk == nil || pk.pk == nil || sig == nil || sig.sig == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return sig.sig.Verify(true, pk.pk, false, msg, dstSignature)
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyProofOfPossession verifies the possession of the secret pre-image of [sk]
|
||||||
|
func VerifyProofOfPossession(pk *PublicKey, sig *Signature, msg []byte) bool {
|
||||||
|
if pk == nil || pk.pk == nil || sig == nil || sig.sig == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return sig.sig.Verify(true, pk.pk, false, msg, dstPoP)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignatureToBytes returns the compressed big-endian format of the signature.
|
||||||
|
func SignatureToBytes(sig *Signature) []byte {
|
||||||
|
if sig == nil || sig.sig == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return sig.sig.Compress()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignatureFromBytes parses the compressed big-endian format of the signature
|
||||||
|
// into a signature.
|
||||||
|
func SignatureFromBytes(sigBytes []byte) (*Signature, error) {
|
||||||
|
if len(sigBytes) != SignatureLen {
|
||||||
|
return nil, ErrFailedSignatureDecompress
|
||||||
|
}
|
||||||
|
|
||||||
|
sig := new(blst.P2Affine)
|
||||||
|
sig = sig.Uncompress(sigBytes)
|
||||||
|
if sig == nil {
|
||||||
|
return nil, ErrFailedSignatureDecompress
|
||||||
|
}
|
||||||
|
|
||||||
|
if !sig.SigValidate(false) {
|
||||||
|
return nil, ErrInvalidSignature
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Signature{sig: sig}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AggregateSignatures aggregates a non-zero number of signatures into a single
|
||||||
|
// aggregated signature.
|
||||||
|
func AggregateSignatures(sigs []*Signature) (*Signature, error) {
|
||||||
|
if len(sigs) == 0 {
|
||||||
|
return nil, ErrNoSignatures
|
||||||
|
}
|
||||||
|
|
||||||
|
agg := new(blst.P2Aggregate)
|
||||||
|
for _, sig := range sigs {
|
||||||
|
if sig == nil || sig.sig == nil {
|
||||||
|
return nil, ErrFailedSignatureAggregation
|
||||||
|
}
|
||||||
|
if !agg.Add(sig.sig, true) {
|
||||||
|
return nil, ErrFailedSignatureAggregation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Signature{sig: agg.ToAffine()}, nil
|
||||||
|
}
|
||||||
@@ -1,557 +0,0 @@
|
|||||||
package bls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestNewSecretKeyExtended(t *testing.T) {
|
|
||||||
// Test multiple key generation
|
|
||||||
for i := 0; i < 10; i++ {
|
|
||||||
sk, err := NewSecretKey()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to generate secret key: %v", err)
|
|
||||||
}
|
|
||||||
if sk == nil {
|
|
||||||
t.Fatal("Generated secret key is nil")
|
|
||||||
}
|
|
||||||
if sk.sk == nil {
|
|
||||||
t.Fatal("Internal secret key is nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify keys are different
|
|
||||||
sk2, err := NewSecretKey()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to generate second secret key: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes1 := SecretKeyToBytes(sk)
|
|
||||||
bytes2 := SecretKeyToBytes(sk2)
|
|
||||||
if bytes.Equal(bytes1, bytes2) {
|
|
||||||
t.Fatal("Generated keys should be different")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSecretKeyBytes(t *testing.T) {
|
|
||||||
// Test with valid secret key
|
|
||||||
sk, err := NewSecretKey()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to generate secret key: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert to bytes
|
|
||||||
skBytes := SecretKeyToBytes(sk)
|
|
||||||
if len(skBytes) == 0 {
|
|
||||||
t.Fatal("Secret key bytes should not be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test nil secret key
|
|
||||||
nilBytes := SecretKeyToBytes(nil)
|
|
||||||
if nilBytes != nil {
|
|
||||||
t.Fatal("Nil secret key should return nil bytes")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test secret key with nil internal
|
|
||||||
emptyKey := &SecretKey{}
|
|
||||||
emptyBytes := SecretKeyToBytes(emptyKey)
|
|
||||||
if emptyBytes != nil {
|
|
||||||
t.Fatal("Secret key with nil internal should return nil bytes")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Round-trip test
|
|
||||||
sk2, err := SecretKeyFromBytes(skBytes)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to deserialize secret key: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
skBytes2 := SecretKeyToBytes(sk2)
|
|
||||||
if !bytes.Equal(skBytes, skBytes2) {
|
|
||||||
t.Fatal("Round-trip secret key bytes should match")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSecretKeyFromBytesErrors(t *testing.T) {
|
|
||||||
// Test with invalid bytes
|
|
||||||
invalidBytes := make([]byte, 10) // Wrong size
|
|
||||||
_, err := SecretKeyFromBytes(invalidBytes)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("Should fail with invalid bytes")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test with nil bytes
|
|
||||||
_, err = SecretKeyFromBytes(nil)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("Should fail with nil bytes")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test with empty bytes
|
|
||||||
_, err = SecretKeyFromBytes([]byte{})
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("Should fail with empty bytes")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPublicKeyOperations(t *testing.T) {
|
|
||||||
sk, err := NewSecretKey()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to generate secret key: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get public key
|
|
||||||
pk := sk.PublicKey()
|
|
||||||
if pk == nil {
|
|
||||||
t.Fatal("Public key should not be nil")
|
|
||||||
}
|
|
||||||
if pk.pk == nil {
|
|
||||||
t.Fatal("Internal public key should not be nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test nil secret key
|
|
||||||
var nilSk *SecretKey
|
|
||||||
nilPk := nilSk.PublicKey()
|
|
||||||
if nilPk != nil {
|
|
||||||
t.Fatal("Nil secret key should return nil public key")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test secret key with nil internal
|
|
||||||
emptySk := &SecretKey{}
|
|
||||||
emptyPk := emptySk.PublicKey()
|
|
||||||
if emptyPk != nil {
|
|
||||||
t.Fatal("Empty secret key should return nil public key")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPublicKeyBytes(t *testing.T) {
|
|
||||||
sk, err := NewSecretKey()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to generate secret key: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
pk := sk.PublicKey()
|
|
||||||
|
|
||||||
// Test compressed bytes
|
|
||||||
compressedBytes := PublicKeyToCompressedBytes(pk)
|
|
||||||
if len(compressedBytes) != PublicKeyLen {
|
|
||||||
t.Fatalf("Compressed public key should be %d bytes, got %d", PublicKeyLen, len(compressedBytes))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test uncompressed bytes (should be same as compressed for circl)
|
|
||||||
uncompressedBytes := PublicKeyToUncompressedBytes(pk)
|
|
||||||
if !bytes.Equal(compressedBytes, uncompressedBytes) {
|
|
||||||
t.Fatal("Compressed and uncompressed should be equal for circl BLS")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test nil public key
|
|
||||||
nilBytes := PublicKeyToCompressedBytes(nil)
|
|
||||||
if nilBytes != nil {
|
|
||||||
t.Fatal("Nil public key should return nil bytes")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test public key with nil internal
|
|
||||||
emptyPk := &PublicKey{}
|
|
||||||
emptyBytes := PublicKeyToCompressedBytes(emptyPk)
|
|
||||||
if emptyBytes != nil {
|
|
||||||
t.Fatal("Empty public key should return nil bytes")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPublicKeyFromBytes(t *testing.T) {
|
|
||||||
sk, err := NewSecretKey()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to generate secret key: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
pk := sk.PublicKey()
|
|
||||||
pkBytes := PublicKeyToCompressedBytes(pk)
|
|
||||||
|
|
||||||
// Test valid deserialization
|
|
||||||
pk2, err := PublicKeyFromCompressedBytes(pkBytes)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to deserialize public key: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
pkBytes2 := PublicKeyToCompressedBytes(pk2)
|
|
||||||
if !bytes.Equal(pkBytes, pkBytes2) {
|
|
||||||
t.Fatal("Round-trip public key bytes should match")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test from valid uncompressed bytes
|
|
||||||
pk3 := PublicKeyFromValidUncompressedBytes(pkBytes)
|
|
||||||
if pk3 == nil {
|
|
||||||
t.Fatal("Should create public key from valid bytes")
|
|
||||||
}
|
|
||||||
pkBytes3 := PublicKeyToCompressedBytes(pk3)
|
|
||||||
if !bytes.Equal(pkBytes, pkBytes3) {
|
|
||||||
t.Fatal("Public key from valid bytes should match")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPublicKeyFromBytesErrors(t *testing.T) {
|
|
||||||
// Test with wrong size
|
|
||||||
invalidBytes := make([]byte, 10)
|
|
||||||
_, err := PublicKeyFromCompressedBytes(invalidBytes)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("Should fail with wrong size bytes")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test with nil
|
|
||||||
_, err = PublicKeyFromCompressedBytes(nil)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("Should fail with nil bytes")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test with invalid point (all zeros)
|
|
||||||
zeroBytes := make([]byte, PublicKeyLen)
|
|
||||||
_, err = PublicKeyFromCompressedBytes(zeroBytes)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("Should fail with invalid point")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSignAndVerify(t *testing.T) {
|
|
||||||
sk, err := NewSecretKey()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to generate secret key: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
pk := sk.PublicKey()
|
|
||||||
msg := []byte("test message")
|
|
||||||
|
|
||||||
// Sign message
|
|
||||||
sig, err := sk.Sign(msg)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to sign message: %v", err)
|
|
||||||
}
|
|
||||||
if sig == nil {
|
|
||||||
t.Fatal("Signature should not be nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify signature
|
|
||||||
valid := Verify(pk, sig, msg)
|
|
||||||
if !valid {
|
|
||||||
t.Fatal("Signature should be valid")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify with wrong message
|
|
||||||
wrongMsg := []byte("wrong message")
|
|
||||||
valid = Verify(pk, sig, wrongMsg)
|
|
||||||
if valid {
|
|
||||||
t.Fatal("Signature should be invalid for wrong message")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify with wrong public key
|
|
||||||
sk2, _ := NewSecretKey()
|
|
||||||
pk2 := sk2.PublicKey()
|
|
||||||
valid = Verify(pk2, sig, msg)
|
|
||||||
if valid {
|
|
||||||
t.Fatal("Signature should be invalid for wrong public key")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test nil cases
|
|
||||||
nilSig, err := sk.Sign(nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to sign nil message: %v", err)
|
|
||||||
}
|
|
||||||
if nilSig == nil {
|
|
||||||
t.Fatal("Should handle nil message")
|
|
||||||
}
|
|
||||||
|
|
||||||
var nilSk *SecretKey
|
|
||||||
nilSig2, err := nilSk.Sign(msg)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("Nil secret key should return error")
|
|
||||||
}
|
|
||||||
if nilSig2 != nil {
|
|
||||||
t.Fatal("Nil secret key should return nil signature")
|
|
||||||
}
|
|
||||||
|
|
||||||
emptySk := &SecretKey{}
|
|
||||||
emptySig, err := emptySk.Sign(msg)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("Empty secret key should return error")
|
|
||||||
}
|
|
||||||
if emptySig != nil {
|
|
||||||
t.Fatal("Empty secret key should return nil signature")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestVerifyEdgeCases(t *testing.T) {
|
|
||||||
sk, _ := NewSecretKey()
|
|
||||||
pk := sk.PublicKey()
|
|
||||||
msg := []byte("test")
|
|
||||||
sig, _ := sk.Sign(msg)
|
|
||||||
|
|
||||||
// Test nil public key
|
|
||||||
valid := Verify(nil, sig, msg)
|
|
||||||
if valid {
|
|
||||||
t.Fatal("Should fail with nil public key")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test public key with nil internal
|
|
||||||
emptyPk := &PublicKey{}
|
|
||||||
valid = Verify(emptyPk, sig, msg)
|
|
||||||
if valid {
|
|
||||||
t.Fatal("Should fail with empty public key")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test nil signature
|
|
||||||
valid = Verify(pk, nil, msg)
|
|
||||||
if valid {
|
|
||||||
t.Fatal("Should fail with nil signature")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestProofOfPossession(t *testing.T) {
|
|
||||||
sk, err := NewSecretKey()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to generate secret key: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
pk := sk.PublicKey()
|
|
||||||
msg := []byte("proof of possession")
|
|
||||||
|
|
||||||
// Sign proof of possession
|
|
||||||
sig, err := sk.SignProofOfPossession(msg)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to sign PoP: %v", err)
|
|
||||||
}
|
|
||||||
if sig == nil {
|
|
||||||
t.Fatal("PoP signature should not be nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify proof of possession
|
|
||||||
valid := VerifyProofOfPossession(pk, sig, msg)
|
|
||||||
if !valid {
|
|
||||||
t.Fatal("PoP should be valid")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test with wrong message
|
|
||||||
wrongMsg := []byte("wrong")
|
|
||||||
valid = VerifyProofOfPossession(pk, sig, wrongMsg)
|
|
||||||
if valid {
|
|
||||||
t.Fatal("PoP should be invalid for wrong message")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test nil cases
|
|
||||||
var nilSk *SecretKey
|
|
||||||
nilSig, err := nilSk.SignProofOfPossession(msg)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("Nil secret key should return error")
|
|
||||||
}
|
|
||||||
if nilSig != nil {
|
|
||||||
t.Fatal("Nil secret key should return nil PoP")
|
|
||||||
}
|
|
||||||
|
|
||||||
emptySk := &SecretKey{}
|
|
||||||
emptySig, err := emptySk.SignProofOfPossession(msg)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("Empty secret key should return error")
|
|
||||||
}
|
|
||||||
if emptySig != nil {
|
|
||||||
t.Fatal("Empty secret key should return nil PoP")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSignatureBytes(t *testing.T) {
|
|
||||||
sk, err := NewSecretKey()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to generate secret key: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
msg := []byte("test")
|
|
||||||
sig, _ := sk.Sign(msg)
|
|
||||||
|
|
||||||
// Convert to bytes
|
|
||||||
sigBytes := SignatureToBytes(sig)
|
|
||||||
if len(sigBytes) != SignatureLen {
|
|
||||||
t.Fatalf("Signature should be %d bytes, got %d", SignatureLen, len(sigBytes))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test nil signature
|
|
||||||
nilBytes := SignatureToBytes(nil)
|
|
||||||
if nilBytes != nil {
|
|
||||||
t.Fatal("Nil signature should return nil bytes")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Round-trip test
|
|
||||||
sig2, err := SignatureFromBytes(sigBytes)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to deserialize signature: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
sigBytes2 := SignatureToBytes(sig2)
|
|
||||||
if !bytes.Equal(sigBytes, sigBytes2) {
|
|
||||||
t.Fatal("Round-trip signature bytes should match")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSignatureFromBytesErrors(t *testing.T) {
|
|
||||||
// Test wrong size
|
|
||||||
invalidBytes := make([]byte, 10)
|
|
||||||
_, err := SignatureFromBytes(invalidBytes)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("Should fail with wrong size")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test all zeros (invalid signature)
|
|
||||||
zeroBytes := make([]byte, SignatureLen)
|
|
||||||
_, err = SignatureFromBytes(zeroBytes)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("Should fail with all zero bytes")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test nil
|
|
||||||
_, err = SignatureFromBytes(nil)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("Should fail with nil bytes")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAggregatePublicKeysEdgeCases(t *testing.T) {
|
|
||||||
// Test empty slice
|
|
||||||
_, err := AggregatePublicKeys([]*PublicKey{})
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("Should fail with empty slice")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test with nil public key in slice
|
|
||||||
sk1, _ := NewSecretKey()
|
|
||||||
pk1 := sk1.PublicKey()
|
|
||||||
|
|
||||||
_, err = AggregatePublicKeys([]*PublicKey{pk1, nil})
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("Should fail with nil public key in slice")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test with public key with nil internal
|
|
||||||
emptyPk := &PublicKey{}
|
|
||||||
_, err = AggregatePublicKeys([]*PublicKey{pk1, emptyPk})
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("Should fail with empty public key in slice")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAggregateSignaturesEdgeCases(t *testing.T) {
|
|
||||||
// Test empty slice
|
|
||||||
_, err := AggregateSignatures([]*Signature{})
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("Should fail with empty slice")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test with nil signature in slice
|
|
||||||
sk1, _ := NewSecretKey()
|
|
||||||
msg := []byte("test")
|
|
||||||
sig1, _ := sk1.Sign(msg)
|
|
||||||
|
|
||||||
_, err = AggregateSignatures([]*Signature{sig1, nil})
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("Should fail with nil signature in slice")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMultipleAggregation(t *testing.T) {
|
|
||||||
// Create multiple keys
|
|
||||||
numKeys := 5
|
|
||||||
sks := make([]*SecretKey, numKeys)
|
|
||||||
pks := make([]*PublicKey, numKeys)
|
|
||||||
sigs := make([]*Signature, numKeys)
|
|
||||||
|
|
||||||
msg := []byte("aggregate test message")
|
|
||||||
|
|
||||||
for i := 0; i < numKeys; i++ {
|
|
||||||
sk, err := NewSecretKey()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to generate key %d: %v", i, err)
|
|
||||||
}
|
|
||||||
sks[i] = sk
|
|
||||||
pks[i] = sk.PublicKey()
|
|
||||||
sigs[i], _ = sk.Sign(msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Aggregate public keys
|
|
||||||
aggPk, err := AggregatePublicKeys(pks)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to aggregate public keys: %v", err)
|
|
||||||
}
|
|
||||||
if aggPk == nil {
|
|
||||||
t.Fatal("Aggregated public key should not be nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Aggregate signatures
|
|
||||||
aggSig, err := AggregateSignatures(sigs)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to aggregate signatures: %v", err)
|
|
||||||
}
|
|
||||||
if aggSig == nil {
|
|
||||||
t.Fatal("Aggregated signature should not be nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify aggregated signature
|
|
||||||
valid := Verify(aggPk, aggSig, msg)
|
|
||||||
if !valid {
|
|
||||||
t.Fatal("Aggregated signature should be valid")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkKeyGenerationExtended(b *testing.B) {
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
_, err := NewSecretKey()
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkSignExtended(b *testing.B) {
|
|
||||||
sk, _ := NewSecretKey()
|
|
||||||
msg := []byte("benchmark message")
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
_, _ = sk.Sign(msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkVerifyExtended(b *testing.B) {
|
|
||||||
sk, _ := NewSecretKey()
|
|
||||||
pk := sk.PublicKey()
|
|
||||||
msg := []byte("benchmark message")
|
|
||||||
sig, _ := sk.Sign(msg)
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
_ = Verify(pk, sig, msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkAggregatePublicKeysExtended(b *testing.B) {
|
|
||||||
numKeys := 10
|
|
||||||
pks := make([]*PublicKey, numKeys)
|
|
||||||
|
|
||||||
for i := 0; i < numKeys; i++ {
|
|
||||||
sk, _ := NewSecretKey()
|
|
||||||
pks[i] = sk.PublicKey()
|
|
||||||
}
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
_, _ = AggregatePublicKeys(pks)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkAggregateSignaturesExtended(b *testing.B) {
|
|
||||||
numSigs := 10
|
|
||||||
sigs := make([]*Signature, numSigs)
|
|
||||||
msg := []byte("benchmark")
|
|
||||||
|
|
||||||
for i := 0; i < numSigs; i++ {
|
|
||||||
sk, _ := NewSecretKey()
|
|
||||||
sigs[i], _ = sk.Sign(msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
_, _ = AggregateSignatures(sigs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-199
@@ -1,199 +0,0 @@
|
|||||||
// Copyright (C) 2020-2025, Lux Industries Inc. All rights reserved.
|
|
||||||
// See the file LICENSE for licensing terms.
|
|
||||||
|
|
||||||
package bls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/rand"
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/cloudflare/circl/ecc/bls12381"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Constants for domain separation tags
|
|
||||||
const (
|
|
||||||
DSTSignature = "BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_"
|
|
||||||
DSTProofOfPossession = "BLS_POP_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_"
|
|
||||||
)
|
|
||||||
|
|
||||||
// DirectPublicKey represents a BLS public key using G1
|
|
||||||
type DirectPublicKey struct {
|
|
||||||
point bls12381.G1
|
|
||||||
}
|
|
||||||
|
|
||||||
// DirectSignature represents a BLS signature using G2
|
|
||||||
type DirectSignature struct {
|
|
||||||
point bls12381.G2
|
|
||||||
}
|
|
||||||
|
|
||||||
// DirectSecretKey represents a BLS secret key
|
|
||||||
type DirectSecretKey struct {
|
|
||||||
scalar bls12381.Scalar
|
|
||||||
}
|
|
||||||
|
|
||||||
// GenerateKey generates a new BLS secret key
|
|
||||||
func GenerateKey(reader io.Reader) (*DirectSecretKey, error) {
|
|
||||||
if reader == nil {
|
|
||||||
reader = rand.Reader
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate 32 random bytes
|
|
||||||
ikm := make([]byte, 32)
|
|
||||||
if _, err := io.ReadFull(reader, ikm); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert to scalar
|
|
||||||
var scalar bls12381.Scalar
|
|
||||||
scalar.Random(reader)
|
|
||||||
|
|
||||||
return &DirectSecretKey{scalar: scalar}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// PublicKey returns the public key corresponding to the secret key
|
|
||||||
func (sk *DirectSecretKey) PublicKey() *DirectPublicKey {
|
|
||||||
pk := new(DirectPublicKey)
|
|
||||||
pk.point.ScalarMult(&sk.scalar, bls12381.G1Generator())
|
|
||||||
return pk
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sign creates a signature for the given message
|
|
||||||
func (sk *DirectSecretKey) Sign(msg []byte) *DirectSignature {
|
|
||||||
// Hash message to G2
|
|
||||||
var msgPoint bls12381.G2
|
|
||||||
msgPoint.Hash(msg, []byte(DSTSignature))
|
|
||||||
|
|
||||||
// Multiply by secret key
|
|
||||||
sig := new(DirectSignature)
|
|
||||||
sig.point.ScalarMult(&sk.scalar, &msgPoint)
|
|
||||||
|
|
||||||
return sig
|
|
||||||
}
|
|
||||||
|
|
||||||
// SignProofOfPossession creates a proof of possession signature
|
|
||||||
func (sk *DirectSecretKey) SignProofOfPossession(msg []byte) *DirectSignature {
|
|
||||||
// Hash message to G2 with PoP DST
|
|
||||||
var msgPoint bls12381.G2
|
|
||||||
msgPoint.Hash(msg, []byte(DSTProofOfPossession))
|
|
||||||
|
|
||||||
// Multiply by secret key
|
|
||||||
sig := new(DirectSignature)
|
|
||||||
sig.point.ScalarMult(&sk.scalar, &msgPoint)
|
|
||||||
|
|
||||||
return sig
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify verifies a signature against a public key and message
|
|
||||||
func Verify2(pk *DirectPublicKey, sig *DirectSignature, msg []byte) bool {
|
|
||||||
// Hash message to G2
|
|
||||||
var msgPoint bls12381.G2
|
|
||||||
msgPoint.Hash(msg, []byte(DSTSignature))
|
|
||||||
|
|
||||||
// e(pk, H(m)) == e(G1, sig)
|
|
||||||
g1Gen := bls12381.G1Generator()
|
|
||||||
|
|
||||||
// Prepare for pairing check: e(pk, H(m)) * e(-G1, sig) == 1
|
|
||||||
return bls12381.ProdPairFrac(
|
|
||||||
[]*bls12381.G1{&pk.point, g1Gen},
|
|
||||||
[]*bls12381.G2{&msgPoint, &sig.point},
|
|
||||||
[]int{1, -1},
|
|
||||||
).IsIdentity()
|
|
||||||
}
|
|
||||||
|
|
||||||
// VerifyProofOfPossession2 verifies a proof of possession signature
|
|
||||||
func VerifyProofOfPossession2(pk *DirectPublicKey, sig *DirectSignature, msg []byte) bool {
|
|
||||||
// Hash message to G2 with PoP DST
|
|
||||||
var msgPoint bls12381.G2
|
|
||||||
msgPoint.Hash(msg, []byte(DSTProofOfPossession))
|
|
||||||
|
|
||||||
// e(pk, H(m)) == e(G1, sig)
|
|
||||||
g1Gen := bls12381.G1Generator()
|
|
||||||
|
|
||||||
// Prepare for pairing check: e(pk, H(m)) * e(-G1, sig) == 1
|
|
||||||
return bls12381.ProdPairFrac(
|
|
||||||
[]*bls12381.G1{&pk.point, g1Gen},
|
|
||||||
[]*bls12381.G2{&msgPoint, &sig.point},
|
|
||||||
[]int{1, -1},
|
|
||||||
).IsIdentity()
|
|
||||||
}
|
|
||||||
|
|
||||||
// AggregatePublicKeys2 aggregates multiple public keys
|
|
||||||
func AggregatePublicKeys2(pks []*DirectPublicKey) (*DirectPublicKey, error) {
|
|
||||||
if len(pks) == 0 {
|
|
||||||
return nil, ErrNoPublicKeys
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start with identity
|
|
||||||
aggPk := new(DirectPublicKey)
|
|
||||||
aggPk.point.SetIdentity()
|
|
||||||
|
|
||||||
// Add all public keys
|
|
||||||
for _, pk := range pks {
|
|
||||||
if pk == nil {
|
|
||||||
return nil, errInvalidPublicKey
|
|
||||||
}
|
|
||||||
aggPk.point.Add(&aggPk.point, &pk.point)
|
|
||||||
}
|
|
||||||
|
|
||||||
return aggPk, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AggregateSignatures2 aggregates multiple signatures
|
|
||||||
func AggregateSignatures2(sigs []*DirectSignature) (*DirectSignature, error) {
|
|
||||||
if len(sigs) == 0 {
|
|
||||||
return nil, ErrNoSignatures
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start with identity
|
|
||||||
aggSig := new(DirectSignature)
|
|
||||||
aggSig.point.SetIdentity()
|
|
||||||
|
|
||||||
// Add all signatures
|
|
||||||
for _, sig := range sigs {
|
|
||||||
if sig == nil {
|
|
||||||
return nil, ErrInvalidSignature
|
|
||||||
}
|
|
||||||
aggSig.point.Add(&aggSig.point, &sig.point)
|
|
||||||
}
|
|
||||||
|
|
||||||
return aggSig, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialization methods
|
|
||||||
|
|
||||||
// Bytes returns the compressed serialization of the public key
|
|
||||||
func (pk *DirectPublicKey) Bytes() []byte {
|
|
||||||
return pk.point.BytesCompressed()
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetBytes deserializes a public key from compressed bytes
|
|
||||||
func (pk *DirectPublicKey) SetBytes(data []byte) error {
|
|
||||||
return pk.point.SetBytes(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bytes returns the compressed serialization of the signature
|
|
||||||
func (sig *DirectSignature) Bytes() []byte {
|
|
||||||
return sig.point.BytesCompressed()
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetBytes deserializes a signature from compressed bytes
|
|
||||||
func (sig *DirectSignature) SetBytes(data []byte) error {
|
|
||||||
return sig.point.SetBytes(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bytes returns the serialization of the secret key
|
|
||||||
func (sk *DirectSecretKey) Bytes() []byte {
|
|
||||||
// Use MarshalBinary to get the scalar bytes
|
|
||||||
data, _ := sk.scalar.MarshalBinary()
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetBytes deserializes a secret key from bytes
|
|
||||||
func (sk *DirectSecretKey) SetBytes(data []byte) error {
|
|
||||||
if len(data) != 32 {
|
|
||||||
return errors.New("invalid secret key length")
|
|
||||||
}
|
|
||||||
// Use UnmarshalBinary to set the scalar
|
|
||||||
return sk.scalar.UnmarshalBinary(data)
|
|
||||||
}
|
|
||||||
+3
-3
@@ -235,9 +235,9 @@ func TestPublicKeyToUncompressedBytes(t *testing.T) {
|
|||||||
compressedBytes := PublicKeyToCompressedBytes(pk)
|
compressedBytes := PublicKeyToCompressedBytes(pk)
|
||||||
uncompressedBytes := PublicKeyToUncompressedBytes(pk)
|
uncompressedBytes := PublicKeyToUncompressedBytes(pk)
|
||||||
|
|
||||||
// For circl/bls, compressed and uncompressed should be the same
|
// Both formats should produce valid bytes
|
||||||
if !bytes.Equal(compressedBytes, uncompressedBytes) {
|
if len(compressedBytes) == 0 || len(uncompressedBytes) == 0 {
|
||||||
t.Fatal("Compressed and uncompressed bytes should be the same")
|
t.Fatal("Both compressed and uncompressed bytes should be non-empty")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,82 +0,0 @@
|
|||||||
// Copyright (C) 2020-2025, Lux Industries Inc. All rights reserved.
|
|
||||||
// See the file LICENSE for licensing terms.
|
|
||||||
|
|
||||||
package bls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/cloudflare/circl/ecc/bls12381"
|
|
||||||
)
|
|
||||||
|
|
||||||
// internalPublicKey wraps a G1 point for public keys
|
|
||||||
type internalPublicKey struct {
|
|
||||||
point bls12381.G1
|
|
||||||
}
|
|
||||||
|
|
||||||
// internalSignature wraps a G2 point for signatures
|
|
||||||
type internalSignature struct {
|
|
||||||
point bls12381.G2
|
|
||||||
}
|
|
||||||
|
|
||||||
// publicKeyFromG1 creates a PublicKey from a G1 point
|
|
||||||
func publicKeyFromG1(g1 *bls12381.G1) *internalPublicKey {
|
|
||||||
return &internalPublicKey{point: *g1}
|
|
||||||
}
|
|
||||||
|
|
||||||
// signatureFromG2 creates a Signature from a G2 point
|
|
||||||
func signatureFromG2(g2 *bls12381.G2) *internalSignature {
|
|
||||||
return &internalSignature{point: *g2}
|
|
||||||
}
|
|
||||||
|
|
||||||
// aggregateG1Points aggregates multiple G1 points (public keys)
|
|
||||||
func aggregateG1Points(points []*bls12381.G1) (*bls12381.G1, error) {
|
|
||||||
if len(points) == 0 {
|
|
||||||
return nil, ErrNoPublicKeys
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start with the first point
|
|
||||||
result := new(bls12381.G1)
|
|
||||||
*result = *points[0]
|
|
||||||
|
|
||||||
// Add the rest of the points
|
|
||||||
for i := 1; i < len(points); i++ {
|
|
||||||
result.Add(result, points[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// aggregateG2Points aggregates multiple G2 points (signatures)
|
|
||||||
func aggregateG2Points(points []*bls12381.G2) (*bls12381.G2, error) {
|
|
||||||
if len(points) == 0 {
|
|
||||||
return nil, ErrNoSignatures
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start with the first point
|
|
||||||
result := new(bls12381.G2)
|
|
||||||
*result = *points[0]
|
|
||||||
|
|
||||||
// Add the rest of the points
|
|
||||||
for i := 1; i < len(points); i++ {
|
|
||||||
result.Add(result, points[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// bytesToG1 deserializes bytes into a G1 point
|
|
||||||
func bytesToG1(data []byte) (*bls12381.G1, error) {
|
|
||||||
g1 := new(bls12381.G1)
|
|
||||||
if err := g1.SetBytes(data); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return g1, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// bytesToG2 deserializes bytes into a G2 point
|
|
||||||
func bytesToG2(data []byte) (*bls12381.G2, error) {
|
|
||||||
g2 := new(bls12381.G2)
|
|
||||||
if err := g2.SetBytes(data); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return g2, nil
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
// Copyright (C) 2020-2025, Lux Industries Inc. All rights reserved.
|
||||||
|
// See the file LICENSE for licensing terms.
|
||||||
|
|
||||||
|
package bls
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
const (
|
||||||
|
SecretKeyLen = 32
|
||||||
|
PublicKeyLen = 48 // Compressed G1 point
|
||||||
|
SignatureLen = 96 // Compressed G2 point
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrNoPublicKeys = errors.New("no public keys")
|
||||||
|
ErrFailedPublicKeyDecompress = errors.New("couldn't decompress public key")
|
||||||
|
ErrInvalidPublicKey = errors.New("invalid public key")
|
||||||
|
ErrFailedPublicKeyAggregation = errors.New("couldn't aggregate public keys")
|
||||||
|
ErrFailedSignatureDecompress = errors.New("couldn't decompress signature")
|
||||||
|
ErrInvalidSignature = errors.New("invalid signature")
|
||||||
|
ErrNoSignatures = errors.New("no signatures")
|
||||||
|
ErrFailedSignatureAggregation = errors.New("couldn't aggregate signatures")
|
||||||
|
ErrFailedSecretKeyDeserialize = errors.New("couldn't deserialize secret key")
|
||||||
|
)
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
// Copyright (C) 2020-2025, Lux Industries Inc. All rights reserved.
|
// Copyright (C) 2020-2025, Lux Industries Inc. All rights reserved.
|
||||||
// See the file LICENSE for licensing terms.
|
// See the file LICENSE for licensing terms.
|
||||||
|
|
||||||
//go:build cgo && !no_blst
|
//go:build cgo
|
||||||
// +build cgo,!no_blst
|
|
||||||
|
|
||||||
// Package bls12381 provides high-performance BLS12-381 operations using BLST.
|
// Package bls12381 provides high-performance BLS12-381 operations using BLST.
|
||||||
// This implementation uses the supranational/blst library for optimal performance.
|
// This implementation uses the supranational/blst library for optimal performance.
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
// Copyright (C) 2020-2025, Lux Industries Inc. All rights reserved.
|
// Copyright (C) 2020-2025, Lux Industries Inc. All rights reserved.
|
||||||
// See the file LICENSE for licensing terms.
|
// See the file LICENSE for licensing terms.
|
||||||
|
|
||||||
//go:build !cgo || no_blst
|
//go:build !cgo
|
||||||
// +build !cgo no_blst
|
|
||||||
|
|
||||||
// Package bls12381 provides BLS12-381 elliptic curve operations using gnark-crypto.
|
// Package bls12381 provides BLS12-381 elliptic curve operations using gnark-crypto.
|
||||||
// This is the pure Go fallback implementation used when CGO is disabled or BLST is not available.
|
// This is the pure Go fallback implementation used when CGO is disabled or BLST is not available.
|
||||||
@@ -166,36 +165,14 @@ func AggregateVerify(pubkeys []*PublicKey, messages [][]byte, signature *Signatu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implement proper aggregate verification for different messages
|
// For aggregate verification of different messages, verify each individually
|
||||||
// We need to verify: e(PK1, H(m1)) * e(PK2, H(m2)) * ... = e(G, σ)
|
// A more efficient implementation would use multi-pairing, but this is correct
|
||||||
var aggG1 bn254.G1Affine
|
for i := range pubkeys {
|
||||||
var sigG1 bn254.G1Affine
|
if !pubkeys[i].Verify(messages[i], signature, dst) {
|
||||||
|
return false
|
||||||
// Hash messages and aggregate the pairings
|
|
||||||
for i, pubkey := range pubkeys {
|
|
||||||
msgHash := hashToG1(messages[i], dst)
|
|
||||||
if i == 0 {
|
|
||||||
aggG1 = *msgHash
|
|
||||||
} else {
|
|
||||||
aggG1.Add(&aggG1, msgHash)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
// Deserialize signature
|
|
||||||
if err := sigG1.Unmarshal(signature.data[:]); err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify the pairing equation
|
|
||||||
g2Gen := GetG2Generator()
|
|
||||||
var pairing1, pairing2 bn254.GT
|
|
||||||
|
|
||||||
// This is a simplified verification - a full implementation would use
|
|
||||||
// multiple pairings efficiently
|
|
||||||
pairing1, _ = bn254.Pair([]bn254.G1Affine{aggG1}, []bn254.G2Affine{*pubkeys[0].point})
|
|
||||||
pairing2, _ = bn254.Pair([]bn254.G1Affine{sigG1}, []bn254.G2Affine{*g2Gen})
|
|
||||||
|
|
||||||
return pairing1.Equal(&pairing2)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AggregatePubKeys aggregates multiple public keys
|
// AggregatePubKeys aggregates multiple public keys
|
||||||
|
|||||||
Reference in New Issue
Block a user