lib/protocol, vendor: Import luhn code directly

I've changed it incompatibly to fix a correctness bug. Nonetheless, we
should remain incorrect indefinitely.
This commit is contained in:
Jakob Borg
2017-09-20 21:34:32 +02:00
parent 3ee12464b4
commit 2dd9450793
6 changed files with 103 additions and 97 deletions

View File

@@ -10,7 +10,6 @@ import (
"fmt"
"strings"
"github.com/calmh/luhn"
"github.com/syncthing/syncthing/lib/sha256"
)
@@ -158,7 +157,7 @@ func luhnify(s string) (string, error) {
for i := 0; i < 4; i++ {
p := s[i*13 : (i+1)*13]
copy(res[i*(13+1):], p)
l, err := luhn.Base32.Generate(p)
l, err := luhnBase32.generate(p)
if err != nil {
return "", err
}
@@ -176,7 +175,7 @@ func unluhnify(s string) (string, error) {
for i := 0; i < 4; i++ {
p := s[i*(13+1) : (i+1)*(13+1)-1]
copy(res[i*13:], p)
l, err := luhn.Base32.Generate(p)
l, err := luhnBase32.generate(p)
if err != nil {
return "", err
}

53
lib/protocol/luhn.go Normal file
View File

@@ -0,0 +1,53 @@
// Copyright (C) 2014 The Protocol Authors.
package protocol
import (
"fmt"
"strings"
)
// An alphabet is a string of N characters, representing the digits of a given
// base N.
type luhnAlphabet string
var (
luhnBase32 luhnAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
)
// generate returns a check digit for the string s, which should be composed
// of characters from the Alphabet a.
func (a luhnAlphabet) generate(s string) (rune, error) {
factor := 1
sum := 0
n := len(a)
for i := range s {
codepoint := strings.IndexByte(string(a), s[i])
if codepoint == -1 {
return 0, fmt.Errorf("Digit %q not valid in alphabet %q", s[i], a)
}
addend := factor * codepoint
if factor == 2 {
factor = 1
} else {
factor = 2
}
addend = (addend / n) + (addend % n)
sum += addend
}
remainder := sum % 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
}

48
lib/protocol/luhn_test.go Normal file
View File

@@ -0,0 +1,48 @@
// Copyright (C) 2014 The Protocol Authors.
package protocol
import (
"testing"
)
func TestGenerate(t *testing.T) {
// Base 6 Luhn
a := luhnAlphabet("abcdef")
c, err := a.generate("abcdef")
if err != nil {
t.Fatal(err)
}
if c != 'e' {
t.Errorf("Incorrect check digit %c != e", c)
}
// Base 10 Luhn
a = luhnAlphabet("0123456789")
c, err = a.generate("7992739871")
if err != nil {
t.Fatal(err)
}
if c != '3' {
t.Errorf("Incorrect check digit %c != 3", c)
}
}
func TestInvalidString(t *testing.T) {
a := luhnAlphabet("ABC")
_, err := a.generate("7992739871")
t.Log(err)
if err == nil {
t.Error("Unexpected nil error")
}
}
func TestValidate(t *testing.T) {
a := luhnAlphabet("abcdef")
if !a.luhnValidate("abcdefe") {
t.Errorf("Incorrect validation response for abcdefe")
}
if a.luhnValidate("abcdefd") {
t.Errorf("Incorrect validation response for abcdefd")
}
}