-
Notifications
You must be signed in to change notification settings - Fork 0
/
dh.go
111 lines (99 loc) · 3.27 KB
/
dh.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
package dh
// package dh implements the Diffie-Hellman secret exchange defined on RFC 7748
// using the X25519 function
// (begin of quote)
// Alice generates 32 random bytes in a[0] to a[31] and transmits K_A =
// X25519(a, 9) to Bob, where 9 is the u-coordinate of the base point
// and is encoded as a byte with value 9, followed by 31 zero bytes.
//
// Bob similarly generates 32 random bytes in b[0] to b[31], computes
// K_B = X25519(b, 9), and transmits it to Alice.
//
// Using their generated values and the received input, Alice computes
// X25519(a, K_B) and Bob computes X25519(b, K_A).
//
// Both now share K = X25519(a, X25519(b, 9)) = X25519(b, X25519(a, 9))
// as a shared secret. Both MAY check, without leaking extra
// information about the value of K, whether K is the all-zero value and
// abort if so (see below). Alice and Bob can then use a key-derivation
// function that includes K, K_A, and K_B to derive a symmetric key.
//
// The check for the all-zero value results from the fact that the
// X25519 function produces that value if it operates on an input
// corresponding to a point with small order, where the order divides
// the cofactor of the curve (see Section 7). The check may be
// performed by ORing all the bytes together and checking whether the
// result is zero, as this eliminates standard side-channels in software
// implementations.
// (end of quote)
// the SHA256 hash on the agreed key is used as a key for an AES 256 Cipher.
import (
"math/rand"
"github.com/freehandle/breeze/crypto"
"github.com/freehandle/breeze/crypto/dh/curve25519"
)
type Party struct {
key []byte
keyX []byte
agreedKey []byte
}
func NewEphemeralKey() (crypto.PrivateKey, crypto.Token) {
var pubToken crypto.Token
var prvToken crypto.PrivateKey
rand.Read(prvToken[0:32])
pub, err := curve25519.X25519(prvToken[0:32], curve25519.Basepoint)
if err == nil {
copy(prvToken[32:], pub)
copy(pubToken[:], pub)
}
return prvToken, pubToken
}
func ConsensusKey(local crypto.PrivateKey, remote crypto.Token) []byte {
agreedKey, err := curve25519.X25519(local[0:32], remote[:])
if err != nil {
return nil
}
hashed := crypto.Hasher(agreedKey)
return hashed[:]
}
func ConsensusCipher(local crypto.PrivateKey, remote crypto.Token) crypto.Cipher {
return crypto.CipherFromKey(ConsensusKey(local, remote))
}
func NewEphemeralRequest() *Party {
rnd := make([]byte, 32)
rand.Read(rnd)
rndX, err := curve25519.X25519(rnd, curve25519.Basepoint)
if err != nil {
return nil
}
return &Party{key: rnd, keyX: rndX}
}
func NewEphemeralResponse(aliceKeyX []byte) *Party {
rnd := make([]byte, 32)
rand.Read(rnd)
rndX, err := curve25519.X25519(rnd, curve25519.Basepoint)
if err != nil {
return nil
}
agreedKey, err := curve25519.X25519(rnd, aliceKeyX)
if err != nil {
return nil
}
return &Party{key: rnd, keyX: rndX, agreedKey: agreedKey}
}
func (p *Party) IncorporateResponse(otherKeyX []byte) bool {
agreedKey, err := curve25519.X25519(p.key, otherKeyX)
if err != nil {
return false
}
p.agreedKey = agreedKey
return true
}
func (p *Party) Cipher() crypto.Cipher {
hashed := crypto.Hasher(p.agreedKey)
return crypto.CipherFromKey(hashed[:])
}
func (p *Party) CipherNonce() crypto.CipherNonce {
hashed := crypto.Hasher(p.agreedKey)
return crypto.CipherNonceFromKey(hashed[:])
}