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 }