Skip to content

Commit

Permalink
shutter: port over crypto pkg (#13752)
Browse files Browse the repository at this point in the history
part 2 of #13384
used in #13725

Short-term ported from:
https://github.com/shutter-network/shutter/tree/main/shlib/shcrypto
Long term plan is to import this as a library from the shutter repos,
however for now im doing this port because currently that library has
go-ethereum dependencies and so that will bring go-ethereum transitive
dependencies to erigon which is not desirable. I've had a look and
actually it is possible to remove that go-ethereum dependency in the
shutter crypto package but needs a bit of work and cooperation with the
shutter team. I've created an issue
(#13724) for this and have
left it for a future time (after we have a working shutter validator
implementation).
  • Loading branch information
taratorio authored Feb 10, 2025
1 parent 325afd7 commit 81ad408
Show file tree
Hide file tree
Showing 13 changed files with 2,379 additions and 1 deletion.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ require (
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/stoewer/go-strcase v1.2.0 // indirect
github.com/supranational/blst v0.3.13 // indirect
github.com/supranational/blst v0.3.13
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
go.etcd.io/bbolt v1.3.6 // indirect
go.opentelemetry.io/otel v1.31.0 // indirect
Expand Down
8 changes: 8 additions & 0 deletions txnprovider/shutter/internal/crypto/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<h1>Context</h1>

This is ported from https://github.com/shutter-network/shutter/tree/main/shlib/shcrypto to
avoid introducing a go-ethereum transitive dependency in Erigon.

In the long term, we can work with the shutter team to remove their dependency on go-ethereum
for their `shcrypto` package (doable). When that is done we will be able to simply use their
library directly.
300 changes: 300 additions & 0 deletions txnprovider/shutter/internal/crypto/encoding.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,300 @@
// Copyright 2025 The Erigon Authors
// This file is part of Erigon.
//
// Erigon is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Erigon is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Erigon. If not, see <http://www.gnu.org/licenses/>.

package crypto

import (
"bytes"
"errors"
"fmt"
"math/big"

blst "github.com/supranational/blst/bindings/go"

"github.com/erigontech/erigon-lib/common/hexutil"
"github.com/erigontech/erigon-lib/common/hexutility"
)

var (
ErrInputTooLong = errors.New("input too long")
ErrInvalidEonSecretKeyShare = errors.New("invalid eon secret key share")
ErrVersionMismatch = func(version_got byte) error {
return fmt.Errorf("version mismatch. want %d got %d", VersionIdentifier, version_got)
}
)

// Marshal serializes the EncryptedMessage object. It panics, if C1 is nil.
func (m *EncryptedMessage) Marshal() []byte {
if m.C1 == nil {
panic("not a valid encrypted message. C1==nil")
}

buff := bytes.Buffer{}
buff.WriteByte(VersionIdentifier)
buff.Write(m.C1.Compress())
buff.Write(m.C2[:])
for i := range m.C3 {
buff.Write(m.C3[i][:])
}

return buff.Bytes()
}

// Unmarshal deserializes an EncryptedMessage from the given byte slice.
func (m *EncryptedMessage) Unmarshal(d []byte) error {
if len(d) == 0 {
return errors.New("not enough data")
}
if d[0] != VersionIdentifier {
return ErrVersionMismatch(IdentifyVersion(d))
}

if len(d) < 1+blst.BLST_P2_COMPRESS_BYTES+BlockSize ||
(len(d)-1-blst.BLST_P2_COMPRESS_BYTES-BlockSize)%BlockSize != 0 {
return fmt.Errorf("invalid length %d of encrypted message", len(d))
}
if m.C1 == nil {
m.C1 = new(blst.P2Affine)
}
m.C1.Uncompress(d[1 : 1+blst.BLST_P2_COMPRESS_BYTES])
if m.C1 == nil {
return errors.New("failed to deserialize C1")
}
if !m.C1.InG2() {
return errors.New("C1 not on curve")
}

copy(m.C2[:], d[1+blst.BLST_P2_COMPRESS_BYTES:1+blst.BLST_P2_COMPRESS_BYTES+BlockSize])

m.C3 = nil
for i := 1 + blst.BLST_P2_COMPRESS_BYTES + BlockSize; i < len(d); i += BlockSize {
b := Block{}
copy(b[:], d[i:i+BlockSize])
m.C3 = append(m.C3, b)
}

return nil
}

// Marshal serializes the eon secret key share.
func (eonSecretKeyShare *EonSecretKeyShare) Marshal() []byte {
return (*big.Int)(eonSecretKeyShare).Bytes()
}

// Unarshal deserializes an eon secret key share.
func (eonSecretKeyShare *EonSecretKeyShare) Unmarshal(m []byte) error {
(*big.Int)(eonSecretKeyShare).SetBytes(m)
if (*big.Int)(eonSecretKeyShare).Cmp(order) >= 0 {
return ErrInvalidEonSecretKeyShare
}
return nil
}

// Marshal serializes the eon public key share.
func (eonPublicKeyShare *EonPublicKeyShare) Marshal() []byte {
return (*blst.P2Affine)(eonPublicKeyShare).Compress()
}

// Unmarshal deserializes an eon public key share.
func (eonPublicKeyShare *EonPublicKeyShare) Unmarshal(m []byte) error {
p := (*blst.P2Affine)(eonPublicKeyShare)
p.Uncompress(m)
if p == nil {
return errors.New("failed to deserialize eon public key share")
}
if !p.InG2() {
return errors.New("eon public key share is not on curve")
}
return nil
}

// Marshal serializes the eon public key.
func (eonPublicKey *EonPublicKey) Marshal() []byte {
return (*blst.P2Affine)(eonPublicKey).Compress()
}

// Unmarshal deserializes an eon public key from the given byte slice.
func (eonPublicKey *EonPublicKey) Unmarshal(m []byte) error {
p := (*blst.P2Affine)(eonPublicKey)
p.Uncompress(m)
if p == nil {
return errors.New("failed to deserialize eon public key")
}
if !p.InG2() {
return errors.New("eon public key is not on curve")
}
return nil
}

// MarshalText serializes the eon public key to hex.
func (eonPublicKey EonPublicKey) MarshalText() ([]byte, error) {
return hexutility.Bytes(eonPublicKey.Marshal()).MarshalText()
}

// UnmarshalText deserializes the eon public key from hex.
func (eonPublicKey *EonPublicKey) UnmarshalText(input []byte) error {
var b hexutility.Bytes
if err := b.UnmarshalText(input); err != nil {
return err
}
return eonPublicKey.Unmarshal(b)
}

// Marshal serializes the epoch id.
func (epochID *EpochID) Marshal() []byte {
return (*blst.P1Affine)(epochID).Compress()
}

// Unmarshal deserializes an epoch id.
func (epochID *EpochID) Unmarshal(m []byte) error {
p := (*blst.P1Affine)(epochID)
p.Uncompress(m)
if p == nil {
return errors.New("failed to deserialize epoch id")
}
if !p.InG1() {
return errors.New("epoch id is not on curve")
}
*epochID = EpochID(*p)
return nil
}

// Marshal serializes the epoch secret key share.
func (epochSecretKeyShare *EpochSecretKeyShare) Marshal() []byte {
return (*blst.P1Affine)(epochSecretKeyShare).Compress()
}

// Unmarshal deserializes an epoch secret key share.
func (epochSecretKeyShare *EpochSecretKeyShare) Unmarshal(m []byte) error {
p := (*blst.P1Affine)(epochSecretKeyShare)
p.Uncompress(m)
if p == nil {
return errors.New("failed to deserialize epoch secret key share")
}
if !p.InG1() {
return errors.New("epoch secret key share is not on curve")
}
return nil
}

// Marshal serializes the epoch secret key.
func (epochSecretKey *EpochSecretKey) Marshal() []byte {
return (*blst.P1Affine)(epochSecretKey).Compress()
}

// Unmarshal deserializes an epoch secret key.
func (epochSecretKey *EpochSecretKey) Unmarshal(m []byte) error {
p := (*blst.P1Affine)(epochSecretKey)
p.Uncompress(m)
if p == nil {
return errors.New("failed to deserialize epoch secret key")
}
if !p.InG1() {
return errors.New("epoch secret key is not on curve")
}
return nil
}

// MarshalText serializes the epoch secret key to hex.
func (epochSecretKey EpochSecretKey) MarshalText() ([]byte, error) { //nolint: unparam
return []byte(hexutility.Encode(epochSecretKey.Marshal())), nil
}

// UnmarshalText deserializes the epoch secret key from hex.
func (epochSecretKey *EpochSecretKey) UnmarshalText(input []byte) error {
var b hexutility.Bytes
if err := b.UnmarshalText(input); err != nil {
return err
}
return epochSecretKey.Unmarshal(b)
}

// MarshalText serializes the block to hex.
func (block Block) MarshalText() ([]byte, error) { //nolint:unparam
return []byte(hexutility.Encode(block[:])), nil
}

// UnmarshalText deserializes the block from hex.
func (block *Block) UnmarshalText(b []byte) error {
decoded, err := hexutil.Decode(string(b))
copy(block[:], decoded)
return err
}

// MarshalText serializes the encrypted message to hex.
func (m EncryptedMessage) MarshalText() ([]byte, error) { //nolint:unparam
return []byte(hexutility.Encode(m.Marshal())), nil
}

// UnmarshalText deserializes the encrypted message from hex.
func (m *EncryptedMessage) UnmarshalText(b []byte) error {
decoded, err := hexutil.Decode(string(b))
if err != nil {
return err
}
err = m.Unmarshal(decoded)
return err
}

// Marshal serializes the gammas value.
//
// Serialization format: [n:4][gamma1:96]...[gamman:96]
func (g *Gammas) Marshal() []byte {
if g == nil {
return []byte{}
}
buff := bytes.Buffer{}
for _, p := range *g {
buff.Write(p.Compress())
}
return buff.Bytes()
}

// Unmarshal deserializes a gammas value.
func (g *Gammas) Unmarshal(m []byte) error {
if len(m)%blst.BLST_P2_COMPRESS_BYTES != 0 {
return errors.New("invalid length of gammas")
}
n := len(m) / blst.BLST_P2_COMPRESS_BYTES
*g = make(Gammas, n)
for i := 0; i < n; i++ {
p := new(blst.P2Affine)
p = p.Uncompress(m[i*blst.BLST_P2_COMPRESS_BYTES : (i+1)*blst.BLST_P2_COMPRESS_BYTES])
if p == nil {
return errors.New("failed to deserialize gamma")
}
if !p.InG2() {
return errors.New("gamma is not on curve")
}
(*g)[i] = p
}
return nil
}

// MarshalText serializes the gammas as hex.
func (g *Gammas) MarshalText() ([]byte, error) {
return []byte(hexutility.Encode(g.Marshal())), nil
}

// UnmarshalText deserializes the gammas from hex.
func (g *Gammas) UnmarshalText(b []byte) error {
decoded, err := hexutil.Decode(string(b))
if err != nil {
return err
}
err = g.Unmarshal(decoded)
return err
}
Loading

0 comments on commit 81ad408

Please sign in to comment.