forked from aergoio/aergo
/
signature.go
134 lines (114 loc) · 3.5 KB
/
signature.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/**
* @file
* @copyright defined in aergo/LICENSE.txt
*/
package p2p
import (
"fmt"
"github.com/aergoio/aergo/p2p/p2pcommon"
"github.com/aergoio/aergo/types"
"github.com/golang/protobuf/proto"
crypto "github.com/libp2p/go-libp2p-crypto"
peer "github.com/libp2p/go-libp2p-peer"
)
type defaultMsgSigner struct {
selfPeerID peer.ID
privateKey crypto.PrivKey
pubKey crypto.PubKey
pidBytes []byte
pubKeyBytes []byte
}
func newDefaultMsgSigner(privKey crypto.PrivKey, pubKey crypto.PubKey, peerID peer.ID) p2pcommon.MsgSigner {
pidBytes := []byte(peerID)
pubKeyBytes, _ := pubKey.Bytes()
return &defaultMsgSigner{selfPeerID: peerID, privateKey: privKey, pubKey: pubKey, pidBytes: pidBytes, pubKeyBytes: pubKeyBytes}
}
// sign an outgoing p2p message payload
func (pm *defaultMsgSigner) SignMsg(message *types.P2PMessage) error {
// TODO this code modify caller's parameter.
message.Header.PeerID = pm.pidBytes
message.Header.NodePubKey = pm.pubKeyBytes
data, err := proto.Marshal(&types.P2PMessage{Header: canonicalizeHeader(message.Header), Data: message.Data})
if err != nil {
return err
}
signature, err := pm.signBytes(data)
if err != nil {
return err
}
message.Header.Sign = signature
return nil
}
func canonicalizeHeader(src *types.MsgHeader) *types.MsgHeader {
// copy fields excluding generated fields and signature itself
return &types.MsgHeader{
ClientVersion: src.ClientVersion,
Gossip: src.Gossip,
Id: src.Id,
Length: src.Length,
NodePubKey: src.NodePubKey,
PeerID: src.PeerID,
Sign: nil,
Subprotocol: src.Subprotocol,
Timestamp: src.Timestamp,
}
}
// sign binary data using the local node's private key
func (pm *defaultMsgSigner) signBytes(data []byte) ([]byte, error) {
key := pm.privateKey
res, err := key.Sign(data)
return res, err
}
func (pm *defaultMsgSigner) VerifyMsg(msg *types.P2PMessage, senderID peer.ID) error {
// check signature
pubKey, err := crypto.UnmarshalPublicKey(msg.Header.NodePubKey)
if err != nil {
return err
}
signature := msg.Header.Sign
checkOrigin := false
if checkOrigin {
// TODO it can be needed, and if that modify code to get peerid from caller and enable this code
if err := checkPidWithPubkey(senderID, pubKey); err != nil {
return err
}
}
data, _ := proto.Marshal(&types.P2PMessage{Header: canonicalizeHeader(msg.Header), Data: msg.Data})
return verifyBytes(data, signature, pubKey)
}
func checkPidWithPubkey(peerID peer.ID, pubKey crypto.PubKey) error {
// extract node peer.ID from the provided public key
idFromKey, err := peer.IDFromPublicKey(pubKey)
if err != nil {
return err
}
// verify that message author node peer.ID matches the provided node public key
if idFromKey != peerID {
return fmt.Errorf("PeerID mismatch")
}
return nil
}
// VerifyData Verifies incoming p2p message data integrity
// data: data to verify
// signature: author signature provided in the message payload
// pubkey: author public key from the message payload
func verifyBytes(data []byte, signature []byte, pubkey crypto.PubKey) error {
res, err := pubkey.Verify(data, signature)
if err != nil {
return err
}
if !res {
return fmt.Errorf("signature mismatch")
}
return nil
}
var dummyBytes = []byte{}
type dummySigner struct{}
func (d *dummySigner) SignMsg(msg *types.P2PMessage) error {
msg.Header.Sign = dummyBytes
return nil
}
func (d *dummySigner) VerifyMsg(msg *types.P2PMessage, senderID peer.ID) error {
return nil
}
var _ p2pcommon.MsgSigner = (*dummySigner)(nil)