-
Notifications
You must be signed in to change notification settings - Fork 4
/
xsecretbox.go
109 lines (90 loc) · 2.67 KB
/
xsecretbox.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
package unclassified
import (
"errors"
)
const (
KeySize = 32
NonceSize = 24
TagSize = poly1305_TagSize
)
// SealX appends an encrypted and authenticated copy of message to out, which
// must not overlap message. The key and nonce pair must be unique for each
// distinct message and the output will be Overhead bytes longer than message.
func SealX(out, nonce, message, key []byte) []byte {
if len(nonce) != NonceSize {
panic("unsupported nonce size")
}
if len(key) != KeySize {
panic("unsupported key size")
}
var firstBlock [64]byte
cipher, _ := chacha20_NewUnauthenticatedCipher(key, nonce)
cipher.XORKeyStream(firstBlock[:], firstBlock[:])
var polyKey [KeySize]byte
copy(polyKey[:], firstBlock[:KeySize])
ret, out := sliceForAppend(out, TagSize+len(message))
if anyOverlap(out, message) {
panic("nacl: invalid buffer overlap")
}
firstMessageBlock := message
if len(firstMessageBlock) > KeySize {
firstMessageBlock = firstMessageBlock[:KeySize]
}
tagOut := out
out = out[TagSize:]
for i, x := range firstMessageBlock {
out[i] = firstBlock[KeySize+i] ^ x
}
message = message[len(firstMessageBlock):]
ciphertext := out
out = out[len(firstMessageBlock):]
cipher.SetCounter(1)
cipher.XORKeyStream(out, message)
var tag [TagSize]byte
hash := poly1305_New(&polyKey)
hash.Write(ciphertext)
hash.Sum(tag[:0])
copy(tagOut, tag[:])
return ret
}
// OpenX authenticates and decrypts a box produced by SealX and appends the
// message to out, which must not overlap box. The output will be Overhead
// bytes smaller than box.
func OpenX(out, nonce, box, key []byte) ([]byte, error) {
if len(nonce) != NonceSize {
panic("unsupported nonce size")
}
if len(key) != KeySize {
panic("unsupported key size")
}
if len(box) < Overhead {
return nil, errors.New("ciphertext is too short")
}
var firstBlock [64]byte
cipher, _ := chacha20_NewUnauthenticatedCipher(key, nonce)
cipher.XORKeyStream(firstBlock[:], firstBlock[:])
var polyKey [KeySize]byte
copy(polyKey[:], firstBlock[:KeySize])
ciphertext := box[Overhead:]
hash := poly1305_New(&polyKey)
hash.Write(ciphertext)
if !hash.Verify(box[:Overhead]) {
return nil, errors.New("OpenX:ciphertext authentication failed")
}
ret, out := sliceForAppend(out, len(ciphertext))
if anyOverlap(out, box) {
panic("nacl: invalid buffer overlap")
}
firstMessageBlock := ciphertext
if len(firstMessageBlock) > KeySize {
firstMessageBlock = firstMessageBlock[:KeySize]
}
for i, x := range firstMessageBlock {
out[i] = firstBlock[KeySize+i] ^ x
}
ciphertext = ciphertext[len(firstMessageBlock):]
out = out[len(firstMessageBlock):]
cipher.SetCounter(1)
cipher.XORKeyStream(out, ciphertext)
return ret, nil
}