luhn.go 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. // Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file).
  2. //
  3. // This program is free software: you can redistribute it and/or modify it
  4. // under the terms of the GNU General Public License as published by the Free
  5. // Software Foundation, either version 3 of the License, or (at your option)
  6. // any later version.
  7. //
  8. // This program is distributed in the hope that it will be useful, but WITHOUT
  9. // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  10. // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  11. // more details.
  12. //
  13. // You should have received a copy of the GNU General Public License along
  14. // with this program. If not, see <http://www.gnu.org/licenses/>.
  15. // Package luhn generates and validates Luhn mod N check digits.
  16. package luhn
  17. import (
  18. "fmt"
  19. "strings"
  20. )
  21. // An alphabet is a string of N characters, representing the digits of a given
  22. // base N.
  23. type Alphabet string
  24. var (
  25. Base32 Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
  26. )
  27. // Generate returns a check digit for the string s, which should be composed
  28. // of characters from the Alphabet a.
  29. func (a Alphabet) Generate(s string) (rune, error) {
  30. if err := a.check(); err != nil {
  31. return 0, err
  32. }
  33. factor := 1
  34. sum := 0
  35. n := len(a)
  36. for i := range s {
  37. codepoint := strings.IndexByte(string(a), s[i])
  38. if codepoint == -1 {
  39. return 0, fmt.Errorf("Digit %q not valid in alphabet %q", s[i], a)
  40. }
  41. addend := factor * codepoint
  42. if factor == 2 {
  43. factor = 1
  44. } else {
  45. factor = 2
  46. }
  47. addend = (addend / n) + (addend % n)
  48. sum += addend
  49. }
  50. remainder := sum % n
  51. checkCodepoint := (n - remainder) % n
  52. return rune(a[checkCodepoint]), nil
  53. }
  54. // Validate returns true if the last character of the string s is correct, for
  55. // a string s composed of characters in the alphabet a.
  56. func (a Alphabet) Validate(s string) bool {
  57. t := s[:len(s)-1]
  58. c, err := a.Generate(t)
  59. if err != nil {
  60. return false
  61. }
  62. return rune(s[len(s)-1]) == c
  63. }
  64. // check returns an error if the given alphabet does not consist of unique characters
  65. func (a Alphabet) check() error {
  66. cm := make(map[byte]bool, len(a))
  67. for i := range a {
  68. if cm[a[i]] {
  69. return fmt.Errorf("Digit %q non-unique in alphabet %q", a[i], a)
  70. }
  71. cm[a[i]] = true
  72. }
  73. return nil
  74. }