-
Notifications
You must be signed in to change notification settings - Fork 211
/
crypto.go
164 lines (135 loc) · 3.6 KB
/
crypto.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
package p2pcrypto
import (
"crypto/rand"
"errors"
"fmt"
"github.com/btcsuite/btcutil/base58"
"github.com/spacemeshos/go-spacemesh/log"
"golang.org/x/crypto/nacl/box"
"io"
)
const (
keySize = 32 // non-configurable, expected by NaCl
nonceSize = 24 // non-configurable, expected by NaCl
)
type Key interface {
Bytes() []byte
raw() *[keySize]byte
Array() [32]byte
String() string
}
type PrivateKey interface {
Key
}
type PublicKey interface {
Key
}
type SharedSecret interface {
Key
Seal(message []byte) (out []byte)
Open(encryptedMessage []byte) (out []byte, err error)
}
type key struct {
bytes [keySize]byte
}
var _ PrivateKey = (*key)(nil)
var _ PublicKey = (*key)(nil)
var _ SharedSecret = (*key)(nil)
func (k key) raw() *[keySize]byte {
return &k.bytes
}
func (k key) Array() [32]byte {
return k.bytes
}
func (k key) Bytes() []byte {
return k.bytes[:]
}
func (k key) String() string {
return base58.Encode(k.Bytes())
}
func getRandomNonce() [nonceSize]byte {
nonce := [nonceSize]byte{}
if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil {
log.Panic("Panic: ", err)
}
return nonce
}
func (k key) Seal(message []byte) (out []byte) {
nonce := getRandomNonce() // TODO: @noam replace with counter to prevent replays
return box.SealAfterPrecomputation(nonce[:], message, &nonce, k.raw())
}
func (k key) Open(encryptedMessage []byte) (out []byte, err error) {
if len(encryptedMessage) <= nonceSize {
return nil, errors.New("message was too small")
}
nonce := &[nonceSize]byte{}
copy(nonce[:], encryptedMessage[:nonceSize])
message, ok := box.OpenAfterPrecomputation(nil, encryptedMessage[nonceSize:], nonce, k.raw())
if !ok {
return nil, errors.New("opening boxed message failed")
}
return message, nil
}
func GenerateKeyPair() (PrivateKey, PublicKey, error) {
public, private, err := box.GenerateKey(rand.Reader)
if err != nil {
return nil, nil, err
}
return key{*private}, key{*public}, nil
}
func GenerateSharedSecret(privkey PrivateKey, peerPubkey PublicKey) SharedSecret {
sharedSecret := newKey()
box.Precompute(sharedSecret.raw(), peerPubkey.raw(), privkey.raw())
return sharedSecret
}
func PrependPubkey(message []byte, pubkey PublicKey) []byte {
return append(pubkey.Bytes(), message...)
}
func ExtractPubkey(message []byte) ([]byte, PublicKey, error) {
if mSize := len(message); mSize <= keySize {
return nil, nil, fmt.Errorf("cannot extract pubkey of size %d from message of size %d", keySize, mSize)
}
pubkey, err := NewPubkeyFromBytes(message[:keySize])
if err != nil {
log.Panic("Panic: ", err) // this should never happen as we control the key size
}
return message[keySize:], pubkey, nil
}
func newKey() key {
return key{[keySize]byte{}}
}
var nilKey key
func newKeyFromBytes(bytes []byte) (key, error) {
if l := len(bytes); l != keySize {
return nilKey, fmt.Errorf("invalid key size (got %v instead of %v bytes)", l, keySize)
}
k := newKey()
copy(k.bytes[:], bytes)
return k, nil
}
func NewPubkeyFromBytes(bytes []byte) (PublicKey, error) {
return newKeyFromBytes(bytes)
}
func newKeyFromBase58(s string) (key, error) {
bytes := base58.Decode(s)
if len(bytes) == 0 {
return nilKey, errors.New("unable to decode key")
}
return newKeyFromBytes(bytes)
}
func NewPrivateKeyFromBase58(s string) (PrivateKey, error) {
return newKeyFromBase58(s)
}
func NewPublicKeyFromBase58(s string) (PublicKey, error) {
return newKeyFromBase58(s)
}
func NewRandomPubkey() PublicKey {
k := newKey()
if _, err := io.ReadFull(rand.Reader, k.bytes[:]); err != nil {
log.Panic("Panic: ", err)
}
return k
}
func PublicKeyFromArray(ke [32]byte) PublicKey {
return key{ke}
}