luhn.go 1.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647
  1. // Copyright (C) 2014 The Protocol Authors.
  2. package protocol
  3. import "fmt"
  4. var luhnBase32 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
  5. func codepoint32(b byte) int {
  6. switch {
  7. case 'A' <= b && b <= 'Z':
  8. return int(b - 'A')
  9. case '2' <= b && b <= '7':
  10. return int(b + 26 - '2')
  11. default:
  12. return -1
  13. }
  14. }
  15. // luhn32 returns a check digit for the string s, which should be composed
  16. // of characters from the alphabet luhnBase32.
  17. // Doesn't follow the actual Luhn algorithm
  18. // see https://forum.syncthing.net/t/v0-9-0-new-node-id-format/478/6 for more.
  19. func luhn32(s string) (rune, error) {
  20. factor := 1
  21. sum := 0
  22. const n = 32
  23. for i := range s {
  24. codepoint := codepoint32(s[i])
  25. if codepoint == -1 {
  26. return 0, fmt.Errorf("digit %q not valid in alphabet %q", s[i], luhnBase32)
  27. }
  28. addend := factor * codepoint
  29. if factor == 2 {
  30. factor = 1
  31. } else {
  32. factor = 2
  33. }
  34. addend = (addend / n) + (addend % n)
  35. sum += addend
  36. }
  37. remainder := sum % n
  38. checkCodepoint := (n - remainder) % n
  39. return rune(luhnBase32[checkCodepoint]), nil
  40. }