|
@@ -2,32 +2,34 @@
|
|
|
|
|
|
|
|
package protocol
|
|
package protocol
|
|
|
|
|
|
|
|
-import (
|
|
|
|
|
- "fmt"
|
|
|
|
|
- "strings"
|
|
|
|
|
-)
|
|
|
|
|
|
|
+import "fmt"
|
|
|
|
|
|
|
|
-// An alphabet is a string of N characters, representing the digits of a given
|
|
|
|
|
-// base N.
|
|
|
|
|
-type luhnAlphabet string
|
|
|
|
|
|
|
+var luhnBase32 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
|
|
|
|
|
|
|
|
-var (
|
|
|
|
|
- luhnBase32 luhnAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
|
|
|
|
|
-)
|
|
|
|
|
|
|
+func codepoint32(b byte) int {
|
|
|
|
|
+ switch {
|
|
|
|
|
+ case 'A' <= b && b <= 'Z':
|
|
|
|
|
+ return int(b - 'A')
|
|
|
|
|
+ case '2' <= b && b <= '7':
|
|
|
|
|
+ return int(b + 26 - '2')
|
|
|
|
|
+ default:
|
|
|
|
|
+ return -1
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
-// generate returns a check digit for the string s, which should be composed
|
|
|
|
|
-// of characters from the Alphabet a.
|
|
|
|
|
|
|
+// luhn32 returns a check digit for the string s, which should be composed
|
|
|
|
|
+// of characters from the alphabet luhnBase32.
|
|
|
// Doesn't follow the actual Luhn algorithm
|
|
// Doesn't follow the actual Luhn algorithm
|
|
|
// see https://forum.syncthing.net/t/v0-9-0-new-node-id-format/478/6 for more.
|
|
// see https://forum.syncthing.net/t/v0-9-0-new-node-id-format/478/6 for more.
|
|
|
-func (a luhnAlphabet) generate(s string) (rune, error) {
|
|
|
|
|
|
|
+func luhn32(s string) (rune, error) {
|
|
|
factor := 1
|
|
factor := 1
|
|
|
sum := 0
|
|
sum := 0
|
|
|
- n := len(a)
|
|
|
|
|
|
|
+ const n = 32
|
|
|
|
|
|
|
|
for i := range s {
|
|
for i := range s {
|
|
|
- codepoint := strings.IndexByte(string(a), s[i])
|
|
|
|
|
|
|
+ codepoint := codepoint32(s[i])
|
|
|
if codepoint == -1 {
|
|
if codepoint == -1 {
|
|
|
- return 0, fmt.Errorf("digit %q not valid in alphabet %q", s[i], a)
|
|
|
|
|
|
|
+ return 0, fmt.Errorf("digit %q not valid in alphabet %q", s[i], luhnBase32)
|
|
|
}
|
|
}
|
|
|
addend := factor * codepoint
|
|
addend := factor * codepoint
|
|
|
if factor == 2 {
|
|
if factor == 2 {
|
|
@@ -40,16 +42,5 @@ func (a luhnAlphabet) generate(s string) (rune, error) {
|
|
|
}
|
|
}
|
|
|
remainder := sum % n
|
|
remainder := sum % n
|
|
|
checkCodepoint := (n - remainder) % n
|
|
checkCodepoint := (n - remainder) % n
|
|
|
- return rune(a[checkCodepoint]), nil
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// luhnValidate returns true if the last character of the string s is correct, for
|
|
|
|
|
-// a string s composed of characters in the alphabet a.
|
|
|
|
|
-func (a luhnAlphabet) luhnValidate(s string) bool {
|
|
|
|
|
- t := s[:len(s)-1]
|
|
|
|
|
- c, err := a.generate(t)
|
|
|
|
|
- if err != nil {
|
|
|
|
|
- return false
|
|
|
|
|
- }
|
|
|
|
|
- return rune(s[len(s)-1]) == c
|
|
|
|
|
|
|
+ return rune(luhnBase32[checkCodepoint]), nil
|
|
|
}
|
|
}
|