mirror of
https://github.com/yggdrasil-network/yggdrasil-go.git
synced 2025-04-28 22:25:07 +03:00
157 lines
5.4 KiB
Go
157 lines
5.4 KiB
Go
// Package address contains the types used by mesh to represent IPv6 addresses or prefixes, as well as functions for working with these types.
|
|
// Of particular importance are the functions used to derive addresses or subnets from a NodeID, or to get the NodeID and bitmask of the bits visible from an address, which is needed for DHT searches.
|
|
package core
|
|
|
|
import (
|
|
"crypto/ed25519"
|
|
"encoding/hex"
|
|
)
|
|
|
|
// Address represents an IPv6 address in the mesh address range.
|
|
type Address [16]byte
|
|
|
|
// Subnet represents an IPv6 /64 subnet in the mesh subnet range.
|
|
type Subnet [8]byte
|
|
|
|
// GetPrefix returns the address prefix used by mesh.
|
|
// The current implementation requires this to be a multiple of 8 bits + 7 bits.
|
|
// The 8th bit of the last byte is used to signal nodes (0) or /64 prefixes (1).
|
|
// Nodes that configure this differently will be unable to communicate with each other using IP packets, though routing and the DHT machinery *should* still work.
|
|
func (c *Core) GetPrefix() [1]byte {
|
|
p, err := hex.DecodeString(c.config.networkdomain.Prefix)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
var prefix [1]byte
|
|
copy(prefix[:], p[:1])
|
|
return prefix
|
|
}
|
|
|
|
// IsValid returns true if an address falls within the range used by nodes in the network.
|
|
func (c *Core) IsValidAddress(a Address) bool {
|
|
prefix := c.GetPrefix()
|
|
for idx := range prefix {
|
|
if a[idx] != prefix[idx] {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// IsValid returns true if a prefix falls within the range usable by the network.
|
|
func (c *Core) IsValidSubnet(s Subnet) bool {
|
|
prefix := c.GetPrefix()
|
|
l := len(prefix)
|
|
for idx := range prefix[:l-1] {
|
|
if s[idx] != prefix[idx] {
|
|
return false
|
|
}
|
|
}
|
|
return s[l-1] == prefix[l-1]|0x01
|
|
}
|
|
|
|
// AddrForKey takes an ed25519.PublicKey as an argument and returns an *Address.
|
|
// This function returns nil if the key length is not ed25519.PublicKeySize.
|
|
// This address begins with the contents of GetPrefix(), with the last bit set to 0 to indicate an address.
|
|
// The following 8 bits are set to the number of leading 1 bits in the bitwise inverse of the public key.
|
|
// The bitwise inverse of the key, excluding the leading 1 bits and the first leading 0 bit, is truncated to the appropriate length and makes up the remainder of the address.
|
|
func (c *Core) AddrForKey(publicKey ed25519.PublicKey) *Address {
|
|
// 128 bit address
|
|
// Begins with prefix
|
|
// Next bit is a 0
|
|
// Next 7 bits, interpreted as a uint, are # of leading 1s in the NodeID
|
|
// Leading 1s and first leading 0 of the NodeID are truncated off
|
|
// The rest is appended to the IPv6 address (truncated to 128 bits total)
|
|
if len(publicKey) != ed25519.PublicKeySize {
|
|
return nil
|
|
}
|
|
var buf [ed25519.PublicKeySize]byte
|
|
copy(buf[:], publicKey)
|
|
for idx := range buf {
|
|
buf[idx] = ^buf[idx]
|
|
}
|
|
var addr Address
|
|
var temp = make([]byte, 0, 32)
|
|
done := false
|
|
ones := byte(0)
|
|
bits := byte(0)
|
|
nBits := 0
|
|
for idx := 0; idx < 8*len(buf); idx++ {
|
|
bit := (buf[idx/8] & (0x80 >> byte(idx%8))) >> byte(7-(idx%8))
|
|
if !done && bit != 0 {
|
|
ones++
|
|
continue
|
|
}
|
|
if !done && bit == 0 {
|
|
done = true
|
|
continue // FIXME? this assumes that ones <= 127, probably only worth changing by using a variable length uint64, but that would require changes to the addressing scheme, and I'm not sure ones > 127 is realistic
|
|
}
|
|
bits = (bits << 1) | bit
|
|
nBits++
|
|
if nBits == 8 {
|
|
nBits = 0
|
|
temp = append(temp, bits)
|
|
}
|
|
}
|
|
prefix := c.GetPrefix()
|
|
copy(addr[:], prefix[:])
|
|
addr[len(prefix)] = ones
|
|
copy(addr[len(prefix)+1:], temp)
|
|
return &addr
|
|
}
|
|
|
|
// SubnetForKey takes an ed25519.PublicKey as an argument and returns a *Subnet.
|
|
// This function returns nil if the key length is not ed25519.PublicKeySize.
|
|
// The subnet begins with the address prefix, with the last bit set to 1 to indicate a prefix.
|
|
// The following 8 bits are set to the number of leading 1 bits in the bitwise inverse of the key.
|
|
// The bitwise inverse of the key, excluding the leading 1 bits and the first leading 0 bit, is truncated to the appropriate length and makes up the remainder of the subnet.
|
|
func (c *Core) SubnetForKey(publicKey ed25519.PublicKey) *Subnet {
|
|
// Exactly as the address version, with two exceptions:
|
|
// 1) The first bit after the fixed prefix is a 1 instead of a 0
|
|
// 2) It's truncated to a subnet prefix length instead of 128 bits
|
|
addr := c.AddrForKey(publicKey)
|
|
if addr == nil {
|
|
return nil
|
|
}
|
|
var snet Subnet
|
|
copy(snet[:], addr[:])
|
|
prefix := c.GetPrefix() // nolint:staticcheck
|
|
snet[len(prefix)-1] |= 0x01
|
|
return &snet
|
|
}
|
|
|
|
// GetKet returns the partial ed25519.PublicKey for the Address.
|
|
// This is used for key lookup.
|
|
func (c *Core) GetAddressKey(a Address) ed25519.PublicKey {
|
|
var key [ed25519.PublicKeySize]byte
|
|
prefix := c.GetPrefix() // nolint:staticcheck
|
|
ones := int(a[len(prefix)])
|
|
for idx := 0; idx < ones; idx++ {
|
|
key[idx/8] |= 0x80 >> byte(idx%8)
|
|
}
|
|
keyOffset := ones + 1
|
|
addrOffset := 8*len(prefix) + 8
|
|
for idx := addrOffset; idx < 8*len(a); idx++ {
|
|
bits := a[idx/8] & (0x80 >> byte(idx%8))
|
|
bits <<= byte(idx % 8)
|
|
keyIdx := keyOffset + (idx - addrOffset)
|
|
bits >>= byte(keyIdx % 8)
|
|
idx := keyIdx / 8
|
|
if idx >= len(key) {
|
|
break
|
|
}
|
|
key[idx] |= bits
|
|
}
|
|
for idx := range key {
|
|
key[idx] = ^key[idx]
|
|
}
|
|
return ed25519.PublicKey(key[:])
|
|
}
|
|
|
|
// GetKet returns the partial ed25519.PublicKey for the Subnet.
|
|
// This is used for key lookup.
|
|
func (c *Core) GetSubnetKey(s Subnet) ed25519.PublicKey {
|
|
var addr Address
|
|
copy(addr[:], s[:])
|
|
return c.GetAddressKey(addr)
|
|
}
|