Prad Nukala 807b2e86ec
feature/1220 origin handle exists method (#1241)
* feat: add docs and CI workflow for publishing to onsonr.dev

* (refactor): Move hway,motr executables to their own repos

* feat: simplify devnet and testnet configurations

* refactor: update import path for didcrypto package

* docs(networks): Add README with project overview, architecture, and community links

* refactor: Move network configurations to deploy directory

* build: update golang version to 1.23

* refactor: move logger interface to appropriate package

* refactor: Move devnet configuration to networks/devnet

* chore: improve release process with date variable

* (chore): Move Crypto Library

* refactor: improve code structure and readability in DID module

* feat: integrate Trunk CI checks

* ci: optimize CI workflow by removing redundant build jobs

---------

Co-authored-by: Darp Alakun <i@prad.nu>
2025-01-06 17:06:10 +00:00

124 lines
3.8 KiB
Go
Executable File

//
// 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"
"github.com/onsonr/sonr/crypto/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
}