github.com/FUSIONFoundation/efsn@v3.6.2-0.20200916075423-dbb5dd5d2cc7+incompatible/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/FusionFoundation/efsn/common"
    24  	"github.com/FusionFoundation/efsn/crypto"
    25  	"github.com/FusionFoundation/efsn/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  	resourceUpdateLength := r.resourceUpdate.binaryLength()
   109  	// signature is the last item in the chunk data
   110  	copy(r.binaryData[resourceUpdateLength:], r.signature[:])
   111  
   112  	chunk := storage.NewChunk(r.updateAddr, r.binaryData)
   113  	return chunk, nil
   114  }
   115  
   116  // fromChunk populates this structure from chunk data. It does not verify the signature is valid.
   117  func (r *SignedResourceUpdate) fromChunk(updateAddr storage.Address, chunkdata []byte) error {
   118  	// for update chunk layout see SignedResourceUpdate definition
   119  
   120  	//deserialize the resource update portion
   121  	if err := r.resourceUpdate.binaryGet(chunkdata); err != nil {
   122  		return err
   123  	}
   124  
   125  	// Extract the signature
   126  	var signature *Signature
   127  	cursor := r.resourceUpdate.binaryLength()
   128  	sigdata := chunkdata[cursor : cursor+signatureLength]
   129  	if len(sigdata) > 0 {
   130  		signature = &Signature{}
   131  		copy(signature[:], sigdata)
   132  	}
   133  
   134  	r.signature = signature
   135  	r.updateAddr = updateAddr
   136  	r.binaryData = chunkdata
   137  
   138  	return nil
   139  
   140  }
   141  
   142  // GetDigest creates the resource update digest used in signatures (formerly known as keyDataHash)
   143  // the serialized payload is cached in .binaryData
   144  func (r *SignedResourceUpdate) GetDigest() (result common.Hash, err error) {
   145  	hasher := hashPool.Get().(hash.Hash)
   146  	defer hashPool.Put(hasher)
   147  	hasher.Reset()
   148  	dataLength := r.resourceUpdate.binaryLength()
   149  	if r.binaryData == nil {
   150  		r.binaryData = make([]byte, dataLength+signatureLength)
   151  		if err := r.resourceUpdate.binaryPut(r.binaryData[:dataLength]); err != nil {
   152  			return result, err
   153  		}
   154  	}
   155  	hasher.Write(r.binaryData[:dataLength]) //everything except the signature.
   156  
   157  	return common.BytesToHash(hasher.Sum(nil)), nil
   158  }
   159  
   160  // getOwner extracts the address of the resource update signer
   161  func getOwner(digest common.Hash, signature Signature) (common.Address, error) {
   162  	pub, err := crypto.SigToPub(digest.Bytes(), signature[:])
   163  	if err != nil {
   164  		return common.Address{}, err
   165  	}
   166  	return crypto.PubkeyToAddress(*pub), nil
   167  }
   168  
   169  // verifyResourceOwnerhsip checks that the signer of the update actually owns the resource
   170  // H(ownerAddr, metaHash) is computed. If it matches the rootAddr the update chunk is claiming
   171  // to update, it is proven that signer of the resource update owns the resource.
   172  // See metadataHash in metadata.go for a more detailed explanation
   173  func verifyOwner(ownerAddr common.Address, metaHash []byte, rootAddr storage.Address) bool {
   174  	hasher := hashPool.Get().(hash.Hash)
   175  	defer hashPool.Put(hasher)
   176  	hasher.Reset()
   177  	hasher.Write(metaHash)
   178  	hasher.Write(ownerAddr.Bytes())
   179  	rootAddr2 := hasher.Sum(nil)
   180  	return bytes.Equal(rootAddr2, rootAddr)
   181  }