You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

cloaks.go 2.6KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. // Copyright (c) 2019 Shivaram Lingamneni
  2. package cloaks
  3. import (
  4. "fmt"
  5. "net"
  6. "golang.org/x/crypto/sha3"
  7. "github.com/ergochat/ergo/irc/utils"
  8. )
  9. type CloakConfig struct {
  10. Enabled bool
  11. EnabledForAlwaysOn bool `yaml:"enabled-for-always-on"`
  12. Netname string
  13. CidrLenIPv4 int `yaml:"cidr-len-ipv4"`
  14. CidrLenIPv6 int `yaml:"cidr-len-ipv6"`
  15. NumBits int `yaml:"num-bits"`
  16. LegacySecretValue string `yaml:"secret"`
  17. secret string
  18. numBytes int
  19. ipv4Mask net.IPMask
  20. ipv6Mask net.IPMask
  21. }
  22. func (cloakConfig *CloakConfig) Initialize() {
  23. // sanity checks:
  24. numBits := cloakConfig.NumBits
  25. if 0 == numBits {
  26. numBits = 64
  27. } else if 256 < numBits {
  28. numBits = 256
  29. }
  30. // derived values:
  31. cloakConfig.numBytes = numBits / 8
  32. // round up to the nearest byte
  33. if numBits%8 != 0 {
  34. cloakConfig.numBytes += 1
  35. }
  36. cloakConfig.ipv4Mask = net.CIDRMask(cloakConfig.CidrLenIPv4, 32)
  37. cloakConfig.ipv6Mask = net.CIDRMask(cloakConfig.CidrLenIPv6, 128)
  38. }
  39. func (cloakConfig *CloakConfig) SetSecret(secret string) {
  40. cloakConfig.secret = secret
  41. }
  42. // simple cloaking algorithm: normalize the IP to its CIDR,
  43. // then hash the resulting bytes with a secret key,
  44. // then truncate to the desired length, b32encode, and append the fake TLD.
  45. func (config *CloakConfig) ComputeCloak(ip net.IP) string {
  46. if !config.Enabled {
  47. return ""
  48. } else if config.NumBits == 0 || config.secret == "" {
  49. return config.Netname
  50. }
  51. var masked net.IP
  52. v4ip := ip.To4()
  53. if v4ip != nil {
  54. masked = v4ip.Mask(config.ipv4Mask)
  55. } else {
  56. masked = ip.Mask(config.ipv6Mask)
  57. }
  58. return config.macAndCompose(masked)
  59. }
  60. func (config *CloakConfig) macAndCompose(b []byte) string {
  61. // SHA3(K || M):
  62. // https://crypto.stackexchange.com/questions/17735/is-hmac-needed-for-a-sha-3-based-mac
  63. input := make([]byte, len(config.secret)+len(b))
  64. copy(input, config.secret[:])
  65. copy(input[len(config.secret):], b)
  66. digest := sha3.Sum512(input)
  67. b32digest := utils.B32Encoder.EncodeToString(digest[:config.numBytes])
  68. return fmt.Sprintf("%s.%s", b32digest, config.Netname)
  69. }
  70. func (config *CloakConfig) ComputeAccountCloak(accountName string) string {
  71. // XXX don't bother checking EnabledForAlwaysOn, since if it's disabled,
  72. // we need to use the server name which we don't have
  73. if config.NumBits == 0 || config.secret == "" {
  74. return config.Netname
  75. }
  76. // pad with 16 initial bytes of zeroes, avoiding any possibility of collision
  77. // with a masked IP that could be an input to ComputeCloak:
  78. paddedAccountName := make([]byte, 16+len(accountName))
  79. copy(paddedAccountName[16:], accountName[:])
  80. return config.macAndCompose(paddedAccountName)
  81. }