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