github.com/iotexproject/iotex-core@v1.14.1-rc1/endorsement/endorsement.go (about) 1 // Copyright (c) 2019 IoTeX Foundation 2 // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability 3 // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed. 4 // This source code is governed by Apache License 2.0 that can be found in the LICENSE file. 5 6 package endorsement 7 8 import ( 9 "time" 10 11 "github.com/iotexproject/go-pkgs/crypto" 12 "github.com/iotexproject/go-pkgs/hash" 13 "github.com/iotexproject/iotex-proto/golang/iotextypes" 14 "google.golang.org/protobuf/types/known/timestamppb" 15 16 "github.com/iotexproject/iotex-core/pkg/util/byteutil" 17 ) 18 19 type ( 20 // Document defines a signable docuement 21 Document interface { 22 Hash() ([]byte, error) 23 } 24 25 // Endorsement defines an endorsement with timestamp 26 Endorsement struct { 27 ts time.Time 28 endorser crypto.PublicKey 29 signature []byte 30 } 31 32 // EndorsedDocument is an signed document 33 EndorsedDocument interface { 34 Document() Document 35 Endorsement() *Endorsement 36 } 37 ) 38 39 func hashDocWithTime(doc Document, ts time.Time) ([]byte, error) { 40 h, err := doc.Hash() 41 if err != nil { 42 return nil, err 43 } 44 h = append(h, byteutil.Uint64ToBytes(uint64(ts.Unix()))...) 45 h256 := hash.Hash256b(append(h, byteutil.Uint32ToBytes(uint32(ts.Nanosecond()))...)) 46 47 return h256[:], nil 48 } 49 50 // NewEndorsement creates a new Endorsement 51 func NewEndorsement( 52 ts time.Time, 53 endorserPubKey crypto.PublicKey, 54 sig []byte, 55 ) *Endorsement { 56 cs := make([]byte, len(sig)) 57 copy(cs, sig) 58 return &Endorsement{ 59 ts: ts.UTC(), 60 endorser: endorserPubKey, 61 signature: cs, 62 } 63 } 64 65 // Endorse endorses a document 66 func Endorse( 67 signer crypto.PrivateKey, 68 doc Document, 69 ts time.Time, 70 ) (*Endorsement, error) { 71 hash, err := hashDocWithTime(doc, ts) 72 if err != nil { 73 return nil, err 74 } 75 sig, err := signer.Sign(hash) 76 if err != nil { 77 return nil, err 78 } 79 return NewEndorsement(ts, signer.PublicKey(), sig), nil 80 } 81 82 // VerifyEndorsedDocument checks an endorsed document 83 func VerifyEndorsedDocument(endorsedDoc EndorsedDocument) bool { 84 return VerifyEndorsement(endorsedDoc.Document(), endorsedDoc.Endorsement()) 85 } 86 87 // VerifyEndorsement checks the signature in an endorsement against a document 88 func VerifyEndorsement(doc Document, en *Endorsement) bool { 89 hash, err := hashDocWithTime(doc, en.Timestamp()) 90 if err != nil { 91 return false 92 } 93 94 return en.Endorser().Verify(hash, en.Signature()) 95 } 96 97 // Timestamp returns the signature time 98 func (en *Endorsement) Timestamp() time.Time { 99 return en.ts 100 } 101 102 // Endorser returns the endorser's public key 103 func (en *Endorsement) Endorser() crypto.PublicKey { 104 return en.endorser 105 } 106 107 // Signature returns the signature of this endorsement 108 func (en *Endorsement) Signature() []byte { 109 signature := make([]byte, len(en.signature)) 110 copy(signature, en.signature) 111 112 return signature 113 } 114 115 // Proto converts an endorsement to protobuf message 116 func (en *Endorsement) Proto() (*iotextypes.Endorsement, error) { 117 ts := timestamppb.New(en.ts) 118 return &iotextypes.Endorsement{ 119 Timestamp: ts, 120 Endorser: en.endorser.Bytes(), 121 Signature: en.Signature(), 122 }, nil 123 } 124 125 // LoadProto converts a protobuf message to endorsement 126 func (en *Endorsement) LoadProto(ePb *iotextypes.Endorsement) (err error) { 127 if err = ePb.Timestamp.CheckValid(); err != nil { 128 return err 129 } 130 en.ts = ePb.Timestamp.AsTime() 131 eb := make([]byte, len(ePb.Endorser)) 132 copy(eb, ePb.Endorser) 133 if en.endorser, err = crypto.BytesToPublicKey(eb); err != nil { 134 return err 135 } 136 en.signature = make([]byte, len(ePb.Signature)) 137 copy(en.signature, ePb.Signature) 138 139 return nil 140 }