luhn.go 1.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950
  1. // Copyright (C) 2014 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at https://mozilla.org/MPL/2.0/.
  6. package protocol
  7. import "fmt"
  8. var luhnBase32 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
  9. func codepoint32(b byte) int {
  10. switch {
  11. case 'A' <= b && b <= 'Z':
  12. return int(b - 'A')
  13. case '2' <= b && b <= '7':
  14. return int(b + 26 - '2')
  15. default:
  16. return -1
  17. }
  18. }
  19. // luhn32 returns a check digit for the string s, which should be composed
  20. // of characters from the alphabet luhnBase32.
  21. // Doesn't follow the actual Luhn algorithm
  22. // see https://forum.syncthing.net/t/v0-9-0-new-node-id-format/478/6 for more.
  23. func luhn32(s string) (rune, error) {
  24. factor := 1
  25. sum := 0
  26. const n = 32
  27. for i := range s {
  28. codepoint := codepoint32(s[i])
  29. if codepoint == -1 {
  30. return 0, fmt.Errorf("digit %q not valid in alphabet %q", s[i], luhnBase32)
  31. }
  32. addend := factor * codepoint
  33. if factor == 2 {
  34. factor = 1
  35. } else {
  36. factor = 2
  37. }
  38. addend = (addend / n) + (addend % n)
  39. sum += addend
  40. }
  41. remainder := sum % n
  42. checkCodepoint := (n - remainder) % n
  43. return rune(luhnBase32[checkCodepoint]), nil
  44. }