1// Package crypto is a wrapper around packages under golang.org/x/crypto/, particulaly curve25519, ed25519, and nacl/box.
2// This is used to avoid explicitly importing and using these packages throughout yggdrasil.
3// It also includes the all-important NodeID and TreeID types, which are used to identify nodes in the DHT and in the spanning tree's root selection algorithm, respectively.
4package crypto
5
6/*
7
8This part of the package wraps crypto operations needed elsewhere
9
10In particular, it exposes key generation for ed25519 and nacl box
11
12It also defines NodeID and TreeID as hashes of keys, and wraps hash functions
13
14*/
15
16import (
17	"crypto/rand"
18	"crypto/sha512"
19	"encoding/hex"
20
21	"golang.org/x/crypto/curve25519"
22	"golang.org/x/crypto/ed25519"
23	"golang.org/x/crypto/nacl/box"
24
25	"github.com/yggdrasil-network/yggdrasil-go/src/util"
26)
27
28////////////////////////////////////////////////////////////////////////////////
29
30// NodeID and TreeID
31
32// NodeIDLen is the length (in bytes) of a NodeID.
33const NodeIDLen = sha512.Size
34
35// TreeIDLen is the length (in bytes) of a TreeID.
36const TreeIDLen = sha512.Size
37
38// handleLen is the length (in bytes) of a Handle.
39const handleLen = 8
40
41// NodeID is how a yggdrasil node is identified in the DHT, and is used to derive IPv6 addresses and subnets in the main executable. It is a sha512sum hash of the node's BoxPubKey
42type NodeID [NodeIDLen]byte
43
44// TreeID is how a yggdrasil node is identified in the root selection algorithm used to construct the spanning tree.
45type TreeID [TreeIDLen]byte
46
47type Handle [handleLen]byte
48
49func (n *NodeID) String() string {
50	return hex.EncodeToString(n[:])
51}
52
53// Network returns "nodeid" nearly always right now.
54func (n *NodeID) Network() string {
55	return "nodeid"
56}
57
58// PrefixLength returns the number of bits set in a masked NodeID.
59func (n *NodeID) PrefixLength() int {
60	var len int
61	for i, v := range *n {
62		_, _ = i, v
63		if v == 0xff {
64			len += 8
65			continue
66		}
67		for v&0x80 != 0 {
68			len++
69			v <<= 1
70		}
71		if v != 0 {
72			return -1
73		}
74		for i++; i < NodeIDLen; i++ {
75			if n[i] != 0 {
76				return -1
77			}
78		}
79		break
80	}
81	return len
82}
83
84// GetNodeID returns the NodeID associated with a BoxPubKey.
85func GetNodeID(pub *BoxPubKey) *NodeID {
86	h := sha512.Sum512(pub[:])
87	return (*NodeID)(&h)
88}
89
90// GetTreeID returns the TreeID associated with a BoxPubKey
91func GetTreeID(pub *SigPubKey) *TreeID {
92	h := sha512.Sum512(pub[:])
93	return (*TreeID)(&h)
94}
95
96// NewHandle returns a new (cryptographically random) Handle, used by the session code to identify which session an incoming packet is associated with.
97func NewHandle() *Handle {
98	var h Handle
99	_, err := rand.Read(h[:])
100	if err != nil {
101		panic(err)
102	}
103	return &h
104}
105
106////////////////////////////////////////////////////////////////////////////////
107
108// Signatures
109
110// SigPubKeyLen is the length of a SigPubKey in bytes.
111const SigPubKeyLen = ed25519.PublicKeySize
112
113// SigPrivKeyLen is the length of a SigPrivKey in bytes.
114const SigPrivKeyLen = ed25519.PrivateKeySize
115
116// SigLen is the length of SigBytes.
117const SigLen = ed25519.SignatureSize
118
119// SigPubKey is a public ed25519 signing key.
120type SigPubKey [SigPubKeyLen]byte
121
122// SigPrivKey is a private ed25519 signing key.
123type SigPrivKey [SigPrivKeyLen]byte
124
125// SigBytes is an ed25519 signature.
126type SigBytes [SigLen]byte
127
128// NewSigKeys generates a public/private ed25519 key pair.
129func NewSigKeys() (*SigPubKey, *SigPrivKey) {
130	var pub SigPubKey
131	var priv SigPrivKey
132	pubSlice, privSlice, err := ed25519.GenerateKey(rand.Reader)
133	if err != nil {
134		panic(err)
135	}
136	copy(pub[:], pubSlice)
137	copy(priv[:], privSlice)
138	return &pub, &priv
139}
140
141// Sign returns the SigBytes signing a message.
142func Sign(priv *SigPrivKey, msg []byte) *SigBytes {
143	var sig SigBytes
144	sigSlice := ed25519.Sign(priv[:], msg)
145	copy(sig[:], sigSlice)
146	return &sig
147}
148
149// Verify returns true if the provided signature matches the key and message.
150func Verify(pub *SigPubKey, msg []byte, sig *SigBytes) bool {
151	// Should sig be an array instead of a slice?...
152	// It's fixed size, but
153	return ed25519.Verify(pub[:], msg, sig[:])
154}
155
156// Public returns the SigPubKey associated with this SigPrivKey.
157func (p SigPrivKey) Public() SigPubKey {
158	priv := make(ed25519.PrivateKey, ed25519.PrivateKeySize)
159	copy(priv[:], p[:])
160	pub := priv.Public().(ed25519.PublicKey)
161	var sigPub SigPubKey
162	copy(sigPub[:], pub[:])
163	return sigPub
164}
165
166////////////////////////////////////////////////////////////////////////////////
167
168// NaCl-like crypto "box" (curve25519+xsalsa20+poly1305)
169
170// BoxPubKeyLen is the length of a BoxPubKey in bytes.
171const BoxPubKeyLen = 32
172
173// BoxPrivKeyLen is the length of a BoxPrivKey in bytes.
174const BoxPrivKeyLen = 32
175
176// BoxSharedKeyLen is the length of a BoxSharedKey in bytes.
177const BoxSharedKeyLen = 32
178
179// BoxNonceLen is the length of a BoxNonce in bytes.
180const BoxNonceLen = 24
181
182// BoxOverhead is the length of the overhead from boxing something.
183const BoxOverhead = box.Overhead
184
185// BoxPubKey is a NaCl-like "box" public key (curve25519+xsalsa20+poly1305).
186type BoxPubKey [BoxPubKeyLen]byte
187
188// BoxPrivKey is a NaCl-like "box" private key (curve25519+xsalsa20+poly1305).
189type BoxPrivKey [BoxPrivKeyLen]byte
190
191// BoxSharedKey is a NaCl-like "box" shared key (curve25519+xsalsa20+poly1305).
192type BoxSharedKey [BoxSharedKeyLen]byte
193
194// BoxNonce is the nonce used in NaCl-like crypto "box" operations (curve25519+xsalsa20+poly1305), and must not be reused for different messages encrypted using the same BoxSharedKey.
195type BoxNonce [BoxNonceLen]byte
196
197// NewBoxKeys generates a new pair of public/private crypto box keys.
198func NewBoxKeys() (*BoxPubKey, *BoxPrivKey) {
199	pubBytes, privBytes, err := box.GenerateKey(rand.Reader)
200	if err != nil {
201		panic(err)
202	}
203	pub := (*BoxPubKey)(pubBytes)
204	priv := (*BoxPrivKey)(privBytes)
205	return pub, priv
206}
207
208// GetSharedKey returns the shared key derived from your private key and the destination's public key.
209func GetSharedKey(myPrivKey *BoxPrivKey,
210	othersPubKey *BoxPubKey) *BoxSharedKey {
211	var shared [BoxSharedKeyLen]byte
212	priv := (*[BoxPrivKeyLen]byte)(myPrivKey)
213	pub := (*[BoxPubKeyLen]byte)(othersPubKey)
214	box.Precompute(&shared, pub, priv)
215	return (*BoxSharedKey)(&shared)
216}
217
218// BoxOpen returns a message and true if it successfull opens a crypto box using the provided shared key and nonce.
219func BoxOpen(shared *BoxSharedKey,
220	boxed []byte,
221	nonce *BoxNonce) ([]byte, bool) {
222	out := util.GetBytes()
223	s := (*[BoxSharedKeyLen]byte)(shared)
224	n := (*[BoxNonceLen]byte)(nonce)
225	unboxed, success := box.OpenAfterPrecomputation(out, boxed, n, s)
226	return unboxed, success
227}
228
229// BoxSeal seals a crypto box using the provided shared key, returning the box and the nonce needed to decrypt it.
230// If nonce is nil, a random BoxNonce will be used and returned.
231// If nonce is non-nil, then nonce.Increment() will be called before using it, and the incremented BoxNonce is what is returned.
232func BoxSeal(shared *BoxSharedKey, unboxed []byte, nonce *BoxNonce) ([]byte, *BoxNonce) {
233	if nonce == nil {
234		nonce = NewBoxNonce()
235	}
236	nonce.Increment()
237	out := util.GetBytes()
238	s := (*[BoxSharedKeyLen]byte)(shared)
239	n := (*[BoxNonceLen]byte)(nonce)
240	boxed := box.SealAfterPrecomputation(out, unboxed, n, s)
241	return boxed, nonce
242}
243
244// NewBoxNonce generates a (cryptographically) random BoxNonce.
245func NewBoxNonce() *BoxNonce {
246	var nonce BoxNonce
247	_, err := rand.Read(nonce[:])
248	for ; err == nil && nonce[0] == 0xff; _, err = rand.Read(nonce[:]) {
249		// Make sure nonce isn't too high
250		// This is just to make rollover unlikely to happen
251		// Rollover is fine, but it may kill the session and force it to reopen
252	}
253	if err != nil {
254		panic(err)
255	}
256	return &nonce
257}
258
259// Increment adds 2 to a BoxNonce, which is useful if one node intends to send only with odd BoxNonce values, and the other only with even BoxNonce values.
260func (n *BoxNonce) Increment() {
261	oldNonce := *n
262	n[len(n)-1] += 2
263	for i := len(n) - 2; i >= 0; i-- {
264		if n[i+1] < oldNonce[i+1] {
265			n[i] += 1
266		}
267	}
268}
269
270// Public returns the BoxPubKey associated with this BoxPrivKey.
271func (p BoxPrivKey) Public() BoxPubKey {
272	var boxPub [BoxPubKeyLen]byte
273	var boxPriv [BoxPrivKeyLen]byte
274	copy(boxPriv[:BoxPrivKeyLen], p[:BoxPrivKeyLen])
275	curve25519.ScalarBaseMult(&boxPub, &boxPriv)
276	return boxPub
277}
278
279// Minus is the result of subtracting the provided BoNonce from this BoxNonce, bounded at +- 64.
280// It's primarily used to determine if a new BoxNonce is higher than the last known BoxNonce from a crypto session, and by how much.
281// This is used in the machinery that makes sure replayed packets can't keep a session open indefinitely or stuck using old/bad information about a node.
282func (n *BoxNonce) Minus(m *BoxNonce) int64 {
283	diff := int64(0)
284	for idx := range n {
285		diff *= 256
286		diff += int64(n[idx]) - int64(m[idx])
287		if diff > 64 {
288			diff = 64
289		}
290		if diff < -64 {
291			diff = -64
292		}
293	}
294	return diff
295}
296