github.com/ethersphere/bee/v2@v2.2.0/pkg/soc/soc.go (about)

     1  // Copyright 2020 The Swarm Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package soc provides the single-owner chunk implementation
     6  // and validator.
     7  package soc
     8  
     9  import (
    10  	"bytes"
    11  	"errors"
    12  
    13  	"github.com/ethersphere/bee/v2/pkg/cac"
    14  	"github.com/ethersphere/bee/v2/pkg/crypto"
    15  	"github.com/ethersphere/bee/v2/pkg/swarm"
    16  )
    17  
    18  var (
    19  	errInvalidAddress = errors.New("soc: invalid address")
    20  	errWrongChunkSize = errors.New("soc: chunk length is less than minimum")
    21  )
    22  
    23  // ID is a SOC identifier
    24  type ID []byte
    25  
    26  // SOC wraps a content-addressed chunk.
    27  type SOC struct {
    28  	id        ID
    29  	owner     []byte // owner is the address in bytes of SOC owner.
    30  	signature []byte
    31  	chunk     swarm.Chunk // wrapped chunk.
    32  }
    33  
    34  // New creates a new SOC representation from arbitrary id and
    35  // a content-addressed chunk.
    36  func New(id ID, ch swarm.Chunk) *SOC {
    37  	return &SOC{
    38  		id:    id,
    39  		chunk: ch,
    40  	}
    41  }
    42  
    43  // NewSigned creates a single-owner chunk based on already signed data.
    44  func NewSigned(id ID, ch swarm.Chunk, owner, sig []byte) (*SOC, error) {
    45  	s := New(id, ch)
    46  	if len(owner) != crypto.AddressSize {
    47  		return nil, errInvalidAddress
    48  	}
    49  	s.owner = owner
    50  	s.signature = sig
    51  	return s, nil
    52  }
    53  
    54  // Address returns the SOC chunk address.
    55  func (s *SOC) Address() (swarm.Address, error) {
    56  	if len(s.owner) != crypto.AddressSize {
    57  		return swarm.ZeroAddress, errInvalidAddress
    58  	}
    59  	return CreateAddress(s.id, s.owner)
    60  }
    61  
    62  // WrappedChunk returns the chunk wrapped by the SOC.
    63  func (s *SOC) WrappedChunk() swarm.Chunk {
    64  	return s.chunk
    65  }
    66  
    67  // Chunk returns the SOC chunk.
    68  func (s *SOC) Chunk() (swarm.Chunk, error) {
    69  	socAddress, err := s.Address()
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  	return swarm.NewChunk(socAddress, s.toBytes()), nil
    74  }
    75  
    76  // Signature returns the SOC signature.
    77  func (s *SOC) Signature() []byte {
    78  	return s.signature
    79  }
    80  
    81  // OwnerAddress returns the ethereum address of the SOC owner.
    82  func (s *SOC) OwnerAddress() []byte {
    83  	return s.owner
    84  }
    85  
    86  // ID returns the SOC id.
    87  func (s *SOC) ID() []byte {
    88  	return s.id
    89  }
    90  
    91  // toBytes is a helper function to convert the SOC data to bytes.
    92  func (s *SOC) toBytes() []byte {
    93  	buf := bytes.NewBuffer(nil)
    94  	buf.Write(s.id)
    95  	buf.Write(s.signature)
    96  	buf.Write(s.chunk.Data())
    97  	return buf.Bytes()
    98  }
    99  
   100  // Sign signs a SOC using the given signer.
   101  // It returns a signed SOC chunk ready for submission to the network.
   102  func (s *SOC) Sign(signer crypto.Signer) (swarm.Chunk, error) {
   103  	// create owner
   104  	publicKey, err := signer.PublicKey()
   105  	if err != nil {
   106  		return nil, err
   107  	}
   108  	ownerAddressBytes, err := crypto.NewEthereumAddress(*publicKey)
   109  	if err != nil {
   110  		return nil, err
   111  	}
   112  	if len(ownerAddressBytes) != crypto.AddressSize {
   113  		return nil, errInvalidAddress
   114  	}
   115  	s.owner = ownerAddressBytes
   116  
   117  	// generate the data to sign
   118  	toSignBytes, err := hash(s.id, s.chunk.Address().Bytes())
   119  	if err != nil {
   120  		return nil, err
   121  	}
   122  
   123  	// sign the chunk
   124  	signature, err := signer.Sign(toSignBytes)
   125  	if err != nil {
   126  		return nil, err
   127  	}
   128  	s.signature = signature
   129  
   130  	return s.Chunk()
   131  }
   132  
   133  // FromChunk recreates a SOC representation from swarm.Chunk data.
   134  func FromChunk(sch swarm.Chunk) (*SOC, error) {
   135  	chunkData := sch.Data()
   136  	if len(chunkData) < swarm.SocMinChunkSize {
   137  		return nil, errWrongChunkSize
   138  	}
   139  
   140  	// add all the data fields to the SOC
   141  	s := &SOC{}
   142  	cursor := 0
   143  
   144  	s.id = chunkData[cursor:swarm.HashSize]
   145  	cursor += swarm.HashSize
   146  
   147  	s.signature = chunkData[cursor : cursor+swarm.SocSignatureSize]
   148  	cursor += swarm.SocSignatureSize
   149  
   150  	ch, err := cac.NewWithDataSpan(chunkData[cursor:])
   151  	if err != nil {
   152  		return nil, err
   153  	}
   154  
   155  	toSignBytes, err := hash(s.id, ch.Address().Bytes())
   156  	if err != nil {
   157  		return nil, err
   158  	}
   159  
   160  	// recover owner information
   161  	recoveredOwnerAddress, err := recoverAddress(s.signature, toSignBytes)
   162  	if err != nil {
   163  		return nil, err
   164  	}
   165  	if len(recoveredOwnerAddress) != crypto.AddressSize {
   166  		return nil, errInvalidAddress
   167  	}
   168  	s.owner = recoveredOwnerAddress
   169  	s.chunk = ch
   170  
   171  	return s, nil
   172  }
   173  
   174  // CreateAddress creates a new SOC address from the id and
   175  // the ethereum address of the owner.
   176  func CreateAddress(id ID, owner []byte) (swarm.Address, error) {
   177  	sum, err := hash(id, owner)
   178  	if err != nil {
   179  		return swarm.ZeroAddress, err
   180  	}
   181  	return swarm.NewAddress(sum), nil
   182  }
   183  
   184  // hash hashes the given values in order.
   185  func hash(values ...[]byte) ([]byte, error) {
   186  	h := swarm.NewHasher()
   187  	for _, v := range values {
   188  		_, err := h.Write(v)
   189  		if err != nil {
   190  			return nil, err
   191  		}
   192  	}
   193  	return h.Sum(nil), nil
   194  }
   195  
   196  // recoverAddress returns the ethereum address of the owner of a SOC.
   197  func recoverAddress(signature, digest []byte) ([]byte, error) {
   198  	recoveredPublicKey, err := crypto.Recover(signature, digest)
   199  	if err != nil {
   200  		return nil, err
   201  	}
   202  	recoveredEthereumAddress, err := crypto.NewEthereumAddress(*recoveredPublicKey)
   203  	if err != nil {
   204  		return nil, err
   205  	}
   206  	return recoveredEthereumAddress, nil
   207  }