This repository has been archived by the owner on Sep 15, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
chacha20poly1305guard.go
181 lines (143 loc) · 4.34 KB
/
chacha20poly1305guard.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
181
// https://github.com/alexzava/chacha20poly1305guard
//
// Package chacha20poly1305guard implements the ChaCha20-Poly1305 AEAD
// and its extended nonce variant XChaCha20-Poly1305 with memguard
// in order to protect the key in memory.
//
// The code is based on https://github.com/codahale/chacha20poly1305
package chacha20poly1305guard
import (
"crypto/cipher"
"crypto/subtle"
"encoding/binary"
"errors"
"github.com/alexzava/chacha20guard"
"golang.org/x/crypto/poly1305"
"github.com/awnumar/memguard"
)
var (
// ErrAuthFailed is returned when the message authentication is invalid due
// to tampering.
ErrAuthFailed = errors.New("message authentication failed")
// ErrInvalidKey is returned when the provided key is the wrong size.
ErrInvalidKey = errors.New("invalid key size")
// ErrInvalidNonce is returned when the provided nonce is the wrong size.
ErrInvalidNonce = errors.New("invalid nonce size")
// KeySize is the required size of ChaCha20 keys.
KeySize = chacha20guard.KeySize
)
type chacha20poly1305 struct {
ek *memguard.LockedBuffer
isXChaCha bool
}
// NewX returns a XChaCha20Poly1305 AEAD.
// The key must be 256 bits long,
// and the nonce must be 192 bits long.
func NewX(key *memguard.LockedBuffer) (cipher.AEAD, error) {
if len(key.Buffer()) != KeySize {
return nil, ErrInvalidKey
}
k := new(chacha20poly1305)
k.ek = key
k.isXChaCha = true
return k, nil
}
// New returns a ChaCha20Poly1305 AEAD.
// The key must be 256 bits long,
// and the nonce must be 64 bits long.
// The nonce must be randomly generated or used only once.
func New(key *memguard.LockedBuffer) (cipher.AEAD, error) {
if len(key.Buffer()) != KeySize {
return nil, ErrInvalidKey
}
k := new(chacha20poly1305)
k.ek = key
k.isXChaCha = false
return k, nil
}
func (k *chacha20poly1305) NonceSize() int {
if k.isXChaCha {
return chacha20guard.XNonceSize
} else {
return chacha20guard.NonceSize
}
}
func (*chacha20poly1305) Overhead() int {
return poly1305.TagSize
}
func (k *chacha20poly1305) Seal(dst, nonce, plaintext, data []byte) []byte {
if len(nonce) != k.NonceSize() {
panic(ErrInvalidNonce)
}
var c cipher.Stream
var err error
if k.isXChaCha {
c, err = chacha20guard.NewX(k.ek, nonce)
if err != nil {
panic(err)
}
} else {
c, err = chacha20guard.New(k.ek, nonce)
if err != nil {
panic(err)
}
}
// Converts the given key and nonce into 64 bytes of ChaCha20 key stream, the
// first 32 of which are used as the Poly1305 key.
subkey := make([]byte, 64)
c.XORKeyStream(subkey, subkey)
var poly1305Key [32]byte
for i := 0; i < 32; i++ {
poly1305Key[i] = subkey[i]
}
ciphertext := make([]byte, len(plaintext))
c.XORKeyStream(ciphertext, plaintext)
tag := tag(poly1305Key, ciphertext, data)
return append(dst, append(ciphertext, tag...)...)
}
func (k *chacha20poly1305) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
if len(nonce) != k.NonceSize() {
panic(ErrInvalidNonce)
}
digest := ciphertext[len(ciphertext)-k.Overhead():]
ciphertext = ciphertext[0 : len(ciphertext)-k.Overhead()]
var c cipher.Stream
var err error
if k.isXChaCha {
c, err = chacha20guard.NewX(k.ek, nonce)
if err != nil {
panic(err)
}
} else {
c, err = chacha20guard.New(k.ek, nonce)
if err != nil {
panic(err)
}
}
// Converts the given key and nonce into 64 bytes of ChaCha20 key stream, the
// first 32 of which are used as the Poly1305 key.
subkey := make([]byte, 64)
c.XORKeyStream(subkey, subkey)
var poly1305Key [32]byte
for i := 0; i < 32; i++ {
poly1305Key[i] = subkey[i]
}
tag := tag(poly1305Key, ciphertext, data)
if subtle.ConstantTimeCompare(tag, digest) != 1 {
return nil, ErrAuthFailed
}
plaintext := make([]byte, len(ciphertext))
c.XORKeyStream(plaintext, ciphertext)
return append(dst, plaintext...), nil
}
func tag(key [32]byte, ciphertext, data []byte) []byte {
m := make([]byte, len(ciphertext)+len(data)+8+8)
copy(m[0:], data)
binary.LittleEndian.PutUint64(m[len(data):], uint64(len(data)))
copy(m[len(data)+8:], ciphertext)
binary.LittleEndian.PutUint64(m[len(data)+8+len(ciphertext):],
uint64(len(ciphertext)))
var out [poly1305.TagSize]byte
poly1305.Sum(&out, m, &key)
return out[0:]
}