deviceid.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. // Copyright (C) 2014 The Protocol Authors.
  2. package protocol
  3. import (
  4. "bytes"
  5. "crypto/sha256"
  6. "encoding/base32"
  7. "encoding/binary"
  8. "errors"
  9. "fmt"
  10. "regexp"
  11. "strings"
  12. "github.com/calmh/luhn"
  13. )
  14. type DeviceID [32]byte
  15. type ShortID uint64
  16. var 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. // NewDeviceID generates a new device ID from the raw bytes of a certificate
  18. func NewDeviceID(rawCert []byte) DeviceID {
  19. var n DeviceID
  20. hf := sha256.New()
  21. hf.Write(rawCert)
  22. hf.Sum(n[:0])
  23. return n
  24. }
  25. func DeviceIDFromString(s string) (DeviceID, error) {
  26. var n DeviceID
  27. err := n.UnmarshalText([]byte(s))
  28. return n, err
  29. }
  30. func DeviceIDFromBytes(bs []byte) DeviceID {
  31. var n DeviceID
  32. if len(bs) != len(n) {
  33. panic("incorrect length of byte slice representing device ID")
  34. }
  35. copy(n[:], bs)
  36. return n
  37. }
  38. // String returns the canonical string representation of the device ID
  39. func (n DeviceID) String() string {
  40. id := base32.StdEncoding.EncodeToString(n[:])
  41. id = strings.Trim(id, "=")
  42. id, err := luhnify(id)
  43. if err != nil {
  44. // Should never happen
  45. panic(err)
  46. }
  47. id = chunkify(id)
  48. return id
  49. }
  50. func (n DeviceID) GoString() string {
  51. return n.String()
  52. }
  53. func (n DeviceID) Compare(other DeviceID) int {
  54. return bytes.Compare(n[:], other[:])
  55. }
  56. func (n DeviceID) Equals(other DeviceID) bool {
  57. return bytes.Compare(n[:], other[:]) == 0
  58. }
  59. // Short returns an integer representing bits 0-63 of the device ID.
  60. func (n DeviceID) Short() ShortID {
  61. return ShortID(binary.BigEndian.Uint64(n[:]))
  62. }
  63. func (n *DeviceID) MarshalText() ([]byte, error) {
  64. return []byte(n.String()), nil
  65. }
  66. func (s ShortID) String() string {
  67. var bs [8]byte
  68. binary.BigEndian.PutUint64(bs[:], uint64(s))
  69. return base32.StdEncoding.EncodeToString(bs[:])[:7]
  70. }
  71. func (n *DeviceID) UnmarshalText(bs []byte) error {
  72. id := string(bs)
  73. id = strings.Trim(id, "=")
  74. id = strings.ToUpper(id)
  75. id = untypeoify(id)
  76. id = unchunkify(id)
  77. var err error
  78. switch len(id) {
  79. case 56:
  80. // New style, with check digits
  81. id, err = unluhnify(id)
  82. if err != nil {
  83. return err
  84. }
  85. fallthrough
  86. case 52:
  87. // Old style, no check digits
  88. dec, err := base32.StdEncoding.DecodeString(id + "====")
  89. if err != nil {
  90. return err
  91. }
  92. copy(n[:], dec)
  93. return nil
  94. default:
  95. return errors.New("device ID invalid: incorrect length")
  96. }
  97. }
  98. func luhnify(s string) (string, error) {
  99. if len(s) != 52 {
  100. panic("unsupported string length")
  101. }
  102. res := make([]string, 0, 4)
  103. for i := 0; i < 4; i++ {
  104. p := s[i*13 : (i+1)*13]
  105. l, err := luhn.Base32.Generate(p)
  106. if err != nil {
  107. return "", err
  108. }
  109. res = append(res, fmt.Sprintf("%s%c", p, l))
  110. }
  111. return res[0] + res[1] + res[2] + res[3], nil
  112. }
  113. func unluhnify(s string) (string, error) {
  114. if len(s) != 56 {
  115. return "", fmt.Errorf("unsupported string length %d", len(s))
  116. }
  117. res := make([]string, 0, 4)
  118. for i := 0; i < 4; i++ {
  119. p := s[i*14 : (i+1)*14-1]
  120. l, err := luhn.Base32.Generate(p)
  121. if err != nil {
  122. return "", err
  123. }
  124. if g := fmt.Sprintf("%s%c", p, l); g != s[i*14:(i+1)*14] {
  125. return "", errors.New("check digit incorrect")
  126. }
  127. res = append(res, p)
  128. }
  129. return res[0] + res[1] + res[2] + res[3], nil
  130. }
  131. func chunkify(s string) string {
  132. s = regexp.MustCompile("(.{7})").ReplaceAllString(s, "$1-")
  133. s = strings.Trim(s, "-")
  134. return s
  135. }
  136. func unchunkify(s string) string {
  137. s = strings.Replace(s, "-", "", -1)
  138. s = strings.Replace(s, " ", "", -1)
  139. return s
  140. }
  141. func untypeoify(s string) string {
  142. s = strings.Replace(s, "0", "O", -1)
  143. s = strings.Replace(s, "1", "I", -1)
  144. s = strings.Replace(s, "8", "B", -1)
  145. return s
  146. }