deviceid.go 4.6 KB

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