github.com/divan/go-ethereum@v1.8.14-0.20180820134928-1de9ada4016d/swarm/storage/mru/signedupdate.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package mru 18 19 import ( 20 "bytes" 21 "hash" 22 23 "github.com/ethereum/go-ethereum/common" 24 "github.com/ethereum/go-ethereum/crypto" 25 "github.com/ethereum/go-ethereum/swarm/storage" 26 ) 27 28 // SignedResourceUpdate represents a resource update with all the necessary information to prove ownership of the resource 29 type SignedResourceUpdate struct { 30 resourceUpdate // actual content that will be put on the chunk, less signature 31 signature *Signature 32 updateAddr storage.Address // resulting chunk address for the update (not serialized, for internal use) 33 binaryData []byte // resulting serialized data (not serialized, for efficiency/internal use) 34 } 35 36 // Verify checks that signatures are valid and that the signer owns the resource to be updated 37 func (r *SignedResourceUpdate) Verify() (err error) { 38 if len(r.data) == 0 { 39 return NewError(ErrInvalidValue, "Update does not contain data") 40 } 41 if r.signature == nil { 42 return NewError(ErrInvalidSignature, "Missing signature field") 43 } 44 45 digest, err := r.GetDigest() 46 if err != nil { 47 return err 48 } 49 50 // get the address of the signer (which also checks that it's a valid signature) 51 ownerAddr, err := getOwner(digest, *r.signature) 52 if err != nil { 53 return err 54 } 55 56 if !bytes.Equal(r.updateAddr, r.UpdateAddr()) { 57 return NewError(ErrInvalidSignature, "Signature address does not match with ownerAddr") 58 } 59 60 // Check if who signed the resource update really owns the resource 61 if !verifyOwner(ownerAddr, r.metaHash, r.rootAddr) { 62 return NewErrorf(ErrUnauthorized, "signature is valid but signer does not own the resource: %v", err) 63 } 64 65 return nil 66 } 67 68 // Sign executes the signature to validate the resource 69 func (r *SignedResourceUpdate) Sign(signer Signer) error { 70 71 r.binaryData = nil //invalidate serialized data 72 digest, err := r.GetDigest() // computes digest and serializes into .binaryData 73 if err != nil { 74 return err 75 } 76 77 signature, err := signer.Sign(digest) 78 if err != nil { 79 return err 80 } 81 82 // Although the Signer interface returns the public address of the signer, 83 // recover it from the signature to see if they match 84 ownerAddress, err := getOwner(digest, signature) 85 if err != nil { 86 return NewError(ErrInvalidSignature, "Error verifying signature") 87 } 88 89 if ownerAddress != signer.Address() { // sanity check to make sure the Signer is declaring the same address used to sign! 90 return NewError(ErrInvalidSignature, "Signer address does not match ownerAddr") 91 } 92 93 r.signature = &signature 94 r.updateAddr = r.UpdateAddr() 95 return nil 96 } 97 98 // create an update chunk. 99 func (r *SignedResourceUpdate) toChunk() (*storage.Chunk, error) { 100 101 // Check that the update is signed and serialized 102 // For efficiency, data is serialized during signature and cached in 103 // the binaryData field when computing the signature digest in .getDigest() 104 if r.signature == nil || r.binaryData == nil { 105 return nil, NewError(ErrInvalidSignature, "newUpdateChunk called without a valid signature or payload data. Call .Sign() first.") 106 } 107 108 chunk := storage.NewChunk(r.updateAddr, nil) 109 resourceUpdateLength := r.resourceUpdate.binaryLength() 110 chunk.SData = r.binaryData 111 112 // signature is the last item in the chunk data 113 copy(chunk.SData[resourceUpdateLength:], r.signature[:]) 114 115 chunk.Size = int64(len(chunk.SData)) 116 return chunk, nil 117 } 118 119 // fromChunk populates this structure from chunk data. It does not verify the signature is valid. 120 func (r *SignedResourceUpdate) fromChunk(updateAddr storage.Address, chunkdata []byte) error { 121 // for update chunk layout see SignedResourceUpdate definition 122 123 //deserialize the resource update portion 124 if err := r.resourceUpdate.binaryGet(chunkdata); err != nil { 125 return err 126 } 127 128 // Extract the signature 129 var signature *Signature 130 cursor := r.resourceUpdate.binaryLength() 131 sigdata := chunkdata[cursor : cursor+signatureLength] 132 if len(sigdata) > 0 { 133 signature = &Signature{} 134 copy(signature[:], sigdata) 135 } 136 137 r.signature = signature 138 r.updateAddr = updateAddr 139 r.binaryData = chunkdata 140 141 return nil 142 143 } 144 145 // GetDigest creates the resource update digest used in signatures (formerly known as keyDataHash) 146 // the serialized payload is cached in .binaryData 147 func (r *SignedResourceUpdate) GetDigest() (result common.Hash, err error) { 148 hasher := hashPool.Get().(hash.Hash) 149 defer hashPool.Put(hasher) 150 hasher.Reset() 151 dataLength := r.resourceUpdate.binaryLength() 152 if r.binaryData == nil { 153 r.binaryData = make([]byte, dataLength+signatureLength) 154 if err := r.resourceUpdate.binaryPut(r.binaryData[:dataLength]); err != nil { 155 return result, err 156 } 157 } 158 hasher.Write(r.binaryData[:dataLength]) //everything except the signature. 159 160 return common.BytesToHash(hasher.Sum(nil)), nil 161 } 162 163 // getOwner extracts the address of the resource update signer 164 func getOwner(digest common.Hash, signature Signature) (common.Address, error) { 165 pub, err := crypto.SigToPub(digest.Bytes(), signature[:]) 166 if err != nil { 167 return common.Address{}, err 168 } 169 return crypto.PubkeyToAddress(*pub), nil 170 } 171 172 // verifyResourceOwnerhsip checks that the signer of the update actually owns the resource 173 // H(ownerAddr, metaHash) is computed. If it matches the rootAddr the update chunk is claiming 174 // to update, it is proven that signer of the resource update owns the resource. 175 // See metadataHash in metadata.go for a more detailed explanation 176 func verifyOwner(ownerAddr common.Address, metaHash []byte, rootAddr storage.Address) bool { 177 hasher := hashPool.Get().(hash.Hash) 178 defer hashPool.Put(hasher) 179 hasher.Reset() 180 hasher.Write(metaHash) 181 hasher.Write(ownerAddr.Bytes()) 182 rootAddr2 := hasher.Sum(nil) 183 return bytes.Equal(rootAddr2, rootAddr) 184 }