fingerprint.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. /*
  2. * Copyright (C)2013-2020 ZeroTier, Inc.
  3. *
  4. * Use of this software is governed by the Business Source License included
  5. * in the LICENSE.TXT file in the project's root directory.
  6. *
  7. * Change Date: 2025-01-01
  8. *
  9. * On the date above, in accordance with the Business Source License, use
  10. * of this software will be governed by version 2.0 of the Apache License.
  11. */
  12. /****/
  13. package zerotier
  14. // #include "../../serviceiocore/GoGlue.h"
  15. import "C"
  16. import (
  17. "bytes"
  18. "encoding/json"
  19. "fmt"
  20. "strings"
  21. "unsafe"
  22. )
  23. // FingerprintHashSize is the length of a fingerprint hash in bytes.
  24. const FingerprintHashSize = 48
  25. // Fingerprint bundles an address with an optional SHA384 full hash of the identity's key(s).
  26. type Fingerprint struct {
  27. Address Address `json:"address"`
  28. Hash []byte `json:"hash"`
  29. }
  30. // NewFingerprintFromString decodes a string-format fingerprint.
  31. // A fingerprint has the format address-hash, where address is a 10-digit
  32. // ZeroTier address and a hash is a base32-encoded SHA384 hash. Fingerprints
  33. // can be missing the hash in which case they are represented the same as
  34. // an Address and the hash field will be nil.
  35. func NewFingerprintFromString(fps string) (*Fingerprint, error) {
  36. if len(fps) < AddressStringLength {
  37. return nil, ErrInvalidZeroTierAddress
  38. }
  39. ss := strings.Split(fps, "-")
  40. if len(ss) < 1 || len(ss) > 2 {
  41. return nil, ErrInvalidParameter
  42. }
  43. a, err := NewAddressFromString(ss[0])
  44. if err != nil {
  45. return nil, err
  46. }
  47. if len(ss) == 2 {
  48. h, err := Base32.DecodeString(ss[1])
  49. if err != nil {
  50. return nil, err
  51. }
  52. if len(h) != 48 {
  53. return nil, ErrInvalidParameter
  54. }
  55. return &Fingerprint{Address: a, Hash: h}, nil
  56. }
  57. return &Fingerprint{Address: a, Hash: nil}, nil
  58. }
  59. func newFingerprintFromCFingerprint(cfp *C.ZT_Fingerprint) *Fingerprint {
  60. var fp Fingerprint
  61. if uintptr(unsafe.Pointer(cfp)) != 0 {
  62. fp.Address = Address(cfp.address)
  63. fp.Hash = C.GoBytes(unsafe.Pointer(&cfp.hash[0]), 48)
  64. if allZero(fp.Hash) {
  65. fp.Hash = nil
  66. }
  67. }
  68. return &fp
  69. }
  70. // String returns an address or a full address-hash depenting on whether a hash is present.
  71. func (fp *Fingerprint) String() string {
  72. if len(fp.Hash) == FingerprintHashSize {
  73. return fmt.Sprintf("%.10x-%s", uint64(fp.Address), Base32.EncodeToString(fp.Hash))
  74. }
  75. return fp.Address.String()
  76. }
  77. // Equals test for full equality with another fingerprint (including hash).
  78. func (fp *Fingerprint) Equals(fp2 *Fingerprint) bool {
  79. return fp.Address == fp2.Address && bytes.Equal(fp.Hash[:], fp2.Hash[:])
  80. }
  81. // BestSpecificityEquals compares either just the addresses or also the hashes if both are present.
  82. func (fp *Fingerprint) BestSpecificityEquals(fp2 *Fingerprint) bool {
  83. if fp2 == nil || fp.Address != fp2.Address {
  84. return false
  85. }
  86. if len(fp.Hash) == FingerprintHashSize && len(fp2.Hash) == FingerprintHashSize {
  87. return bytes.Equal(fp.Hash, fp2.Hash)
  88. }
  89. return true
  90. }
  91. func (fp *Fingerprint) MarshalJSON() ([]byte, error) {
  92. return []byte("\"" + fp.String() + "\""), nil
  93. }
  94. func (fp *Fingerprint) UnmarshalJSON(j []byte) error {
  95. var s string
  96. err := json.Unmarshal(j, &s)
  97. if err != nil {
  98. return err
  99. }
  100. fp2, err := NewFingerprintFromString(s)
  101. fp.Address = fp2.Address
  102. fp.Hash = fp2.Hash
  103. return err
  104. }
  105. func (fp *Fingerprint) cFingerprint() *C.ZT_Fingerprint {
  106. var apifp C.ZT_Fingerprint
  107. apifp.address = C.uint64_t(fp.Address)
  108. copy((*[48]byte)(unsafe.Pointer(&apifp.hash[0]))[:], fp.Hash[:])
  109. return &apifp
  110. }