Blob Blame History Raw
From 2dd94507937276cae93ca17a8518c12b55e8ae6e Mon Sep 17 00:00:00 2001
From: Jakob Borg <jakob@kastelo.net>
Date: Wed, 20 Sep 2017 21:34:32 +0200
Subject: [PATCH] lib/protocol, vendor: Import luhn code directly

I've changed it incompatibly to fix a correctness bug. Nonetheless, we
should remain incorrect indefinitely.
---
 lib/protocol/deviceid.go             |  5 ++-
 lib/protocol/luhn.go                 | 53 ++++++++++++++++++++++++++++
 lib/protocol/luhn_test.go            | 48 ++++++++++++++++++++++++++
 vendor/github.com/calmh/luhn/LICENSE | 19 ----------
 vendor/github.com/calmh/luhn/luhn.go | 67 ------------------------------------
 vendor/manifest                      |  8 -----
 6 files changed, 103 insertions(+), 97 deletions(-)
 create mode 100644 lib/protocol/luhn.go
 create mode 100644 lib/protocol/luhn_test.go
 delete mode 100644 vendor/github.com/calmh/luhn/LICENSE
 delete mode 100644 vendor/github.com/calmh/luhn/luhn.go

diff --git a/lib/protocol/deviceid.go b/lib/protocol/deviceid.go
index df1cbdece..c2a30b286 100644
--- a/lib/protocol/deviceid.go
+++ b/lib/protocol/deviceid.go
@@ -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
 		}
diff --git a/lib/protocol/luhn.go b/lib/protocol/luhn.go
new file mode 100644
index 000000000..ee5155f09
--- /dev/null
+++ b/lib/protocol/luhn.go
@@ -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
+}
diff --git a/lib/protocol/luhn_test.go b/lib/protocol/luhn_test.go
new file mode 100644
index 000000000..fe7f80c11
--- /dev/null
+++ b/lib/protocol/luhn_test.go
@@ -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")
+	}
+}
diff --git a/vendor/github.com/calmh/luhn/LICENSE b/vendor/github.com/calmh/luhn/LICENSE
deleted file mode 100644
index 0e07d0e10..000000000
--- a/vendor/github.com/calmh/luhn/LICENSE
+++ /dev/null
@@ -1,19 +0,0 @@
-Copyright (C) 2014 Jakob Borg
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-of the Software, and to permit persons to whom the Software is furnished to do
-so, subject to the following conditions:
-
-- The above copyright notice and this permission notice shall be included in
-  all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/vendor/github.com/calmh/luhn/luhn.go b/vendor/github.com/calmh/luhn/luhn.go
deleted file mode 100644
index f09f3f298..000000000
--- a/vendor/github.com/calmh/luhn/luhn.go
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (C) 2014 Jakob Borg
-
-// Package luhn generates and validates Luhn mod N check digits.
-package luhn
-
-import (
-	"fmt"
-	"strings"
-)
-
-// An alphabet is a string of N characters, representing the digits of a given
-// base N.
-type Alphabet string
-
-var (
-	Base32 Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
-)
-
-// Generate returns a check digit for the string s, which should be composed
-// of characters from the Alphabet a.
-func (a Alphabet) 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
-}
-
-// Validate 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 Alphabet) Validate(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
-}
-
-// NewAlphabet converts the given string an an Alphabet, verifying that it
-// is correct.
-func NewAlphabet(s string) (Alphabet, error) {
-	cm := make(map[byte]bool, len(s))
-	for i := range s {
-		if cm[s[i]] {
-			return "", fmt.Errorf("Digit %q non-unique in alphabet %q", s[i], s)
-		}
-		cm[s[i]] = true
-	}
-	return Alphabet(s), nil
-}
diff --git a/vendor/manifest b/vendor/manifest
index 72d9c1c01..801a83ccb 100644
--- a/vendor/manifest
+++ b/vendor/manifest
@@ -50,14 +50,6 @@
 			"notests": true
 		},
 		{
-			"importpath": "github.com/calmh/luhn",
-			"repository": "https://github.com/calmh/luhn",
-			"vcs": "git",
-			"revision": "c0f1d77264fb3d1bfc65b70eea6ee264058c57c0",
-			"branch": "master",
-			"notests": true
-		},
-		{
 			"importpath": "github.com/calmh/xdr",
 			"repository": "https://github.com/calmh/xdr",
 			"vcs": "git",