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>
This commit is contained in:
Prad Nukala
2025-01-06 17:06:10 +00:00
committed by GitHub
co-authored by root
parent 9bd5e41fa0
commit 807b2e86ec
483 changed files with 8067 additions and 12559 deletions
+320
View File
@@ -0,0 +1,320 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
// This file implements the Ed25519 signature algorithm. See
// https://ed25519.cr.yp.to/.
//
// These functions are also compatible with the “Ed25519” function defined in
// RFC 8032. However, unlike RFC 8032's formulation, this package's private key
// representation includes a public key suffix to make multiple signing
// operations with the same key more efficient. This package refers to the RFC
// 8032 private key as the “seed”.
// This code is a port of the public domain, “ref10” implementation of ed25519
// from SUPERCOP.
package nem
import (
"bytes"
"crypto"
cryptorand "crypto/rand"
"fmt"
"io"
"strconv"
"filippo.io/edwards25519"
"golang.org/x/crypto/sha3"
"github.com/onsonr/sonr/crypto/internal"
)
const (
// PublicKeySize is the size, in bytes, of public keys as used in this package.
PublicKeySize = 32
// PrivateKeySize is the size, in bytes, of private keys as used in this package.
PrivateKeySize = 64
// SignatureSize is the size, in bytes, of signatures generated and verified by this package.
SignatureSize = 64
// SeedSize is the size, in bytes, of private key seeds. These are the private key representations used by RFC 8032.
SeedSize = 32
)
// PublicKey is the type of Ed25519 public keys.
type PublicKey []byte
// PrivateKey is the type of Ed25519 private keys. It implements crypto.Signer.
type PrivateKey []byte
// Bytes returns the publicKey in byte array
func (p PublicKey) Bytes() []byte {
return p
}
// Public returns the PublicKey corresponding to priv.
func (priv PrivateKey) Public() crypto.PublicKey {
publicKey := make([]byte, PublicKeySize)
copy(publicKey, priv[32:])
return PublicKey(publicKey)
}
func Keccak512(data []byte) ([]byte, error) {
k512 := sha3.NewLegacyKeccak512()
_, err := k512.Write(data)
if err != nil {
return nil, err
}
return k512.Sum(nil), nil
}
// Seed returns the private key seed corresponding to priv. It is provided for
// interoperability with RFC 8032. RFC 8032's private keys correspond to seeds
// in this package.
func (priv PrivateKey) Seed() []byte {
seed := make([]byte, SeedSize)
copy(seed, priv[:32])
return seed
}
// Sign signs the given message with priv.
// Ed25519 performs two passes over messages to be signed and therefore cannot
// handle pre-hashed messages. Thus opts.HashFunc() must return zero to
// indicate the message hasn't been hashed. This can be achieved by passing
// crypto.Hash(0) as the value for opts.
func (priv PrivateKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) (signature []byte, err error) {
if opts.HashFunc() != crypto.Hash(0) {
return nil, fmt.Errorf("ed25519: cannot sign hashed message")
}
sig, err := Sign(priv, message)
if err != nil {
return nil, err
}
return sig, nil
}
// GenerateKey generates a public/private key pair using entropy from rand.
// If rand is nil, crypto/rand.Reader will be used.
func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) {
if rand == nil {
rand = cryptorand.Reader
}
seed := make([]byte, SeedSize)
if _, err := io.ReadFull(rand, seed); err != nil {
return nil, nil, err
}
privateKey, err := NewKeyFromSeed(seed)
if err != nil {
return nil, nil, err
}
publicKey := make([]byte, PublicKeySize)
copy(publicKey, privateKey[32:])
return publicKey, privateKey, nil
}
// NewKeyFromSeed calculates a private key from a seed. It will panic if
// len(seed) is not SeedSize. This function is provided for interoperability
// with RFC 8032. RFC 8032's private keys correspond to seeds in this
// package.
func NewKeyFromSeed(seed []byte) (PrivateKey, error) {
// Outline the function body so that the returned key can be stack-allocated.
privateKey := make([]byte, PrivateKeySize)
err := newKeyFromSeed(privateKey, seed)
if err != nil {
return nil, err
}
return privateKey, nil
}
func newKeyFromSeed(privateKey, seed []byte) error {
if l := len(seed); l != SeedSize {
return fmt.Errorf("ed25519: bad seed length: " + strconv.Itoa(l))
}
// Weird required step to get compatibility with the NEM test vectors
// Have to reverse the bytes from the given seed
digest, err := Keccak512(internal.ReverseScalarBytes(seed))
if err != nil {
return err
}
sc, err := edwards25519.NewScalar().SetBytesWithClamping(digest[:32])
if err != nil {
return err
}
A := edwards25519.Point{}
A.ScalarBaseMult(sc)
publicKeyBytes := A.Bytes()
copy(privateKey, seed)
copy(privateKey[32:], publicKeyBytes[:])
return nil
}
// Sign signs the message with privateKey and returns a signature. It will
// panic if len(privateKey) is not PrivateKeySize.
func Sign(privateKey PrivateKey, message []byte) ([]byte, error) {
// Outline the function body so that the returned signature can be
// stack-allocated.
signature := make([]byte, SignatureSize)
err := sign(signature, privateKey, message)
if err != nil {
return nil, err
}
return signature, nil
}
func sign(signature, privateKey, message []byte) error {
if l := len(privateKey); l != PrivateKeySize {
return fmt.Errorf("ed25519: bad private key length: " + strconv.Itoa(l))
}
seed := privateKey[:32]
digest, err := Keccak512(internal.ReverseScalarBytes(seed))
if err != nil {
return err
}
// H(seed) ie. privkey
expandedSecretKey := digest[:32]
sc, err := edwards25519.NewScalar().SetBytesWithClamping(expandedSecretKey)
if err != nil {
return err
}
// r = H(H(seed) + msg)
hEngine := sha3.NewLegacyKeccak512()
_, err = hEngine.Write(digest[32:])
if err != nil {
return err
}
_, err = hEngine.Write(message)
if err != nil {
return err
}
var hOut1 [64]byte
hEngine.Sum(hOut1[:0])
// hash output -> scalar
// Take 64 byte output from keccak512 so need to set bytes as long
r, err := edwards25519.NewScalar().SetUniformBytes(hOut1[:])
if err != nil {
return err
}
// R = r*G
R := edwards25519.Point{}
R.ScalarBaseMult(r)
RBytes := R.Bytes()
// s = H(R + pubkey + msg)
hEngine.Reset()
_, err = hEngine.Write(RBytes)
if err != nil {
return err
}
_, err = hEngine.Write(privateKey[32:])
if err != nil {
return err
}
_, err = hEngine.Write(message)
if err != nil {
return err
}
var hOut2 [64]byte
hEngine.Sum(hOut2[:0])
// hash output -> scalar
// Take 64 byte output from keccak512 so need to set bytes as long
h, err := edwards25519.NewScalar().SetUniformBytes(hOut2[:])
if err != nil {
return err
}
// s = (r + h * privKey)
s := edwards25519.NewScalar().MultiplyAdd(h, sc, r)
copy(signature[:], RBytes)
copy(signature[32:], s.Bytes())
return nil
}
// Verify reports whether sig is a valid signature of message by publicKey. It
// will panic if len(publicKey) is not PublicKeySize.
// Previously publicKey is of type PublicKey
func Verify(publicKey PublicKey, message, sig []byte) (bool, error) {
if l := len(publicKey); l != PublicKeySize {
return false, fmt.Errorf("ed25519: bad public key length: " + strconv.Itoa(l))
}
if len(sig) != SignatureSize || sig[63]&224 != 0 {
return false, fmt.Errorf("ed25519: bad signature size: " + strconv.Itoa(len(sig)))
}
RBytes := sig[:32]
sBytes := sig[32:]
var publicKeyBytes [32]byte
copy(publicKeyBytes[:], publicKey)
A := edwards25519.Point{}
_, err := A.SetBytes(publicKeyBytes[:])
if err != nil {
return false, err
}
negA := edwards25519.Point{}
negA.Negate(&A)
// h = H(R + pubkey + msg)
hEngine := sha3.NewLegacyKeccak512()
_, err = hEngine.Write(RBytes)
if err != nil {
return false, err
}
_, err = hEngine.Write(publicKeyBytes[:])
if err != nil {
return false, err
}
_, err = hEngine.Write(message)
if err != nil {
return false, err
}
var hOut1 [64]byte
hEngine.Sum(hOut1[:0])
// hash output -> scalar
// Take 64 byte output from keccak512 so need to set bytes as long
h, err := edwards25519.NewScalar().SetUniformBytes(hOut1[:])
if err != nil {
return false, err
}
// s was generated in sign so can set as canonical
s, err := edwards25519.NewScalar().SetCanonicalBytes(sBytes)
if err != nil {
return false, err
}
// R' = s*G - h*Pubkey = h*negPubkey + s*G
RPrime := edwards25519.Point{}
RPrime.VarTimeDoubleScalarBaseMult(h, &negA, s)
RPrimeBytes := RPrime.Bytes()
// Check R == R'
return bytes.Equal(RBytes, RPrimeBytes), nil
}
+190
View File
@@ -0,0 +1,190 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package nem
import (
"bytes"
"encoding/hex"
"testing"
"github.com/stretchr/testify/require"
"golang.org/x/crypto/sha3"
)
type KeyPair struct {
Privkey string `json:"privateKey"`
Pubkey string `json:"publicKey"`
}
type TestSig struct {
Privkey string `json:"privateKey"`
Pubkey string `json:"publicKey"`
Data string `json:"data"`
Length int `json:"length"`
Sig string `json:"signature"`
}
// NOTE: NEM provides no test vectors for Keccak512, but has test vectors for Keccak256
// We use Keccak256 and 512 in the exact same manner, so ensuring this test passes
// gives decent confidence in our Keccak512 use as well
func TestKeccak256SanityCheck(t *testing.T) {
data := "A6151D4904E18EC288243028CEDA30556E6C42096AF7150D6A7232CA5DBA52BD2192E23DAA5FA2BEA3D4BD95EFA2389CD193FCD3376E70A5C097B32C1C62C80AF9D710211545F7CDDDF63747420281D64529477C61E721273CFD78F8890ABB4070E97BAA52AC8FF61C26D195FC54C077DEF7A3F6F79B36E046C1A83CE9674BA1983EC2FB58947DE616DD797D6499B0385D5E8A213DB9AD5078A8E0C940FF0CB6BF92357EA5609F778C3D1FB1E7E36C35DB873361E2BE5C125EA7148EFF4A035B0CCE880A41190B2E22924AD9D1B82433D9C023924F2311315F07B88BFD42850047BF3BE785C4CE11C09D7E02065D30F6324365F93C5E7E423A07D754EB314B5FE9DB4614275BE4BE26AF017ABDC9C338D01368226FE9AF1FB1F815E7317BDBB30A0F36DC69"
toMatch := "4E9E79AB7434F6C7401FB3305D55052EE829B9E46D5D05D43B59FEFB32E9A619"
toMatchBytes, err := hex.DecodeString(toMatch)
require.NoError(t, err)
dataBytes, err := hex.DecodeString(data)
require.NoError(t, err)
k256 := sha3.NewLegacyKeccak256()
_, err = k256.Write(dataBytes)
require.NoError(t, err)
var hashed []byte
hashed = k256.Sum(hashed)
require.Equal(t, hashed, toMatchBytes)
}
// Test that the pubkey can get derived correctly from privkey
func TestPrivToPubkey(t *testing.T) {
testVectors := GetPrivToPubkeyTestCases()
for _, pair := range testVectors {
privkeyBytes, err := hex.DecodeString(pair.Privkey)
require.NoError(t, err)
pubkeyBytes, err := hex.DecodeString(pair.Pubkey)
require.NoError(t, err)
privKeyCalced, err := NewKeyFromSeed(privkeyBytes)
require.NoError(t, err)
pubKeyCalced := privKeyCalced.Public().(PublicKey)
require.Equal(t, pubKeyCalced.Bytes(), pubkeyBytes)
}
}
// Test that we can:
// Get pubkey from privkey
// Obtain the correct signature
// Verify the test vector provided signature
func TestSigs(t *testing.T) {
testVectors := GetSigTestCases()
for _, ts := range testVectors {
// Test priv -> pubkey again
privkeyBytes, err := hex.DecodeString(ts.Privkey)
require.NoError(t, err)
pubkeyBytes, err := hex.DecodeString(ts.Pubkey)
require.NoError(t, err)
privKeyCalced, err := NewKeyFromSeed(privkeyBytes)
require.NoError(t, err)
pubKeyCalced := privKeyCalced.Public().(PublicKey)
require.True(t, bytes.Equal(pubKeyCalced.Bytes(), pubkeyBytes))
dataBytes, err := hex.DecodeString(ts.Data)
require.NoError(t, err)
sigBytes, err := hex.DecodeString(ts.Sig)
require.NoError(t, err)
// Test sign
sigCalced, err := Sign(privKeyCalced, dataBytes)
require.NoError(t, err)
require.True(t, bytes.Equal(sigCalced, sigBytes))
// Test verify
verified, err := Verify(pubKeyCalced, dataBytes, sigBytes)
require.NoError(t, err)
require.True(t, verified)
}
}
// NOTE: Test cases were obtained from NEM
// See link: https://github.com/symbol/test-vectors
// Pulled 5 test vectors for each test case, at time of writing confirmed that all 10000 vectors passed
func GetPrivToPubkeyTestCases() []KeyPair {
var toReturn []KeyPair
kp1 := KeyPair{
Privkey: "575DBB3062267EFF57C970A336EBBC8FBCFE12C5BD3ED7BC11EB0481D7704CED",
Pubkey: "C5F54BA980FCBB657DBAAA42700539B207873E134D2375EFEAB5F1AB52F87844",
}
kp2 := KeyPair{
Privkey: "5B0E3FA5D3B49A79022D7C1E121BA1CBBF4DB5821F47AB8C708EF88DEFC29BFE",
Pubkey: "96EB2A145211B1B7AB5F0D4B14F8ABC8D695C7AEE31A3CFC2D4881313C68EEA3",
}
kp3 := KeyPair{
Privkey: "738BA9BB9110AEA8F15CAA353ACA5653B4BDFCA1DB9F34D0EFED2CE1325AEEDA",
Pubkey: "2D8425E4CA2D8926346C7A7CA39826ACD881A8639E81BD68820409C6E30D142A",
}
kp4 := KeyPair{
Privkey: "E8BF9BC0F35C12D8C8BF94DD3A8B5B4034F1063948E3CC5304E55E31AA4B95A6",
Pubkey: "4FEED486777ED38E44C489C7C4E93A830E4C4A907FA19A174E630EF0F6ED0409",
}
kp5 := KeyPair{
Privkey: "C325EA529674396DB5675939E7988883D59A5FC17A28CA977E3BA85370232A83",
Pubkey: "83EE32E4E145024D29BCA54F71FA335A98B3E68283F1A3099C4D4AE113B53E54",
}
toReturn = append(toReturn, kp1, kp2, kp3, kp4, kp5)
return toReturn
}
func GetSigTestCases() []TestSig {
var toReturn []TestSig
t1 := TestSig{
Privkey: "ABF4CF55A2B3F742D7543D9CC17F50447B969E6E06F5EA9195D428AB12B7318D",
Pubkey: "8A558C728C21C126181E5E654B404A45B4F0137CE88177435A69978CC6BEC1F4",
Data: "8CE03CD60514233B86789729102EA09E867FC6D964DEA8C2018EF7D0A2E0E24BF7E348E917116690B9",
Length: 41,
Sig: "D9CEC0CC0E3465FAB229F8E1D6DB68AB9CC99A18CB0435F70DEB6100948576CD5C0AA1FEB550BDD8693EF81EB10A556A622DB1F9301986827B96716A7134230C",
}
t2 := TestSig{
Privkey: "6AA6DAD25D3ACB3385D5643293133936CDDDD7F7E11818771DB1FF2F9D3F9215",
Pubkey: "BBC8CBB43DDA3ECF70A555981A351A064493F09658FFFE884C6FAB2A69C845C6",
Data: "E4A92208A6FC52282B620699191EE6FB9CF04DAF48B48FD542C5E43DAA9897763A199AAA4B6F10546109F47AC3564FADE0",
Length: 49,
Sig: "98BCA58B075D1748F1C3A7AE18F9341BC18E90D1BEB8499E8A654C65D8A0B4FBD2E084661088D1E5069187A2811996AE31F59463668EF0F8CB0AC46A726E7902",
}
t3 := TestSig{
Privkey: "8E32BC030A4C53DE782EC75BA7D5E25E64A2A072A56E5170B77A4924EF3C32A9",
Pubkey: "72D0E65F1EDE79C4AF0BA7EC14204E10F0F7EA09F2BC43259CD60EA8C3A087E2",
Data: "13ED795344C4448A3B256F23665336645A853C5C44DBFF6DB1B9224B5303B6447FBF8240A2249C55",
Length: 40,
Sig: "EF257D6E73706BB04878875C58AA385385BF439F7040EA8297F7798A0EA30C1C5EFF5DDC05443F801849C68E98111AE65D088E726D1D9B7EECA2EB93B677860C",
}
t4 := TestSig{
Privkey: "C83CE30FCB5B81A51BA58FF827CCBC0142D61C13E2ED39E78E876605DA16D8D7",
Pubkey: "3EC8923F9EA5EA14F8AAA7E7C2784653ED8C7DE44E352EF9FC1DEE81FC3FA1A3",
Data: "A2704638434E9F7340F22D08019C4C8E3DBEE0DF8DD4454A1D70844DE11694F4C8CA67FDCB08FED0CEC9ABB2112B5E5F89",
Length: 49,
Sig: "0C684E71B35FED4D92B222FC60561DB34E0D8AFE44BDD958AAF4EE965911BEF5991236F3E1BCED59FC44030693BCAC37F34D29E5AE946669DC326E706E81B804",
}
t5 := TestSig{
Privkey: "2DA2A0AAE0F37235957B51D15843EDDE348A559692D8FA87B94848459899FC27",
Pubkey: "D73D0B14A9754EEC825FCB25EF1CFA9AE3B1370074EDA53FC64C22334A26C254",
Data: "D2488E854DBCDFDB2C9D16C8C0B2FDBC0ABB6BAC991BFE2B14D359A6BC99D66C00FD60D731AE06D0",
Length: 40,
Sig: "6F17F7B21EF9D6907A7AB104559F77D5A2532B557D95EDFFD6D88C073D87AC00FC838FC0D05282A0280368092A4BD67E95C20F3E14580BE28D8B351968C65E03",
}
toReturn = append(toReturn, t1, t2, t3, t4, t5)
return toReturn
}