deviceid.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. // Copyright (C) 2014 The Protocol Authors.
  2. package protocol
  3. import (
  4. "bytes"
  5. "encoding/base32"
  6. "encoding/binary"
  7. "errors"
  8. "fmt"
  9. "strings"
  10. "github.com/syncthing/syncthing/lib/sha256"
  11. )
  12. const DeviceIDLength = 32
  13. type DeviceID [DeviceIDLength]byte
  14. type ShortID uint64
  15. var (
  16. LocalDeviceID = DeviceID{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
  17. EmptyDeviceID = DeviceID{ /* all zeroes */ }
  18. )
  19. // NewDeviceID generates a new device ID from the raw bytes of a certificate
  20. func NewDeviceID(rawCert []byte) DeviceID {
  21. var n DeviceID
  22. hf := sha256.New()
  23. hf.Write(rawCert)
  24. hf.Sum(n[:0])
  25. return n
  26. }
  27. func DeviceIDFromString(s string) (DeviceID, error) {
  28. var n DeviceID
  29. err := n.UnmarshalText([]byte(s))
  30. return n, err
  31. }
  32. func DeviceIDFromBytes(bs []byte) DeviceID {
  33. var n DeviceID
  34. if len(bs) != len(n) {
  35. panic("incorrect length of byte slice representing device ID")
  36. }
  37. copy(n[:], bs)
  38. return n
  39. }
  40. // String returns the canonical string representation of the device ID
  41. func (n DeviceID) String() string {
  42. if n == EmptyDeviceID {
  43. return ""
  44. }
  45. id := base32.StdEncoding.EncodeToString(n[:])
  46. id = strings.Trim(id, "=")
  47. id, err := luhnify(id)
  48. if err != nil {
  49. // Should never happen
  50. panic(err)
  51. }
  52. id = chunkify(id)
  53. return id
  54. }
  55. func (n DeviceID) GoString() string {
  56. return n.String()
  57. }
  58. func (n DeviceID) Compare(other DeviceID) int {
  59. return bytes.Compare(n[:], other[:])
  60. }
  61. func (n DeviceID) Equals(other DeviceID) bool {
  62. return bytes.Equal(n[:], other[:])
  63. }
  64. // Short returns an integer representing bits 0-63 of the device ID.
  65. func (n DeviceID) Short() ShortID {
  66. return ShortID(binary.BigEndian.Uint64(n[:]))
  67. }
  68. func (n *DeviceID) MarshalText() ([]byte, error) {
  69. return []byte(n.String()), nil
  70. }
  71. func (s ShortID) String() string {
  72. if s == 0 {
  73. return ""
  74. }
  75. var bs [8]byte
  76. binary.BigEndian.PutUint64(bs[:], uint64(s))
  77. return base32.StdEncoding.EncodeToString(bs[:])[:7]
  78. }
  79. func (n *DeviceID) UnmarshalText(bs []byte) error {
  80. id := string(bs)
  81. id = strings.Trim(id, "=")
  82. id = strings.ToUpper(id)
  83. id = untypeoify(id)
  84. id = unchunkify(id)
  85. var err error
  86. switch len(id) {
  87. case 0:
  88. *n = EmptyDeviceID
  89. return nil
  90. case 56:
  91. // New style, with check digits
  92. id, err = unluhnify(id)
  93. if err != nil {
  94. return err
  95. }
  96. fallthrough
  97. case 52:
  98. // Old style, no check digits
  99. dec, err := base32.StdEncoding.DecodeString(id + "====")
  100. if err != nil {
  101. return err
  102. }
  103. copy(n[:], dec)
  104. return nil
  105. default:
  106. return fmt.Errorf("%q: device ID invalid: incorrect length", bs)
  107. }
  108. }
  109. func (n *DeviceID) ProtoSize() int {
  110. // Used by protobuf marshaller.
  111. return DeviceIDLength
  112. }
  113. func (n *DeviceID) MarshalTo(bs []byte) (int, error) {
  114. // Used by protobuf marshaller.
  115. if len(bs) < DeviceIDLength {
  116. return 0, errors.New("destination too short")
  117. }
  118. copy(bs, (*n)[:])
  119. return DeviceIDLength, nil
  120. }
  121. func (n *DeviceID) Unmarshal(bs []byte) error {
  122. // Used by protobuf marshaller.
  123. if len(bs) < DeviceIDLength {
  124. return fmt.Errorf("%q: not enough data", bs)
  125. }
  126. copy((*n)[:], bs)
  127. return nil
  128. }
  129. func luhnify(s string) (string, error) {
  130. if len(s) != 52 {
  131. panic("unsupported string length")
  132. }
  133. res := make([]byte, 4*(13+1))
  134. for i := 0; i < 4; i++ {
  135. p := s[i*13 : (i+1)*13]
  136. copy(res[i*(13+1):], p)
  137. l, err := luhnBase32.generate(p)
  138. if err != nil {
  139. return "", err
  140. }
  141. res[(i+1)*(13)+i] = byte(l)
  142. }
  143. return string(res), nil
  144. }
  145. func unluhnify(s string) (string, error) {
  146. if len(s) != 56 {
  147. return "", fmt.Errorf("%q: unsupported string length %d", s, len(s))
  148. }
  149. res := make([]byte, 52)
  150. for i := 0; i < 4; i++ {
  151. p := s[i*(13+1) : (i+1)*(13+1)-1]
  152. copy(res[i*13:], p)
  153. l, err := luhnBase32.generate(p)
  154. if err != nil {
  155. return "", err
  156. }
  157. if s[(i+1)*14-1] != byte(l) {
  158. return "", fmt.Errorf("%q: check digit incorrect", s)
  159. }
  160. }
  161. return string(res), nil
  162. }
  163. func chunkify(s string) string {
  164. chunks := len(s) / 7
  165. res := make([]byte, chunks*(7+1)-1)
  166. for i := 0; i < chunks; i++ {
  167. if i > 0 {
  168. res[i*(7+1)-1] = '-'
  169. }
  170. copy(res[i*(7+1):], s[i*7:(i+1)*7])
  171. }
  172. return string(res)
  173. }
  174. func unchunkify(s string) string {
  175. s = strings.Replace(s, "-", "", -1)
  176. s = strings.Replace(s, " ", "", -1)
  177. return s
  178. }
  179. func untypeoify(s string) string {
  180. s = strings.Replace(s, "0", "O", -1)
  181. s = strings.Replace(s, "1", "I", -1)
  182. s = strings.Replace(s, "8", "B", -1)
  183. return s
  184. }
  185. // DeviceIDs is a sortable slice of DeviceID
  186. type DeviceIDs []DeviceID
  187. func (l DeviceIDs) Len() int {
  188. return len(l)
  189. }
  190. func (l DeviceIDs) Less(a, b int) bool {
  191. return l[a].Compare(l[b]) == -1
  192. }
  193. func (l DeviceIDs) Swap(a, b int) {
  194. l[a], l[b] = l[b], l[a]
  195. }