This repository has been archived by the owner on Oct 1, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 4
/
hsm.go
180 lines (145 loc) · 4.67 KB
/
hsm.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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
package aiakos
import (
"errors"
"github.com/certusone/yubihsm-go"
"github.com/certusone/yubihsm-go/commands"
"github.com/certusone/yubihsm-go/connector"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/types"
)
type (
// AiakosPV implements PrivValidator using Aiakos
AiakosPV struct {
hsmSessionManager *yubihsm.SessionManager
hsmURL string
authKeyID uint16
password string
signingKeyID uint16
cachedPubKey crypto.PubKey
cmn.BaseService
}
)
// NewAiakosPV returns a new instance of AiakosPV
func NewAiakosPV(hsmURL string, signingKeyID uint16, authKeyID uint16, password string, logger log.Logger) (*AiakosPV, error) {
pv := &AiakosPV{
hsmURL: hsmURL,
authKeyID: authKeyID,
password: password,
signingKeyID: signingKeyID,
}
pv.BaseService = *cmn.NewBaseService(logger, "AiakosPV", pv)
return pv, nil
}
// OnStart implements cmn.Service.
func (a *AiakosPV) OnStart() error {
sessionManager, err := yubihsm.NewSessionManager(connector.NewHTTPConnector(a.hsmURL), a.authKeyID, a.password)
if err != nil {
return err
}
a.hsmSessionManager = sessionManager
return nil
}
// OnStop implements cmn.Service.
func (a *AiakosPV) OnStop() {
a.hsmSessionManager.Destroy()
}
// GetPubKey returns the public key of the validator.
// Implements PrivValidator.
func (a *AiakosPV) GetPubKey() crypto.PubKey {
if a.cachedPubKey != nil {
return a.cachedPubKey
}
command, err := commands.CreateGetPubKeyCommand(a.signingKeyID)
if err != nil {
panic(err)
}
resp, err := a.hsmSessionManager.SendEncryptedCommand(command)
if err != nil {
panic(err)
}
parsedResp, matched := resp.(*commands.GetPubKeyResponse)
if !matched {
a.Logger.Error("invalid response type")
panic(errors.New("invalid response type"))
}
if parsedResp.Algorithm != commands.AlgorighmED25519 {
a.Logger.Error("invalid response type", "algorithm", parsedResp.Algorithm)
panic(errors.New("invalid pubKey algorithm"))
}
if len(parsedResp.KeyData) != ed25519.PubKeyEd25519Size {
a.Logger.Error("invalid pubKey size", "size", len(parsedResp.KeyData))
panic(errors.New("invalid pubKey size"))
}
// Convert raw key data to tendermint PubKey type
publicKey := new(ed25519.PubKeyEd25519)
copy(publicKey[:], parsedResp.KeyData[:])
// Cache publicKey
a.cachedPubKey = publicKey
return publicKey
}
// SignVote signs a canonical representation of the vote, along with the
// chainID. Implements PrivValidator.
func (a *AiakosPV) SignVote(chainID string, vote *types.Vote) error {
signature, err := a.signBytes(vote.SignBytes(chainID))
if err != nil {
return err
}
vote.Signature = signature
return nil
}
// SignProposal signs a canonical representation of the proposal, along with
// the chainID. Implements PrivValidator.
func (a *AiakosPV) SignProposal(chainID string, proposal *types.Proposal) error {
signature, err := a.signBytes(proposal.SignBytes(chainID))
if err != nil {
return err
}
proposal.Signature = signature
return nil
}
// ImportKey imports a Eddsa private key to the specified key slot on the HSM.
// This fails if the key slot already contains a key.
// This should be used for testing purposes only. Wrap and import keys in production.
func (a *AiakosPV) ImportKey(keyID uint16, key []byte) error {
command, err := commands.CreatePutAsymmetricKeyCommand(keyID, []byte("imported"), commands.Domain1, commands.CapabilityAsymmetricSignEddsa, commands.AlgorighmED25519, key, []byte{})
if err != nil {
return err
}
resp, err := a.hsmSessionManager.SendEncryptedCommand(command)
if err != nil {
return err
}
parsedResp, matched := resp.(*commands.PutAsymmetricKeyResponse)
if !matched {
a.Logger.Error("invalid response type")
return errors.New("invalid response type")
}
if parsedResp.KeyID != keyID {
a.Logger.Error("imported KeyID mismatch")
return errors.New("imported KeyID mismatch")
}
return nil
}
func (a *AiakosPV) signBytes(data []byte) ([]byte, error) {
command, err := commands.CreateSignDataEddsaCommand(a.signingKeyID, data)
if err != nil {
return nil, err
}
resp, err := a.hsmSessionManager.SendEncryptedCommand(command)
if err != nil {
return nil, err
}
parsedResp, matched := resp.(*commands.SignDataEddsaResponse)
if !matched {
a.Logger.Error("invalid response type")
return nil, errors.New("invalid response type")
}
if len(parsedResp.Signature) != ed25519.SignatureSize {
a.Logger.Error("invalid signature length", "size", len(parsedResp.Signature))
return nil, errors.New("invalid signature length")
}
return parsedResp.Signature, nil
}