luhn.go 1.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
  1. // Copyright (C) 2014 The Protocol Authors.
  2. package protocol
  3. import (
  4. "fmt"
  5. "strings"
  6. )
  7. // An alphabet is a string of N characters, representing the digits of a given
  8. // base N.
  9. type luhnAlphabet string
  10. var (
  11. luhnBase32 luhnAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
  12. )
  13. // generate returns a check digit for the string s, which should be composed
  14. // of characters from the Alphabet a.
  15. // Doesn't follow the actual Luhn algorithm
  16. // see https://forum.syncthing.net/t/v0-9-0-new-node-id-format/478/6 for more.
  17. func (a luhnAlphabet) generate(s string) (rune, error) {
  18. factor := 1
  19. sum := 0
  20. n := len(a)
  21. for i := range s {
  22. codepoint := strings.IndexByte(string(a), s[i])
  23. if codepoint == -1 {
  24. return 0, fmt.Errorf("digit %q not valid in alphabet %q", s[i], a)
  25. }
  26. addend := factor * codepoint
  27. if factor == 2 {
  28. factor = 1
  29. } else {
  30. factor = 2
  31. }
  32. addend = (addend / n) + (addend % n)
  33. sum += addend
  34. }
  35. remainder := sum % n
  36. checkCodepoint := (n - remainder) % n
  37. return rune(a[checkCodepoint]), nil
  38. }
  39. // luhnValidate returns true if the last character of the string s is correct, for
  40. // a string s composed of characters in the alphabet a.
  41. func (a luhnAlphabet) luhnValidate(s string) bool {
  42. t := s[:len(s)-1]
  43. c, err := a.generate(t)
  44. if err != nil {
  45. return false
  46. }
  47. return rune(s[len(s)-1]) == c
  48. }