@ -1,294 +0,0 @@ | |||||
package nano | |||||
import ( | |||||
"bytes" | |||||
"encoding/hex" | |||||
"github.com/pkg/errors" | |||||
ledger "github.com/ethanfrey/ledger" | |||||
crypto "github.com/tendermint/go-crypto" | |||||
amino "github.com/tendermint/go-amino" | |||||
) | |||||
//nolint | |||||
const ( | |||||
NameLedgerEd25519 = "ledger-ed25519" | |||||
TypeLedgerEd25519 = 0x10 | |||||
// Timeout is the number of seconds to wait for a response from the ledger | |||||
// if eg. waiting for user confirmation on button push | |||||
Timeout = 20 | |||||
) | |||||
var device *ledger.Ledger | |||||
// getLedger gets a copy of the device, and caches it | |||||
func getLedger() (*ledger.Ledger, error) { | |||||
var err error | |||||
if device == nil { | |||||
device, err = ledger.FindLedger() | |||||
} | |||||
return device, err | |||||
} | |||||
func signLedger(device *ledger.Ledger, msg []byte) (pub crypto.PubKey, sig crypto.Signature, err error) { | |||||
var resp []byte | |||||
packets := generateSignRequests(msg) | |||||
for _, pack := range packets { | |||||
resp, err = device.Exchange(pack, Timeout) | |||||
if err != nil { | |||||
return pub, sig, err | |||||
} | |||||
} | |||||
// the last call is the result we want and needs to be parsed | |||||
key, bsig, err := parseDigest(resp) | |||||
if err != nil { | |||||
return pub, sig, err | |||||
} | |||||
var b [32]byte | |||||
copy(b[:], key) | |||||
return PubKeyLedgerEd25519FromBytes(b), crypto.SignatureEd25519FromBytes(bsig), nil | |||||
} | |||||
// PrivKeyLedgerEd25519 implements PrivKey, calling the ledger nano | |||||
// we cache the PubKey from the first call to use it later | |||||
type PrivKeyLedgerEd25519 struct { | |||||
// PubKey should be private, but we want to encode it via go-amino | |||||
// so we can view the address later, even without having the ledger | |||||
// attached | |||||
CachedPubKey crypto.PubKey | |||||
} | |||||
// NewPrivKeyLedgerEd25519 will generate a new key and store the | |||||
// public key for later use. | |||||
func NewPrivKeyLedgerEd25519() (crypto.PrivKey, error) { | |||||
var pk PrivKeyLedgerEd25519 | |||||
// getPubKey will cache the pubkey for later use, | |||||
// this allows us to return an error early if the ledger | |||||
// is not plugged in | |||||
_, err := pk.getPubKey() | |||||
return pk.Wrap(), err | |||||
} | |||||
// ValidateKey allows us to verify the sanity of a key | |||||
// after loading it from disk | |||||
func (pk *PrivKeyLedgerEd25519) ValidateKey() error { | |||||
// getPubKey will return an error if the ledger is not | |||||
// properly set up... | |||||
pub, err := pk.forceGetPubKey() | |||||
if err != nil { | |||||
return err | |||||
} | |||||
// verify this matches cached address | |||||
if !pub.Equals(pk.CachedPubKey) { | |||||
return errors.New("ledger doesn't match cached key") | |||||
} | |||||
return nil | |||||
} | |||||
// AssertIsPrivKeyInner fulfils PrivKey Interface | |||||
func (pk *PrivKeyLedgerEd25519) AssertIsPrivKeyInner() {} | |||||
// Bytes fulfils PrivKey Interface - but it stores the cached pubkey so we can verify | |||||
// the same key when we reconnect to a ledger | |||||
func (pk *PrivKeyLedgerEd25519) Bytes() []byte { | |||||
return amino.BinaryBytes(pk.Wrap()) | |||||
} | |||||
// Sign calls the ledger and stores the PubKey for future use | |||||
// | |||||
// XXX/TODO: panics if there is an error communicating with the ledger. | |||||
// | |||||
// Communication is checked on NewPrivKeyLedger and PrivKeyFromBytes, | |||||
// returning an error, so this should only trigger if the privkey is held | |||||
// in memory for a while before use. | |||||
func (pk *PrivKeyLedgerEd25519) Sign(msg []byte) crypto.Signature { | |||||
// oh, I wish there was better error handling | |||||
dev, err := getLedger() | |||||
if err != nil { | |||||
panic(err) | |||||
} | |||||
pub, sig, err := signLedger(dev, msg) | |||||
if err != nil { | |||||
panic(err) | |||||
} | |||||
// if we have no pubkey yet, store it for future queries | |||||
if pk.CachedPubKey.Empty() { | |||||
pk.CachedPubKey = pub | |||||
} else if !pk.CachedPubKey.Equals(pub) { | |||||
panic("signed with a different key than stored") | |||||
} | |||||
return sig | |||||
} | |||||
// PubKey returns the stored PubKey | |||||
// TODO: query the ledger if not there, once it is not volatile | |||||
func (pk *PrivKeyLedgerEd25519) PubKey() crypto.PubKey { | |||||
key, err := pk.getPubKey() | |||||
if err != nil { | |||||
panic(err) | |||||
} | |||||
return key | |||||
} | |||||
// getPubKey reads the pubkey from cache or from the ledger itself | |||||
// since this involves IO, it may return an error, which is not exposed | |||||
// in the PubKey interface, so this function allows better error handling | |||||
func (pk *PrivKeyLedgerEd25519) getPubKey() (key crypto.PubKey, err error) { | |||||
// if we have no pubkey, set it | |||||
if pk.CachedPubKey.Empty() { | |||||
pk.CachedPubKey, err = pk.forceGetPubKey() | |||||
} | |||||
return pk.CachedPubKey, err | |||||
} | |||||
// forceGetPubKey is like getPubKey but ignores any cached key | |||||
// and ensures we get it from the ledger itself. | |||||
func (pk *PrivKeyLedgerEd25519) forceGetPubKey() (key crypto.PubKey, err error) { | |||||
dev, err := getLedger() | |||||
if err != nil { | |||||
return key, errors.New("Can't connect to ledger device") | |||||
} | |||||
key, _, err = signLedger(dev, []byte{0}) | |||||
if err != nil { | |||||
return key, errors.New("Please open cosmos app on the ledger") | |||||
} | |||||
return key, err | |||||
} | |||||
// Equals fulfils PrivKey Interface - makes sure both keys refer to the | |||||
// same | |||||
func (pk *PrivKeyLedgerEd25519) Equals(other crypto.PrivKey) bool { | |||||
if ledger, ok := other.Unwrap().(*PrivKeyLedgerEd25519); ok { | |||||
return pk.CachedPubKey.Equals(ledger.CachedPubKey) | |||||
} | |||||
return false | |||||
} | |||||
// MockPrivKeyLedgerEd25519 behaves as the ledger, but stores a pre-packaged call-response | |||||
// for use in test cases | |||||
type MockPrivKeyLedgerEd25519 struct { | |||||
Msg []byte | |||||
Pub [KeyLength]byte | |||||
Sig [SigLength]byte | |||||
} | |||||
// NewMockKey returns | |||||
func NewMockKey(msg, pubkey, sig string) (pk MockPrivKeyLedgerEd25519) { | |||||
var err error | |||||
pk.Msg, err = hex.DecodeString(msg) | |||||
if err != nil { | |||||
panic(err) | |||||
} | |||||
bpk, err := hex.DecodeString(pubkey) | |||||
if err != nil { | |||||
panic(err) | |||||
} | |||||
bsig, err := hex.DecodeString(sig) | |||||
if err != nil { | |||||
panic(err) | |||||
} | |||||
copy(pk.Pub[:], bpk) | |||||
copy(pk.Sig[:], bsig) | |||||
return pk | |||||
} | |||||
var _ crypto.PrivKeyInner = MockPrivKeyLedgerEd25519{} | |||||
// AssertIsPrivKeyInner fulfils PrivKey Interface | |||||
func (pk MockPrivKeyLedgerEd25519) AssertIsPrivKeyInner() {} | |||||
// Bytes fulfils PrivKey Interface - not supported | |||||
func (pk MockPrivKeyLedgerEd25519) Bytes() []byte { | |||||
return nil | |||||
} | |||||
// Sign returns a real SignatureLedger, if the msg matches what we expect | |||||
func (pk MockPrivKeyLedgerEd25519) Sign(msg []byte) crypto.Signature { | |||||
if !bytes.Equal(pk.Msg, msg) { | |||||
panic("Mock key is for different msg") | |||||
} | |||||
return crypto.SignatureEd25519(pk.Sig).Wrap() | |||||
} | |||||
// PubKey returns a real PubKeyLedgerEd25519, that will verify this signature | |||||
func (pk MockPrivKeyLedgerEd25519) PubKey() crypto.PubKey { | |||||
return PubKeyLedgerEd25519FromBytes(pk.Pub) | |||||
} | |||||
// Equals compares that two Mocks have the same data | |||||
func (pk MockPrivKeyLedgerEd25519) Equals(other crypto.PrivKey) bool { | |||||
if mock, ok := other.Unwrap().(MockPrivKeyLedgerEd25519); ok { | |||||
return bytes.Equal(mock.Pub[:], pk.Pub[:]) && | |||||
bytes.Equal(mock.Sig[:], pk.Sig[:]) && | |||||
bytes.Equal(mock.Msg, pk.Msg) | |||||
} | |||||
return false | |||||
} | |||||
//////////////////////////////////////////// | |||||
// pubkey | |||||
// PubKeyLedgerEd25519 works like a normal Ed25519 except a hash before the verify bytes | |||||
type PubKeyLedgerEd25519 struct { | |||||
crypto.PubKeyEd25519 | |||||
} | |||||
// PubKeyLedgerEd25519FromBytes creates a PubKey from the raw bytes | |||||
func PubKeyLedgerEd25519FromBytes(key [32]byte) crypto.PubKey { | |||||
return PubKeyLedgerEd25519{crypto.PubKeyEd25519(key)}.Wrap() | |||||
} | |||||
// Bytes fulfils pk Interface - no data, just type info | |||||
func (pk PubKeyLedgerEd25519) Bytes() []byte { | |||||
return amino.BinaryBytes(pk.Wrap()) | |||||
} | |||||
// VerifyBytes uses the normal Ed25519 algorithm but a sha512 hash beforehand | |||||
func (pk PubKeyLedgerEd25519) VerifyBytes(msg []byte, sig crypto.Signature) bool { | |||||
hmsg := hashMsg(msg) | |||||
return pk.PubKeyEd25519.VerifyBytes(hmsg, sig) | |||||
} | |||||
// Equals implements PubKey interface | |||||
func (pk PubKeyLedgerEd25519) Equals(other crypto.PubKey) bool { | |||||
if ledger, ok := other.Unwrap().(PubKeyLedgerEd25519); ok { | |||||
return pk.PubKeyEd25519.Equals(ledger.PubKeyEd25519.Wrap()) | |||||
} | |||||
return false | |||||
} | |||||
/*** registration with go-data ***/ | |||||
func init() { | |||||
crypto.PrivKeyMapper. | |||||
RegisterImplementation(&PrivKeyLedgerEd25519{}, NameLedgerEd25519, TypeLedgerEd25519). | |||||
RegisterImplementation(MockPrivKeyLedgerEd25519{}, "mock-ledger", 0x11) | |||||
crypto.PubKeyMapper. | |||||
RegisterImplementation(PubKeyLedgerEd25519{}, NameLedgerEd25519, TypeLedgerEd25519) | |||||
} | |||||
// Wrap fulfils interface for PrivKey struct | |||||
func (pk *PrivKeyLedgerEd25519) Wrap() crypto.PrivKey { | |||||
return crypto.PrivKey{PrivKeyInner: pk} | |||||
} | |||||
// Wrap fulfils interface for PrivKey struct | |||||
func (pk MockPrivKeyLedgerEd25519) Wrap() crypto.PrivKey { | |||||
return crypto.PrivKey{PrivKeyInner: pk} | |||||
} | |||||
// Wrap fulfils interface for PubKey struct | |||||
func (pk PubKeyLedgerEd25519) Wrap() crypto.PubKey { | |||||
return crypto.PubKey{PubKeyInner: pk} | |||||
} |
@ -1,142 +0,0 @@ | |||||
package nano | |||||
import ( | |||||
"encoding/hex" | |||||
"os" | |||||
"testing" | |||||
asrt "github.com/stretchr/testify/assert" | |||||
rqr "github.com/stretchr/testify/require" | |||||
crypto "github.com/tendermint/go-crypto" | |||||
) | |||||
func TestLedgerKeys(t *testing.T) { | |||||
assert, require := asrt.New(t), rqr.New(t) | |||||
cases := []struct { | |||||
msg, pubkey, sig string | |||||
valid bool | |||||
}{ | |||||
0: { | |||||
msg: "F00D", | |||||
pubkey: "8E8754F012C2FDB492183D41437FD837CB81D8BBE731924E2E0DAF43FD3F2C93", | |||||
sig: "787DC03E9E4EE05983E30BAE0DEFB8DB0671DBC2F5874AC93F8D8CA4018F7A42D6F9A9BCEADB422AC8E27CEE9CA205A0B88D22CD686F0A43EB806E8190A3C400", | |||||
valid: true, | |||||
}, | |||||
1: { | |||||
msg: "DEADBEEF", | |||||
pubkey: "0C45ADC887A5463F668533443C829ED13EA8E2E890C778957DC28DB9D2AD5A6C", | |||||
sig: "00ED74EED8FDAC7988A14BF6BC222120CBAC249D569AF4C2ADABFC86B792F97DF73C4919BE4B6B0ACB53547273BF29FBF0A9E0992FFAB6CB6C9B09311FC86A00", | |||||
valid: true, | |||||
}, | |||||
2: { | |||||
msg: "1234567890AA", | |||||
pubkey: "598FC1F0C76363D14D7480736DEEF390D85863360F075792A6975EFA149FD7EA", | |||||
sig: "59AAB7D7BDC4F936B6415DE672A8B77FA6B8B3451CD95B3A631F31F9A05DAEEE5E7E4F89B64DDEBB5F63DC042CA13B8FCB8185F82AD7FD5636FFDA6B0DC9570B", | |||||
valid: true, | |||||
}, | |||||
3: { | |||||
msg: "1234432112344321", | |||||
pubkey: "359E0636E780457294CCA5D2D84DB190C3EDBD6879729C10D3963DEA1D5D8120", | |||||
sig: "616B44EC7A65E7C719C170D669A47DE80C6AC0BB13FBCC89230976F9CC14D4CF9ECF26D4AFBB9FFF625599F1FF6F78EDA15E9F6B6BDCE07CFE9D8C407AC45208", | |||||
valid: true, | |||||
}, | |||||
4: { | |||||
msg: "12344321123443", | |||||
pubkey: "359E0636E780457294CCA5D2D84DB190C3EDBD6879729C10D3963DEA1D5D8120", | |||||
sig: "616B44EC7A65E7C719C170D669A47DE80C6AC0BB13FBCC89230976F9CC14D4CF9ECF26D4AFBB9FFF625599F1FF6F78EDA15E9F6B6BDCE07CFE9D8C407AC45208", | |||||
valid: false, | |||||
}, | |||||
5: { | |||||
msg: "1234432112344321", | |||||
pubkey: "459E0636E780457294CCA5D2D84DB190C3EDBD6879729C10D3963DEA1D5D8120", | |||||
sig: "616B44EC7A65E7C719C170D669A47DE80C6AC0BB13FBCC89230976F9CC14D4CF9ECF26D4AFBB9FFF625599F1FF6F78EDA15E9F6B6BDCE07CFE9D8C407AC45208", | |||||
valid: false, | |||||
}, | |||||
6: { | |||||
msg: "1234432112344321", | |||||
pubkey: "359E0636E780457294CCA5D2D84DB190C3EDBD6879729C10D3963DEA1D5D8120", | |||||
sig: "716B44EC7A65E7C719C170D669A47DE80C6AC0BB13FBCC89230976F9CC14D4CF9ECF26D4AFBB9FFF625599F1FF6F78EDA15E9F6B6BDCE07CFE9D8C407AC45208", | |||||
valid: false, | |||||
}, | |||||
} | |||||
for i, tc := range cases { | |||||
bmsg, err := hex.DecodeString(tc.msg) | |||||
require.NoError(err, "%d", i) | |||||
priv := NewMockKey(tc.msg, tc.pubkey, tc.sig) | |||||
pub := priv.PubKey() | |||||
sig := priv.Sign(bmsg) | |||||
valid := pub.VerifyBytes(bmsg, sig) | |||||
assert.Equal(tc.valid, valid, "%d", i) | |||||
} | |||||
} | |||||
func TestRealLedger(t *testing.T) { | |||||
assert, require := asrt.New(t), rqr.New(t) | |||||
if os.Getenv("WITH_LEDGER") == "" { | |||||
t.Skip("Set WITH_LEDGER to run code on real ledger") | |||||
} | |||||
msg := []byte("kuhehfeohg") | |||||
priv, err := NewPrivKeyLedgerEd25519() | |||||
require.Nil(err, "%+v", err) | |||||
pub := priv.PubKey() | |||||
sig := priv.Sign(msg) | |||||
valid := pub.VerifyBytes(msg, sig) | |||||
assert.True(valid) | |||||
// now, let's serialize the key and make sure it still works | |||||
bs := priv.Bytes() | |||||
priv2, err := crypto.PrivKeyFromBytes(bs) | |||||
require.Nil(err, "%+v", err) | |||||
// make sure we get the same pubkey when we load from disk | |||||
pub2 := priv2.PubKey() | |||||
require.Equal(pub, pub2) | |||||
// signing with the loaded key should match the original pubkey | |||||
sig = priv2.Sign(msg) | |||||
valid = pub.VerifyBytes(msg, sig) | |||||
assert.True(valid) | |||||
// make sure pubkeys serialize properly as well | |||||
bs = pub.Bytes() | |||||
bpub, err := crypto.PubKeyFromBytes(bs) | |||||
require.NoError(err) | |||||
assert.Equal(pub, bpub) | |||||
} | |||||
// TestRealLedgerErrorHandling calls. These tests assume | |||||
// the ledger is not plugged in.... | |||||
func TestRealLedgerErrorHandling(t *testing.T) { | |||||
require := rqr.New(t) | |||||
if os.Getenv("WITH_LEDGER") != "" { | |||||
t.Skip("Skipping on WITH_LEDGER as it tests unplugged cases") | |||||
} | |||||
// first, try to generate a key, must return an error | |||||
// (no panic) | |||||
_, err := NewPrivKeyLedgerEd25519() | |||||
require.Error(err) | |||||
led := PrivKeyLedgerEd25519{} // empty | |||||
// or with some pub key | |||||
ed := crypto.GenPrivKeyEd25519() | |||||
led2 := PrivKeyLedgerEd25519{CachedPubKey: ed.PubKey()} | |||||
// loading these should return errors | |||||
bs := led.Bytes() | |||||
_, err = crypto.PrivKeyFromBytes(bs) | |||||
require.Error(err) | |||||
bs = led2.Bytes() | |||||
_, err = crypto.PrivKeyFromBytes(bs) | |||||
require.Error(err) | |||||
} |
@ -1,63 +0,0 @@ | |||||
package nano | |||||
import ( | |||||
"bytes" | |||||
"crypto/sha512" | |||||
"github.com/pkg/errors" | |||||
) | |||||
const ( | |||||
App = 0x80 | |||||
Init = 0x00 | |||||
Update = 0x01 | |||||
Digest = 0x02 | |||||
MaxChunk = 253 | |||||
KeyLength = 32 | |||||
SigLength = 64 | |||||
) | |||||
var separator = []byte{0, 0xCA, 0xFE, 0} | |||||
func generateSignRequests(payload []byte) [][]byte { | |||||
// nice one-shot | |||||
digest := []byte{App, Digest} | |||||
if len(payload) < MaxChunk { | |||||
return [][]byte{append(digest, payload...)} | |||||
} | |||||
// large payload is multi-chunk | |||||
result := [][]byte{{App, Init}} | |||||
update := []byte{App, Update} | |||||
for len(payload) > MaxChunk { | |||||
msg := append(update, payload[:MaxChunk]...) | |||||
payload = payload[MaxChunk:] | |||||
result = append(result, msg) | |||||
} | |||||
result = append(result, append(update, payload...)) | |||||
result = append(result, digest) | |||||
return result | |||||
} | |||||
func parseDigest(resp []byte) (key, sig []byte, err error) { | |||||
if resp[0] != App || resp[1] != Digest { | |||||
return nil, nil, errors.New("Invalid header") | |||||
} | |||||
resp = resp[2:] | |||||
if len(resp) != KeyLength+SigLength+len(separator) { | |||||
return nil, nil, errors.Errorf("Incorrect length: %d", len(resp)) | |||||
} | |||||
key, resp = resp[:KeyLength], resp[KeyLength:] | |||||
if !bytes.Equal(separator, resp[:len(separator)]) { | |||||
return nil, nil, errors.New("Cannot find 0xCAFE") | |||||
} | |||||
sig = resp[len(separator):] | |||||
return key, sig, nil | |||||
} | |||||
func hashMsg(data []byte) []byte { | |||||
res := sha512.Sum512(data) | |||||
return res[:] | |||||
} |
@ -1,160 +0,0 @@ | |||||
package nano | |||||
import ( | |||||
"encoding/hex" | |||||
"testing" | |||||
"github.com/pkg/errors" | |||||
asrt "github.com/stretchr/testify/assert" | |||||
rqr "github.com/stretchr/testify/require" | |||||
crypto "github.com/tendermint/go-crypto" | |||||
) | |||||
func parseEdKey(data []byte) (key crypto.PubKey, err error) { | |||||
ed := crypto.PubKeyEd25519{} | |||||
if len(data) < len(ed) { | |||||
return key, errors.Errorf("Key length too short: %d", len(data)) | |||||
} | |||||
copy(ed[:], data) | |||||
return ed.Wrap(), nil | |||||
} | |||||
func parseSig(data []byte) (key crypto.Signature, err error) { | |||||
ed := crypto.SignatureEd25519{} | |||||
if len(data) < len(ed) { | |||||
return key, errors.Errorf("Sig length too short: %d", len(data)) | |||||
} | |||||
copy(ed[:], data) | |||||
return ed.Wrap(), nil | |||||
} | |||||
func TestParseDigest(t *testing.T) { | |||||
assert, require := asrt.New(t), rqr.New(t) | |||||
cases := []struct { | |||||
output string | |||||
key string | |||||
sig string | |||||
valid bool | |||||
}{ | |||||
{ | |||||
output: "80028E8754F012C2FDB492183D41437FD837CB81D8BBE731924E2E0DAF43FD3F2C9300CAFE00787DC03E9E4EE05983E30BAE0DEFB8DB0671DBC2F5874AC93F8D8CA4018F7A42D6F9A9BCEADB422AC8E27CEE9CA205A0B88D22CD686F0A43EB806E8190A3C400", | |||||
key: "8E8754F012C2FDB492183D41437FD837CB81D8BBE731924E2E0DAF43FD3F2C93", | |||||
sig: "787DC03E9E4EE05983E30BAE0DEFB8DB0671DBC2F5874AC93F8D8CA4018F7A42D6F9A9BCEADB422AC8E27CEE9CA205A0B88D22CD686F0A43EB806E8190A3C400", | |||||
valid: true, | |||||
}, | |||||
{ | |||||
output: "800235467890876543525437890796574535467890", | |||||
key: "", | |||||
sig: "", | |||||
valid: false, | |||||
}, | |||||
} | |||||
for i, tc := range cases { | |||||
msg, err := hex.DecodeString(tc.output) | |||||
require.Nil(err, "%d: %+v", i, err) | |||||
lKey, lSig, err := parseDigest(msg) | |||||
if !tc.valid { | |||||
assert.NotNil(err, "%d", i) | |||||
} else if assert.Nil(err, "%d: %+v", i, err) { | |||||
key, err := hex.DecodeString(tc.key) | |||||
require.Nil(err, "%d: %+v", i, err) | |||||
sig, err := hex.DecodeString(tc.sig) | |||||
require.Nil(err, "%d: %+v", i, err) | |||||
assert.Equal(key, lKey, "%d", i) | |||||
assert.Equal(sig, lSig, "%d", i) | |||||
} | |||||
} | |||||
} | |||||
type cryptoCase struct { | |||||
msg string | |||||
key string | |||||
sig string | |||||
valid bool | |||||
} | |||||
func toBytes(c cryptoCase) (msg, key, sig []byte, err error) { | |||||
msg, err = hex.DecodeString(c.msg) | |||||
if err != nil { | |||||
return | |||||
} | |||||
key, err = hex.DecodeString(c.key) | |||||
if err != nil { | |||||
return | |||||
} | |||||
sig, err = hex.DecodeString(c.sig) | |||||
return | |||||
} | |||||
func TestCryptoConvert(t *testing.T) { | |||||
assert, require := asrt.New(t), rqr.New(t) | |||||
cases := []cryptoCase{ | |||||
0: { | |||||
msg: "F00D", | |||||
key: "8E8754F012C2FDB492183D41437FD837CB81D8BBE731924E2E0DAF43FD3F2C93", | |||||
sig: "787DC03E9E4EE05983E30BAE0DEFB8DB0671DBC2F5874AC93F8D8CA4018F7A42D6F9A9BCEADB422AC8E27CEE9CA205A0B88D22CD686F0A43EB806E8190A3C400", | |||||
valid: true, | |||||
}, | |||||
1: { | |||||
msg: "DEADBEEF", | |||||
key: "0C45ADC887A5463F668533443C829ED13EA8E2E890C778957DC28DB9D2AD5A6C", | |||||
sig: "00ED74EED8FDAC7988A14BF6BC222120CBAC249D569AF4C2ADABFC86B792F97DF73C4919BE4B6B0ACB53547273BF29FBF0A9E0992FFAB6CB6C9B09311FC86A00", | |||||
valid: true, | |||||
}, | |||||
2: { | |||||
msg: "1234567890AA", | |||||
key: "598FC1F0C76363D14D7480736DEEF390D85863360F075792A6975EFA149FD7EA", | |||||
sig: "59AAB7D7BDC4F936B6415DE672A8B77FA6B8B3451CD95B3A631F31F9A05DAEEE5E7E4F89B64DDEBB5F63DC042CA13B8FCB8185F82AD7FD5636FFDA6B0DC9570B", | |||||
valid: true, | |||||
}, | |||||
3: { | |||||
msg: "1234432112344321", | |||||
key: "359E0636E780457294CCA5D2D84DB190C3EDBD6879729C10D3963DEA1D5D8120", | |||||
sig: "616B44EC7A65E7C719C170D669A47DE80C6AC0BB13FBCC89230976F9CC14D4CF9ECF26D4AFBB9FFF625599F1FF6F78EDA15E9F6B6BDCE07CFE9D8C407AC45208", | |||||
valid: true, | |||||
}, | |||||
4: { | |||||
msg: "12344321123443", | |||||
key: "359E0636E780457294CCA5D2D84DB190C3EDBD6879729C10D3963DEA1D5D8120", | |||||
sig: "616B44EC7A65E7C719C170D669A47DE80C6AC0BB13FBCC89230976F9CC14D4CF9ECF26D4AFBB9FFF625599F1FF6F78EDA15E9F6B6BDCE07CFE9D8C407AC45208", | |||||
valid: false, | |||||
}, | |||||
5: { | |||||
msg: "1234432112344321", | |||||
key: "459E0636E780457294CCA5D2D84DB190C3EDBD6879729C10D3963DEA1D5D8120", | |||||
sig: "616B44EC7A65E7C719C170D669A47DE80C6AC0BB13FBCC89230976F9CC14D4CF9ECF26D4AFBB9FFF625599F1FF6F78EDA15E9F6B6BDCE07CFE9D8C407AC45208", | |||||
valid: false, | |||||
}, | |||||
6: { | |||||
msg: "1234432112344321", | |||||
key: "359E0636E780457294CCA5D2D84DB190C3EDBD6879729C10D3963DEA1D5D8120", | |||||
sig: "716B44EC7A65E7C719C170D669A47DE80C6AC0BB13FBCC89230976F9CC14D4CF9ECF26D4AFBB9FFF625599F1FF6F78EDA15E9F6B6BDCE07CFE9D8C407AC45208", | |||||
valid: false, | |||||
}, | |||||
} | |||||
for i, tc := range cases { | |||||
msg, key, sig, err := toBytes(tc) | |||||
require.Nil(err, "%d: %+v", i, err) | |||||
pk, err := parseEdKey(key) | |||||
require.Nil(err, "%d: %+v", i, err) | |||||
psig, err := parseSig(sig) | |||||
require.Nil(err, "%d: %+v", i, err) | |||||
// it is not the signature of the message itself | |||||
valid := pk.VerifyBytes(msg, psig) | |||||
assert.False(valid, "%d", i) | |||||
// but rather of the hash of the msg | |||||
hmsg := hashMsg(msg) | |||||
valid = pk.VerifyBytes(hmsg, psig) | |||||
assert.Equal(tc.valid, valid, "%d", i) | |||||
} | |||||
} |
@ -0,0 +1,4 @@ | |||||
## Simple Merkle Tree | |||||
For smaller static data structures that don't require immutable snapshots or mutability; | |||||
for instance the transactions and validation signatures of a block can be hashed using this simple merkle tree logic. |
@ -0,0 +1,31 @@ | |||||
/* | |||||
Package merkle computes a deterministic minimal height Merkle tree hash. | |||||
If the number of items is not a power of two, some leaves | |||||
will be at different levels. Tries to keep both sides of | |||||
the tree the same size, but the left may be one greater. | |||||
Use this for short deterministic trees, such as the validator list. | |||||
For larger datasets, use IAVLTree. | |||||
Be aware that the current implementation by itself does not prevent | |||||
second pre-image attacks. Hence, use this library with caution. | |||||
Otherwise you might run into similar issues as, e.g., in early Bitcoin: | |||||
https://bitcointalk.org/?topic=102395 | |||||
* | |||||
/ \ | |||||
/ \ | |||||
/ \ | |||||
/ \ | |||||
* * | |||||
/ \ / \ | |||||
/ \ / \ | |||||
/ \ / \ | |||||
* * * h6 | |||||
/ \ / \ / \ | |||||
h0 h1 h2 h3 h4 h5 | |||||
TODO(ismail): add 2nd pre-image protection or clarify further on how we use this and why this secure. | |||||
*/ | |||||
package merkle |
@ -0,0 +1,91 @@ | |||||
package merkle | |||||
import ( | |||||
"github.com/tendermint/go-crypto/tmhash" | |||||
cmn "github.com/tendermint/tmlibs/common" | |||||
) | |||||
// Merkle tree from a map. | |||||
// Leaves are `hash(key) | hash(value)`. | |||||
// Leaves are sorted before Merkle hashing. | |||||
type simpleMap struct { | |||||
kvs cmn.KVPairs | |||||
sorted bool | |||||
} | |||||
func newSimpleMap() *simpleMap { | |||||
return &simpleMap{ | |||||
kvs: nil, | |||||
sorted: false, | |||||
} | |||||
} | |||||
// Set hashes the key and value and appends it to the kv pairs. | |||||
func (sm *simpleMap) Set(key string, value Hasher) { | |||||
sm.sorted = false | |||||
// Hash the key to blind it... why not? | |||||
khash := tmhash.Sum([]byte(key)) | |||||
// And the value is hashed too, so you can | |||||
// check for equality with a cached value (say) | |||||
// and make a determination to fetch or not. | |||||
vhash := value.Hash() | |||||
sm.kvs = append(sm.kvs, cmn.KVPair{ | |||||
Key: khash, | |||||
Value: vhash, | |||||
}) | |||||
} | |||||
// Hash Merkle root hash of items sorted by key | |||||
// (UNSTABLE: and by value too if duplicate key). | |||||
func (sm *simpleMap) Hash() []byte { | |||||
sm.Sort() | |||||
return hashKVPairs(sm.kvs) | |||||
} | |||||
func (sm *simpleMap) Sort() { | |||||
if sm.sorted { | |||||
return | |||||
} | |||||
sm.kvs.Sort() | |||||
sm.sorted = true | |||||
} | |||||
// Returns a copy of sorted KVPairs. | |||||
// NOTE these contain the hashed key and value. | |||||
func (sm *simpleMap) KVPairs() cmn.KVPairs { | |||||
sm.Sort() | |||||
kvs := make(cmn.KVPairs, len(sm.kvs)) | |||||
copy(kvs, sm.kvs) | |||||
return kvs | |||||
} | |||||
//---------------------------------------- | |||||
// A local extension to KVPair that can be hashed. | |||||
// Key and value are length prefixed and concatenated, | |||||
// then hashed. | |||||
type kvPair cmn.KVPair | |||||
func (kv kvPair) Hash() []byte { | |||||
hasher := tmhash.New() | |||||
err := encodeByteSlice(hasher, kv.Key) | |||||
if err != nil { | |||||
panic(err) | |||||
} | |||||
err = encodeByteSlice(hasher, kv.Value) | |||||
if err != nil { | |||||
panic(err) | |||||
} | |||||
return hasher.Sum(nil) | |||||
} | |||||
func hashKVPairs(kvs cmn.KVPairs) []byte { | |||||
kvsH := make([]Hasher, len(kvs)) | |||||
for i, kvp := range kvs { | |||||
kvsH[i] = kvPair(kvp) | |||||
} | |||||
return SimpleHashFromHashers(kvsH) | |||||
} |
@ -0,0 +1,54 @@ | |||||
package merkle | |||||
import ( | |||||
"fmt" | |||||
"testing" | |||||
"github.com/stretchr/testify/assert" | |||||
"github.com/tendermint/go-crypto/tmhash" | |||||
) | |||||
type strHasher string | |||||
func (str strHasher) Hash() []byte { | |||||
return tmhash.Sum([]byte(str)) | |||||
} | |||||
func TestSimpleMap(t *testing.T) { | |||||
{ | |||||
db := newSimpleMap() | |||||
db.Set("key1", strHasher("value1")) | |||||
assert.Equal(t, "3dafc06a52039d029be57c75c9d16356a4256ef4", fmt.Sprintf("%x", db.Hash()), "Hash didn't match") | |||||
} | |||||
{ | |||||
db := newSimpleMap() | |||||
db.Set("key1", strHasher("value2")) | |||||
assert.Equal(t, "03eb5cfdff646bc4e80fec844e72fd248a1c6b2c", fmt.Sprintf("%x", db.Hash()), "Hash didn't match") | |||||
} | |||||
{ | |||||
db := newSimpleMap() | |||||
db.Set("key1", strHasher("value1")) | |||||
db.Set("key2", strHasher("value2")) | |||||
assert.Equal(t, "acc3971eab8513171cc90ce8b74f368c38f9657d", fmt.Sprintf("%x", db.Hash()), "Hash didn't match") | |||||
} | |||||
{ | |||||
db := newSimpleMap() | |||||
db.Set("key2", strHasher("value2")) // NOTE: out of order | |||||
db.Set("key1", strHasher("value1")) | |||||
assert.Equal(t, "acc3971eab8513171cc90ce8b74f368c38f9657d", fmt.Sprintf("%x", db.Hash()), "Hash didn't match") | |||||
} | |||||
{ | |||||
db := newSimpleMap() | |||||
db.Set("key1", strHasher("value1")) | |||||
db.Set("key2", strHasher("value2")) | |||||
db.Set("key3", strHasher("value3")) | |||||
assert.Equal(t, "0cd117ad14e6cd22edcd9aa0d84d7063b54b862f", fmt.Sprintf("%x", db.Hash()), "Hash didn't match") | |||||
} | |||||
{ | |||||
db := newSimpleMap() | |||||
db.Set("key2", strHasher("value2")) // NOTE: out of order | |||||
db.Set("key1", strHasher("value1")) | |||||
db.Set("key3", strHasher("value3")) | |||||
assert.Equal(t, "0cd117ad14e6cd22edcd9aa0d84d7063b54b862f", fmt.Sprintf("%x", db.Hash()), "Hash didn't match") | |||||
} | |||||
} |
@ -0,0 +1,152 @@ | |||||
package merkle | |||||
import ( | |||||
"bytes" | |||||
"fmt" | |||||
) | |||||
// SimpleProof represents a simple merkle proof. | |||||
type SimpleProof struct { | |||||
Aunts [][]byte `json:"aunts"` // Hashes from leaf's sibling to a root's child. | |||||
} | |||||
// SimpleProofsFromHashers computes inclusion proof for given items. | |||||
// proofs[0] is the proof for items[0]. | |||||
func SimpleProofsFromHashers(items []Hasher) (rootHash []byte, proofs []*SimpleProof) { | |||||
trails, rootSPN := trailsFromHashers(items) | |||||
rootHash = rootSPN.Hash | |||||
proofs = make([]*SimpleProof, len(items)) | |||||
for i, trail := range trails { | |||||
proofs[i] = &SimpleProof{ | |||||
Aunts: trail.FlattenAunts(), | |||||
} | |||||
} | |||||
return | |||||
} | |||||
// SimpleProofsFromMap generates proofs from a map. The keys/values of the map will be used as the keys/values | |||||
// in the underlying key-value pairs. | |||||
// The keys are sorted before the proofs are computed. | |||||
func SimpleProofsFromMap(m map[string]Hasher) (rootHash []byte, proofs []*SimpleProof) { | |||||
sm := newSimpleMap() | |||||
for k, v := range m { | |||||
sm.Set(k, v) | |||||
} | |||||
sm.Sort() | |||||
kvs := sm.kvs | |||||
kvsH := make([]Hasher, 0, len(kvs)) | |||||
for _, kvp := range kvs { | |||||
kvsH = append(kvsH, kvPair(kvp)) | |||||
} | |||||
return SimpleProofsFromHashers(kvsH) | |||||
} | |||||
// Verify that leafHash is a leaf hash of the simple-merkle-tree | |||||
// which hashes to rootHash. | |||||
func (sp *SimpleProof) Verify(index int, total int, leafHash []byte, rootHash []byte) bool { | |||||
computedHash := computeHashFromAunts(index, total, leafHash, sp.Aunts) | |||||
return computedHash != nil && bytes.Equal(computedHash, rootHash) | |||||
} | |||||
// String implements the stringer interface for SimpleProof. | |||||
// It is a wrapper around StringIndented. | |||||
func (sp *SimpleProof) String() string { | |||||
return sp.StringIndented("") | |||||
} | |||||
// StringIndented generates a canonical string representation of a SimpleProof. | |||||
func (sp *SimpleProof) StringIndented(indent string) string { | |||||
return fmt.Sprintf(`SimpleProof{ | |||||
%s Aunts: %X | |||||
%s}`, | |||||
indent, sp.Aunts, | |||||
indent) | |||||
} | |||||
// Use the leafHash and innerHashes to get the root merkle hash. | |||||
// If the length of the innerHashes slice isn't exactly correct, the result is nil. | |||||
// Recursive impl. | |||||
func computeHashFromAunts(index int, total int, leafHash []byte, innerHashes [][]byte) []byte { | |||||
if index >= total || index < 0 || total <= 0 { | |||||
return nil | |||||
} | |||||
switch total { | |||||
case 0: | |||||
panic("Cannot call computeHashFromAunts() with 0 total") | |||||
case 1: | |||||
if len(innerHashes) != 0 { | |||||
return nil | |||||
} | |||||
return leafHash | |||||
default: | |||||
if len(innerHashes) == 0 { | |||||
return nil | |||||
} | |||||
numLeft := (total + 1) / 2 | |||||
if index < numLeft { | |||||
leftHash := computeHashFromAunts(index, numLeft, leafHash, innerHashes[:len(innerHashes)-1]) | |||||
if leftHash == nil { | |||||
return nil | |||||
} | |||||
return SimpleHashFromTwoHashes(leftHash, innerHashes[len(innerHashes)-1]) | |||||
} | |||||
rightHash := computeHashFromAunts(index-numLeft, total-numLeft, leafHash, innerHashes[:len(innerHashes)-1]) | |||||
if rightHash == nil { | |||||
return nil | |||||
} | |||||
return SimpleHashFromTwoHashes(innerHashes[len(innerHashes)-1], rightHash) | |||||
} | |||||
} | |||||
// SimpleProofNode is a helper structure to construct merkle proof. | |||||
// The node and the tree is thrown away afterwards. | |||||
// Exactly one of node.Left and node.Right is nil, unless node is the root, in which case both are nil. | |||||
// node.Parent.Hash = hash(node.Hash, node.Right.Hash) or | |||||
// hash(node.Left.Hash, node.Hash), depending on whether node is a left/right child. | |||||
type SimpleProofNode struct { | |||||
Hash []byte | |||||
Parent *SimpleProofNode | |||||
Left *SimpleProofNode // Left sibling (only one of Left,Right is set) | |||||
Right *SimpleProofNode // Right sibling (only one of Left,Right is set) | |||||
} | |||||
// FlattenAunts will return the inner hashes for the item corresponding to the leaf, | |||||
// starting from a leaf SimpleProofNode. | |||||
func (spn *SimpleProofNode) FlattenAunts() [][]byte { | |||||
// Nonrecursive impl. | |||||
innerHashes := [][]byte{} | |||||
for spn != nil { | |||||
if spn.Left != nil { | |||||
innerHashes = append(innerHashes, spn.Left.Hash) | |||||
} else if spn.Right != nil { | |||||
innerHashes = append(innerHashes, spn.Right.Hash) | |||||
} else { | |||||
break | |||||
} | |||||
spn = spn.Parent | |||||
} | |||||
return innerHashes | |||||
} | |||||
// trails[0].Hash is the leaf hash for items[0]. | |||||
// trails[i].Parent.Parent....Parent == root for all i. | |||||
func trailsFromHashers(items []Hasher) (trails []*SimpleProofNode, root *SimpleProofNode) { | |||||
// Recursive impl. | |||||
switch len(items) { | |||||
case 0: | |||||
return nil, nil | |||||
case 1: | |||||
trail := &SimpleProofNode{items[0].Hash(), nil, nil, nil} | |||||
return []*SimpleProofNode{trail}, trail | |||||
default: | |||||
lefts, leftRoot := trailsFromHashers(items[:(len(items)+1)/2]) | |||||
rights, rightRoot := trailsFromHashers(items[(len(items)+1)/2:]) | |||||
rootHash := SimpleHashFromTwoHashes(leftRoot.Hash, rightRoot.Hash) | |||||
root := &SimpleProofNode{rootHash, nil, nil, nil} | |||||
leftRoot.Parent = root | |||||
leftRoot.Right = rightRoot | |||||
rightRoot.Parent = root | |||||
rightRoot.Left = leftRoot | |||||
return append(lefts, rights...), root | |||||
} | |||||
} |
@ -0,0 +1,58 @@ | |||||
package merkle | |||||
import ( | |||||
"github.com/tendermint/go-crypto/tmhash" | |||||
) | |||||
// SimpleHashFromTwoHashes is the basic operation of the Merkle tree: Hash(left | right). | |||||
func SimpleHashFromTwoHashes(left, right []byte) []byte { | |||||
var hasher = tmhash.New() | |||||
err := encodeByteSlice(hasher, left) | |||||
if err != nil { | |||||
panic(err) | |||||
} | |||||
err = encodeByteSlice(hasher, right) | |||||
if err != nil { | |||||
panic(err) | |||||
} | |||||
return hasher.Sum(nil) | |||||
} | |||||
// SimpleHashFromHashers computes a Merkle tree from items that can be hashed. | |||||
func SimpleHashFromHashers(items []Hasher) []byte { | |||||
hashes := make([][]byte, len(items)) | |||||
for i, item := range items { | |||||
hash := item.Hash() | |||||
hashes[i] = hash | |||||
} | |||||
return simpleHashFromHashes(hashes) | |||||
} | |||||
// SimpleHashFromMap computes a Merkle tree from sorted map. | |||||
// Like calling SimpleHashFromHashers with | |||||
// `item = []byte(Hash(key) | Hash(value))`, | |||||
// sorted by `item`. | |||||
func SimpleHashFromMap(m map[string]Hasher) []byte { | |||||
sm := newSimpleMap() | |||||
for k, v := range m { | |||||
sm.Set(k, v) | |||||
} | |||||
return sm.Hash() | |||||
} | |||||
//---------------------------------------------------------------- | |||||
// Expects hashes! | |||||
func simpleHashFromHashes(hashes [][]byte) []byte { | |||||
// Recursive impl. | |||||
switch len(hashes) { | |||||
case 0: | |||||
return nil | |||||
case 1: | |||||
return hashes[0] | |||||
default: | |||||
left := simpleHashFromHashes(hashes[:(len(hashes)+1)/2]) | |||||
right := simpleHashFromHashes(hashes[(len(hashes)+1)/2:]) | |||||
return SimpleHashFromTwoHashes(left, right) | |||||
} | |||||
} |
@ -0,0 +1,88 @@ | |||||
package merkle | |||||
import ( | |||||
"bytes" | |||||
cmn "github.com/tendermint/tmlibs/common" | |||||
. "github.com/tendermint/tmlibs/test" | |||||
"testing" | |||||
"github.com/tendermint/go-crypto/tmhash" | |||||
) | |||||
type testItem []byte | |||||
func (tI testItem) Hash() []byte { | |||||
return []byte(tI) | |||||
} | |||||
func TestSimpleProof(t *testing.T) { | |||||
total := 100 | |||||
items := make([]Hasher, total) | |||||
for i := 0; i < total; i++ { | |||||
items[i] = testItem(cmn.RandBytes(tmhash.Size)) | |||||
} | |||||
rootHash := SimpleHashFromHashers(items) | |||||
rootHash2, proofs := SimpleProofsFromHashers(items) | |||||
if !bytes.Equal(rootHash, rootHash2) { | |||||
t.Errorf("Unmatched root hashes: %X vs %X", rootHash, rootHash2) | |||||
} | |||||
// For each item, check the trail. | |||||
for i, item := range items { | |||||
itemHash := item.Hash() | |||||
proof := proofs[i] | |||||
// Verify success | |||||
ok := proof.Verify(i, total, itemHash, rootHash) | |||||
if !ok { | |||||
t.Errorf("Verification failed for index %v.", i) | |||||
} | |||||
// Wrong item index should make it fail | |||||
{ | |||||
ok = proof.Verify((i+1)%total, total, itemHash, rootHash) | |||||
if ok { | |||||
t.Errorf("Expected verification to fail for wrong index %v.", i) | |||||
} | |||||
} | |||||
// Trail too long should make it fail | |||||
origAunts := proof.Aunts | |||||
proof.Aunts = append(proof.Aunts, cmn.RandBytes(32)) | |||||
{ | |||||
ok = proof.Verify(i, total, itemHash, rootHash) | |||||
if ok { | |||||
t.Errorf("Expected verification to fail for wrong trail length.") | |||||
} | |||||
} | |||||
proof.Aunts = origAunts | |||||
// Trail too short should make it fail | |||||
proof.Aunts = proof.Aunts[0 : len(proof.Aunts)-1] | |||||
{ | |||||
ok = proof.Verify(i, total, itemHash, rootHash) | |||||
if ok { | |||||
t.Errorf("Expected verification to fail for wrong trail length.") | |||||
} | |||||
} | |||||
proof.Aunts = origAunts | |||||
// Mutating the itemHash should make it fail. | |||||
ok = proof.Verify(i, total, MutateByteSlice(itemHash), rootHash) | |||||
if ok { | |||||
t.Errorf("Expected verification to fail for mutated leaf hash") | |||||
} | |||||
// Mutating the rootHash should make it fail. | |||||
ok = proof.Verify(i, total, itemHash, MutateByteSlice(rootHash)) | |||||
if ok { | |||||
t.Errorf("Expected verification to fail for mutated root hash") | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,38 @@ | |||||
package merkle | |||||
import ( | |||||
"io" | |||||
amino "github.com/tendermint/go-amino" | |||||
) | |||||
// Tree is a Merkle tree interface. | |||||
type Tree interface { | |||||
Size() (size int) | |||||
Height() (height int8) | |||||
Has(key []byte) (has bool) | |||||
Proof(key []byte) (value []byte, proof []byte, exists bool) // TODO make it return an index | |||||
Get(key []byte) (index int, value []byte, exists bool) | |||||
GetByIndex(index int) (key []byte, value []byte) | |||||
Set(key []byte, value []byte) (updated bool) | |||||
Remove(key []byte) (value []byte, removed bool) | |||||
HashWithCount() (hash []byte, count int) | |||||
Hash() (hash []byte) | |||||
Save() (hash []byte) | |||||
Load(hash []byte) | |||||
Copy() Tree | |||||
Iterate(func(key []byte, value []byte) (stop bool)) (stopped bool) | |||||
IterateRange(start []byte, end []byte, ascending bool, fx func(key []byte, value []byte) (stop bool)) (stopped bool) | |||||
} | |||||
// Hasher represents a hashable piece of data which can be hashed in the Tree. | |||||
type Hasher interface { | |||||
Hash() []byte | |||||
} | |||||
//----------------------------------------------------------------------- | |||||
// Uvarint length prefixed byteslice | |||||
func encodeByteSlice(w io.Writer, bz []byte) (err error) { | |||||
return amino.EncodeByteSlice(w, bz) | |||||
} |
@ -0,0 +1,48 @@ | |||||
package tmhash | |||||
import ( | |||||
"crypto/sha256" | |||||
"hash" | |||||
) | |||||
const ( | |||||
Size = 20 | |||||
BlockSize = sha256.BlockSize | |||||
) | |||||
type sha256trunc struct { | |||||
sha256 hash.Hash | |||||
} | |||||
func (h sha256trunc) Write(p []byte) (n int, err error) { | |||||
return h.sha256.Write(p) | |||||
} | |||||
func (h sha256trunc) Sum(b []byte) []byte { | |||||
shasum := h.sha256.Sum(b) | |||||
return shasum[:Size] | |||||
} | |||||
func (h sha256trunc) Reset() { | |||||
h.sha256.Reset() | |||||
} | |||||
func (h sha256trunc) Size() int { | |||||
return Size | |||||
} | |||||
func (h sha256trunc) BlockSize() int { | |||||
return h.sha256.BlockSize() | |||||
} | |||||
// New returns a new hash.Hash. | |||||
func New() hash.Hash { | |||||
return sha256trunc{ | |||||
sha256: sha256.New(), | |||||
} | |||||
} | |||||
// Sum returns the first 20 bytes of SHA256 of the bz. | |||||
func Sum(bz []byte) []byte { | |||||
hash := sha256.Sum256(bz) | |||||
return hash[:Size] | |||||
} |
@ -0,0 +1,23 @@ | |||||
package tmhash_test | |||||
import ( | |||||
"crypto/sha256" | |||||
"testing" | |||||
"github.com/stretchr/testify/assert" | |||||
"github.com/tendermint/go-crypto/tmhash" | |||||
) | |||||
func TestHash(t *testing.T) { | |||||
testVector := []byte("abc") | |||||
hasher := tmhash.New() | |||||
hasher.Write(testVector) | |||||
bz := hasher.Sum(nil) | |||||
hasher = sha256.New() | |||||
hasher.Write(testVector) | |||||
bz2 := hasher.Sum(nil) | |||||
bz2 = bz2[:20] | |||||
assert.Equal(t, bz, bz2) | |||||
} |
@ -1,3 +1,3 @@ | |||||
package crypto | package crypto | ||||
const Version = "0.6.2" | |||||
const Version = "0.7.0" |
@ -0,0 +1,238 @@ | |||||
package xchacha20poly1305 | |||||
import ( | |||||
"crypto/cipher" | |||||
"encoding/binary" | |||||
"errors" | |||||
"fmt" | |||||
"golang.org/x/crypto/chacha20poly1305" | |||||
) | |||||
var sigma = [4]uint32{0x61707865, 0x3320646e, 0x79622d32, 0x6b206574} | |||||
type xchacha20poly1305 struct { | |||||
key [KeySize]byte | |||||
} | |||||
const ( | |||||
// KeySize is the size of the key used by this AEAD, in bytes. | |||||
KeySize = 32 | |||||
// NonceSize is the size of the nonce used with this AEAD, in bytes. | |||||
NonceSize = 24 | |||||
) | |||||
//New xChaChapoly1305 AEAD with 24 byte nonces | |||||
func New(key []byte) (cipher.AEAD, error) { | |||||
if len(key) != KeySize { | |||||
return nil, errors.New("chacha20poly1305: bad key length") | |||||
} | |||||
ret := new(xchacha20poly1305) | |||||
copy(ret.key[:], key) | |||||
return ret, nil | |||||
} | |||||
func (c *xchacha20poly1305) NonceSize() int { | |||||
return NonceSize | |||||
} | |||||
func (c *xchacha20poly1305) Overhead() int { | |||||
return 16 | |||||
} | |||||
func (c *xchacha20poly1305) Seal(dst, nonce, plaintext, additionalData []byte) []byte { | |||||
if len(nonce) != NonceSize { | |||||
panic("xchacha20poly1305: bad nonce length passed to Seal") | |||||
} | |||||
if uint64(len(plaintext)) > (1<<38)-64 { | |||||
panic("xchacha20poly1305: plaintext too large") | |||||
} | |||||
var subKey [KeySize]byte | |||||
var hNonce [16]byte | |||||
var subNonce [chacha20poly1305.NonceSize]byte | |||||
copy(hNonce[:], nonce[:16]) | |||||
HChaCha20(&subKey, &hNonce, &c.key) | |||||
chacha20poly1305, _ := chacha20poly1305.New(subKey[:]) | |||||
copy(subNonce[4:], nonce[16:]) | |||||
return chacha20poly1305.Seal(dst, subNonce[:], plaintext, additionalData) | |||||
} | |||||
func (c *xchacha20poly1305) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) { | |||||
if len(nonce) != NonceSize { | |||||
return nil, fmt.Errorf("xchacha20poly1305: bad nonce length passed to Open") | |||||
} | |||||
if uint64(len(ciphertext)) > (1<<38)-48 { | |||||
return nil, fmt.Errorf("xchacha20poly1305: ciphertext too large") | |||||
} | |||||
var subKey [KeySize]byte | |||||
var hNonce [16]byte | |||||
var subNonce [chacha20poly1305.NonceSize]byte | |||||
copy(hNonce[:], nonce[:16]) | |||||
HChaCha20(&subKey, &hNonce, &c.key) | |||||
chacha20poly1305, _ := chacha20poly1305.New(subKey[:]) | |||||
copy(subNonce[4:], nonce[16:]) | |||||
return chacha20poly1305.Open(dst, subNonce[:], ciphertext, additionalData) | |||||
} | |||||
// The MIT License (MIT) | |||||
// Copyright (c) 2016 Andreas Auernhammer | |||||
// 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. | |||||
// HChaCha20 generates 32 pseudo-random bytes from a 128 bit nonce and a 256 bit secret key. | |||||
// It can be used as a key-derivation-function (KDF). | |||||
func HChaCha20(out *[32]byte, nonce *[16]byte, key *[32]byte) { hChaCha20Generic(out, nonce, key) } | |||||
func hChaCha20Generic(out *[32]byte, nonce *[16]byte, key *[32]byte) { | |||||
v00 := sigma[0] | |||||
v01 := sigma[1] | |||||
v02 := sigma[2] | |||||
v03 := sigma[3] | |||||
v04 := binary.LittleEndian.Uint32(key[0:]) | |||||
v05 := binary.LittleEndian.Uint32(key[4:]) | |||||
v06 := binary.LittleEndian.Uint32(key[8:]) | |||||
v07 := binary.LittleEndian.Uint32(key[12:]) | |||||
v08 := binary.LittleEndian.Uint32(key[16:]) | |||||
v09 := binary.LittleEndian.Uint32(key[20:]) | |||||
v10 := binary.LittleEndian.Uint32(key[24:]) | |||||
v11 := binary.LittleEndian.Uint32(key[28:]) | |||||
v12 := binary.LittleEndian.Uint32(nonce[0:]) | |||||
v13 := binary.LittleEndian.Uint32(nonce[4:]) | |||||
v14 := binary.LittleEndian.Uint32(nonce[8:]) | |||||
v15 := binary.LittleEndian.Uint32(nonce[12:]) | |||||
for i := 0; i < 20; i += 2 { | |||||
v00 += v04 | |||||
v12 ^= v00 | |||||
v12 = (v12 << 16) | (v12 >> 16) | |||||
v08 += v12 | |||||
v04 ^= v08 | |||||
v04 = (v04 << 12) | (v04 >> 20) | |||||
v00 += v04 | |||||
v12 ^= v00 | |||||
v12 = (v12 << 8) | (v12 >> 24) | |||||
v08 += v12 | |||||
v04 ^= v08 | |||||
v04 = (v04 << 7) | (v04 >> 25) | |||||
v01 += v05 | |||||
v13 ^= v01 | |||||
v13 = (v13 << 16) | (v13 >> 16) | |||||
v09 += v13 | |||||
v05 ^= v09 | |||||
v05 = (v05 << 12) | (v05 >> 20) | |||||
v01 += v05 | |||||
v13 ^= v01 | |||||
v13 = (v13 << 8) | (v13 >> 24) | |||||
v09 += v13 | |||||
v05 ^= v09 | |||||
v05 = (v05 << 7) | (v05 >> 25) | |||||
v02 += v06 | |||||
v14 ^= v02 | |||||
v14 = (v14 << 16) | (v14 >> 16) | |||||
v10 += v14 | |||||
v06 ^= v10 | |||||
v06 = (v06 << 12) | (v06 >> 20) | |||||
v02 += v06 | |||||
v14 ^= v02 | |||||
v14 = (v14 << 8) | (v14 >> 24) | |||||
v10 += v14 | |||||
v06 ^= v10 | |||||
v06 = (v06 << 7) | (v06 >> 25) | |||||
v03 += v07 | |||||
v15 ^= v03 | |||||
v15 = (v15 << 16) | (v15 >> 16) | |||||
v11 += v15 | |||||
v07 ^= v11 | |||||
v07 = (v07 << 12) | (v07 >> 20) | |||||
v03 += v07 | |||||
v15 ^= v03 | |||||
v15 = (v15 << 8) | (v15 >> 24) | |||||
v11 += v15 | |||||
v07 ^= v11 | |||||
v07 = (v07 << 7) | (v07 >> 25) | |||||
v00 += v05 | |||||
v15 ^= v00 | |||||
v15 = (v15 << 16) | (v15 >> 16) | |||||
v10 += v15 | |||||
v05 ^= v10 | |||||
v05 = (v05 << 12) | (v05 >> 20) | |||||
v00 += v05 | |||||
v15 ^= v00 | |||||
v15 = (v15 << 8) | (v15 >> 24) | |||||
v10 += v15 | |||||
v05 ^= v10 | |||||
v05 = (v05 << 7) | (v05 >> 25) | |||||
v01 += v06 | |||||
v12 ^= v01 | |||||
v12 = (v12 << 16) | (v12 >> 16) | |||||
v11 += v12 | |||||
v06 ^= v11 | |||||
v06 = (v06 << 12) | (v06 >> 20) | |||||
v01 += v06 | |||||
v12 ^= v01 | |||||
v12 = (v12 << 8) | (v12 >> 24) | |||||
v11 += v12 | |||||
v06 ^= v11 | |||||
v06 = (v06 << 7) | (v06 >> 25) | |||||
v02 += v07 | |||||
v13 ^= v02 | |||||
v13 = (v13 << 16) | (v13 >> 16) | |||||
v08 += v13 | |||||
v07 ^= v08 | |||||
v07 = (v07 << 12) | (v07 >> 20) | |||||
v02 += v07 | |||||
v13 ^= v02 | |||||
v13 = (v13 << 8) | (v13 >> 24) | |||||
v08 += v13 | |||||
v07 ^= v08 | |||||
v07 = (v07 << 7) | (v07 >> 25) | |||||
v03 += v04 | |||||
v14 ^= v03 | |||||
v14 = (v14 << 16) | (v14 >> 16) | |||||
v09 += v14 | |||||
v04 ^= v09 | |||||
v04 = (v04 << 12) | (v04 >> 20) | |||||
v03 += v04 | |||||
v14 ^= v03 | |||||
v14 = (v14 << 8) | (v14 >> 24) | |||||
v09 += v14 | |||||
v04 ^= v09 | |||||
v04 = (v04 << 7) | (v04 >> 25) | |||||
} | |||||
binary.LittleEndian.PutUint32(out[0:], v00) | |||||
binary.LittleEndian.PutUint32(out[4:], v01) | |||||
binary.LittleEndian.PutUint32(out[8:], v02) | |||||
binary.LittleEndian.PutUint32(out[12:], v03) | |||||
binary.LittleEndian.PutUint32(out[16:], v12) | |||||
binary.LittleEndian.PutUint32(out[20:], v13) | |||||
binary.LittleEndian.PutUint32(out[24:], v14) | |||||
binary.LittleEndian.PutUint32(out[28:], v15) | |||||
} |
@ -0,0 +1,103 @@ | |||||
package xchacha20poly1305 | |||||
import ( | |||||
"bytes" | |||||
"encoding/hex" | |||||
"testing" | |||||
) | |||||
func toHex(bits []byte) string { | |||||
return hex.EncodeToString(bits) | |||||
} | |||||
func fromHex(bits string) []byte { | |||||
b, err := hex.DecodeString(bits) | |||||
if err != nil { | |||||
panic(err) | |||||
} | |||||
return b | |||||
} | |||||
func TestHChaCha20(t *testing.T) { | |||||
for i, v := range hChaCha20Vectors { | |||||
var key [32]byte | |||||
var nonce [16]byte | |||||
copy(key[:], v.key) | |||||
copy(nonce[:], v.nonce) | |||||
HChaCha20(&key, &nonce, &key) | |||||
if !bytes.Equal(key[:], v.keystream) { | |||||
t.Errorf("Test %d: keystream mismatch:\n \t got: %s\n \t want: %s", i, toHex(key[:]), toHex(v.keystream)) | |||||
} | |||||
} | |||||
} | |||||
var hChaCha20Vectors = []struct { | |||||
key, nonce, keystream []byte | |||||
}{ | |||||
{ | |||||
fromHex("0000000000000000000000000000000000000000000000000000000000000000"), | |||||
fromHex("000000000000000000000000000000000000000000000000"), | |||||
fromHex("1140704c328d1d5d0e30086cdf209dbd6a43b8f41518a11cc387b669b2ee6586"), | |||||
}, | |||||
{ | |||||
fromHex("8000000000000000000000000000000000000000000000000000000000000000"), | |||||
fromHex("000000000000000000000000000000000000000000000000"), | |||||
fromHex("7d266a7fd808cae4c02a0a70dcbfbcc250dae65ce3eae7fc210f54cc8f77df86"), | |||||
}, | |||||
{ | |||||
fromHex("0000000000000000000000000000000000000000000000000000000000000001"), | |||||
fromHex("000000000000000000000000000000000000000000000002"), | |||||
fromHex("e0c77ff931bb9163a5460c02ac281c2b53d792b1c43fea817e9ad275ae546963"), | |||||
}, | |||||
{ | |||||
fromHex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"), | |||||
fromHex("000102030405060708090a0b0c0d0e0f1011121314151617"), | |||||
fromHex("51e3ff45a895675c4b33b46c64f4a9ace110d34df6a2ceab486372bacbd3eff6"), | |||||
}, | |||||
{ | |||||
fromHex("24f11cce8a1b3d61e441561a696c1c1b7e173d084fd4812425435a8896a013dc"), | |||||
fromHex("d9660c5900ae19ddad28d6e06e45fe5e"), | |||||
fromHex("5966b3eec3bff1189f831f06afe4d4e3be97fa9235ec8c20d08acfbbb4e851e3"), | |||||
}, | |||||
} | |||||
func TestVectors(t *testing.T) { | |||||
for i, v := range vectors { | |||||
if len(v.plaintext) == 0 { | |||||
v.plaintext = make([]byte, len(v.ciphertext)) | |||||
} | |||||
var nonce [24]byte | |||||
copy(nonce[:], v.nonce) | |||||
aead, err := New(v.key) | |||||
if err != nil { | |||||
t.Error(err) | |||||
} | |||||
dst := aead.Seal(nil, nonce[:], v.plaintext, v.ad) | |||||
if !bytes.Equal(dst, v.ciphertext) { | |||||
t.Errorf("Test %d: ciphertext mismatch:\n \t got: %s\n \t want: %s", i, toHex(dst), toHex(v.ciphertext)) | |||||
} | |||||
open, err := aead.Open(nil, nonce[:], dst, v.ad) | |||||
if err != nil { | |||||
t.Error(err) | |||||
} | |||||
if !bytes.Equal(open, v.plaintext) { | |||||
t.Errorf("Test %d: plaintext mismatch:\n \t got: %s\n \t want: %s", i, string(open), string(v.plaintext)) | |||||
} | |||||
} | |||||
} | |||||
var vectors = []struct { | |||||
key, nonce, ad, plaintext, ciphertext []byte | |||||
}{ | |||||
{ | |||||
[]byte{0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f}, | |||||
[]byte{0x07, 0x00, 0x00, 0x00, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b}, | |||||
[]byte{0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7}, | |||||
[]byte("Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."), | |||||
[]byte{0x45, 0x3c, 0x06, 0x93, 0xa7, 0x40, 0x7f, 0x04, 0xff, 0x4c, 0x56, 0xae, 0xdb, 0x17, 0xa3, 0xc0, 0xa1, 0xaf, 0xff, 0x01, 0x17, 0x49, 0x30, 0xfc, 0x22, 0x28, 0x7c, 0x33, 0xdb, 0xcf, 0x0a, 0xc8, 0xb8, 0x9a, 0xd9, 0x29, 0x53, 0x0a, 0x1b, 0xb3, 0xab, 0x5e, 0x69, 0xf2, 0x4c, 0x7f, 0x60, 0x70, 0xc8, 0xf8, 0x40, 0xc9, 0xab, 0xb4, 0xf6, 0x9f, 0xbf, 0xc8, 0xa7, 0xff, 0x51, 0x26, 0xfa, 0xee, 0xbb, 0xb5, 0x58, 0x05, 0xee, 0x9c, 0x1c, 0xf2, 0xce, 0x5a, 0x57, 0x26, 0x32, 0x87, 0xae, 0xc5, 0x78, 0x0f, 0x04, 0xec, 0x32, 0x4c, 0x35, 0x14, 0x12, 0x2c, 0xfc, 0x32, 0x31, 0xfc, 0x1a, 0x8b, 0x71, 0x8a, 0x62, 0x86, 0x37, 0x30, 0xa2, 0x70, 0x2b, 0xb7, 0x63, 0x66, 0x11, 0x6b, 0xed, 0x09, 0xe0, 0xfd, 0x5c, 0x6d, 0x84, 0xb6, 0xb0, 0xc1, 0xab, 0xaf, 0x24, 0x9d, 0x5d, 0xd0, 0xf7, 0xf5, 0xa7, 0xea}, | |||||
}, | |||||
} |