Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add checksum validation for Ethereum address #630

Merged
merged 5 commits into from
Sep 27, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 26 additions & 2 deletions baked_in.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"context"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"net"
Expand All @@ -16,6 +17,8 @@ import (
"time"
"unicode/utf8"

"golang.org/x/crypto/sha3"

urn "github.com/leodido/go-urn"
)

Expand Down Expand Up @@ -515,7 +518,7 @@ func isISBN10(fl FieldLevel) bool {
return checksum%11 == 0
}

// IsEthereumAddress is the validation function for validating if the field's value is a valid ethereum address based currently only on the format
// IsEthereumAddress is the validation function for validating if the field's value is a valid Ethereum address.
func isEthereumAddress(fl FieldLevel) bool {
address := fl.Field().String()

Expand All @@ -527,7 +530,28 @@ func isEthereumAddress(fl FieldLevel) bool {
return true
}

// checksum validation is blocked by https://github.com/golang/crypto/pull/28
// Checksum validation. Reference: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md
digitValue := map[byte]int{
'0': 0, '1': 1, '2': 2, '3': 3, '4': 4,
'5': 5, '6': 6, '7': 7, '8': 8, '9': 9,
'a': 10, 'b': 11, 'c': 12,
'd': 13, 'e': 14, 'f': 15,
}
address = address[2:]
h := sha3.NewLegacyKeccak256()
h.Write([]byte(strings.ToLower(address)))
elias19r marked this conversation as resolved.
Show resolved Hide resolved
hash := hex.EncodeToString(h.Sum(nil))

for i := 0; i < len(address); i++ {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here len(address) definitely has value 40, but I preferred to write len(address) instead of 40. Thus we keep the information that "Ethereum addresses have 40 hex digits" declared only in one place, in the regexes.go (ethAddress{Upper,Lower,}RegexString).

Let me know if otherwise

if address[i] <= '9' {
continue
}
nibble := digitValue[hash[i]]

if nibble > 7 && address[i] >= 'a' || nibble <= 7 && address[i] < 'a' {
return false
}
}

return true
}
Expand Down
3 changes: 1 addition & 2 deletions doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -766,8 +766,7 @@ Special thanks to Pieter Wuille for providng reference implementations.
Ethereum Address

This validates that a string value contains a valid ethereum address.
The format of the string is checked to ensure it matches the standard Ethereum address format
Full validation is blocked by https://github.com/golang/crypto/pull/28
The format of the string is checked to ensure it matches the standard Ethereum address format.

Usage: eth_addr

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ require (
github.com/go-playground/locales v0.13.0
github.com/go-playground/universal-translator v0.17.0
github.com/leodido/go-urn v1.2.0
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
)
7 changes: 7 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
Expand Down
26 changes: 22 additions & 4 deletions validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4653,11 +4653,29 @@ func TestEthereumAddressValidation(t *testing.T) {
param string
expected bool
}{
{"", false},
{"0x02F9AE5f22EA3fA88F05780B30385bEC", false},
{"123f681646d4a755815f9cb19e1acc8565a0c2ac", false},
{"0x02F9AE5f22EA3fA88F05780B30385bECFacbf130", true},
// All caps.
{"0x52908400098527886E0F7030069857D2E4169EE7", true},
{"0x8617E340B3D01FA5F11F306F4090FD50E238070D", true},

// All lower.
{"0xde709f2102306220921060314715629080e2fb77", true},
{"0x27b1fdb04752bbc536007a920d24acb045561c26", true},
{"0x123f681646d4a755815f9cb19e1acc8565a0c2ac", true},

// Mixed case: runs checksum validation.
{"0x02F9AE5f22EA3fA88F05780B30385bECFacbf130", true},
{"0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed", true},
{"0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359", true},
{"0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB", true},
{"0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb", true},
{"0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDB", false}, // Invalid checksum.

// Other.
{"", false},
{"D1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb", false}, // Missing "0x" prefix.
{"0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDbc", false}, // More than 40 hex digits.
{"0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aD", false}, // Less than 40 hex digits.
{"0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDw", false}, // Invalid hex digit "w".
}

for i, test := range tests {
Expand Down