mirror of
https://source.quilibrium.com/quilibrium/ceremonyclient.git
synced 2024-12-27 09:05:18 +00:00
124 lines
3.8 KiB
Go
124 lines
3.8 KiB
Go
//
|
|
// Copyright Coinbase, Inc. All Rights Reserved.
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
|
|
package ted25519
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"crypto/sha256"
|
|
"io"
|
|
|
|
"golang.org/x/crypto/hkdf"
|
|
|
|
"source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/core/curves"
|
|
)
|
|
|
|
// NonceShare represents a share of a generated nonce.
|
|
type NonceShare struct {
|
|
*KeyShare
|
|
}
|
|
|
|
// NewNonceShare is a NonceShare construction
|
|
func NewNonceShare(identifier byte, secret []byte) *NonceShare {
|
|
return &NonceShare{NewKeyShare(identifier, secret)}
|
|
}
|
|
|
|
// NonceShareFromBytes unmashals a NonceShare from its bytes representation
|
|
func NonceShareFromBytes(bytes []byte) *NonceShare {
|
|
return &NonceShare{KeyShareFromBytes(bytes)}
|
|
}
|
|
|
|
func generateSharableNonce(s *KeyShare, p PublicKey, m Message) (PublicKey, []byte, error) {
|
|
// Create an HKDF reader that produces random bytes that we will use to create a nonce
|
|
hkdf, err := generateRandomHkdf(s, p, m)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
// Generate a random nonce that is within the field range so that we can share it.
|
|
//
|
|
// This diverges from how the standard implementation treats it because their scalar
|
|
// multiplication accepts values up to the curve order, but we must constrain it to be able to
|
|
// split it and aggregate.
|
|
//
|
|
// WARN: This operation is not constant time and we are dealing with a secret value
|
|
nonce, err := curves.NewField(curves.Ed25519Order()).RandomElement(hkdf)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
nonceBytes := nonce.Bytes()
|
|
reverseBytes := reverseBytes(nonceBytes)
|
|
var reverseInput [32]byte
|
|
copy(reverseInput[:], reverseBytes)
|
|
scalar, err := new(curves.ScalarEd25519).SetBytesCanonical(reverseInput[:])
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
// Generate the nonce pubkey by multiplying it by the base point.
|
|
noncePubkey := curves.ED25519().Point.Generator().Mul(scalar)
|
|
|
|
return noncePubkey.ToAffineCompressed(), nonceBytes, nil
|
|
}
|
|
|
|
// GenerateSharedNonce generates a random nonce, splits it, and returns the nonce pubkey, nonce shares, and
|
|
// VSS commitments.
|
|
func GenerateSharedNonce(config *ShareConfiguration, s *KeyShare, p PublicKey, m Message) (
|
|
PublicKey,
|
|
[]*NonceShare,
|
|
Commitments,
|
|
error,
|
|
) {
|
|
noncePubkey, nonce, err := generateSharableNonce(s, p, m)
|
|
if err != nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
keyShares, vssCommitments, err := splitPrivateKey(config, nonce)
|
|
if err != nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
|
|
nonceShares := make([]*NonceShare, len(keyShares))
|
|
for i, k := range keyShares {
|
|
nonceShares[i] = &NonceShare{k}
|
|
}
|
|
return noncePubkey, nonceShares, vssCommitments, nil
|
|
}
|
|
|
|
// Add returns the sum of two NonceShares.
|
|
func (n NonceShare) Add(other *NonceShare) *NonceShare {
|
|
return &NonceShare{
|
|
&KeyShare{
|
|
// use Add method from the shamir.Share type to sum the shares
|
|
// WARN: This is not constant time and deals with secrets
|
|
n.ShamirShare.Add(other.ShamirShare),
|
|
},
|
|
}
|
|
}
|
|
|
|
// generateRandomHkdf returns an HMAC-based extract-and-expand Key Derivation Function (see RFC 5869).
|
|
func generateRandomHkdf(s *KeyShare, p PublicKey, m Message) (io.Reader, error) {
|
|
// We _must_ introduce randomness to the HKDF to make the output non-deterministic because deterministic nonces open
|
|
// up threshold schemes to potential nonce-reuse attacks. We continue to use the HKDF that takes in context about
|
|
// what is going to be signed as it adds some protection against bad local randomness.
|
|
randNonce := make([]byte, SeedSize)
|
|
if _, err := io.ReadFull(rand.Reader, randNonce); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var secret []byte
|
|
secret = append(secret, s.Bytes()...)
|
|
secret = append(secret, randNonce...)
|
|
|
|
info := []byte("ted25519nonce")
|
|
// We use info for non-secret inputs to limit an attacker's ability to influence the key.
|
|
info = append(info, p.Bytes()...)
|
|
info = append(info, m...)
|
|
|
|
return hkdf.New(sha256.New, secret, nil, info), nil
|
|
}
|