@ -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 | |||
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}, | |||
}, | |||
} |