@ -0,0 +1,154 @@ | |||
# Changelog | |||
## 0.9.0 | |||
BREAKING CHANGES | |||
- `priv.PubKey()` no longer returns an error. Any applicable errors (such as when fetching the public key from a hardware wallet) should be checked and returned when constructing the private key. | |||
## 0.8.0 | |||
**TBD** | |||
## 0.7.0 | |||
**May 30th, 2018** | |||
BREAKING CHANGES | |||
No breaking changes compared to 0.6.2, but making up for the version bump that | |||
should have happened in 0.6.1. | |||
We also bring in the `tmlibs/merkle` package with breaking changes: | |||
- change the hash function from RIPEMD160 to tmhash (first 20-bytes of SHA256) | |||
- remove unused funcs and unexport SimpleMap | |||
FEATURES | |||
- [xchacha20poly1305] New authenticated encryption module | |||
- [merkle] Moved in from tmlibs | |||
- [merkle/tmhash] New hash function: the first 20-bytes of SHA256 | |||
IMPROVEMENTS | |||
- Remove some dead code | |||
- Use constant-time compare for signatures | |||
BUG FIXES | |||
- Fix MixEntropy weakness | |||
- Fix PrivKeyEd25519.Generate() | |||
## 0.6.2 (April 9, 2018) | |||
IMPROVEMENTS | |||
- Update for latest go-amino | |||
## 0.6.1 (March 26, 2018) | |||
BREAKING CHANGES | |||
- Encoding uses MarshalBinaryBare rather than MarshalBinary (which auto-length-prefixes) for pub/priv/sig. | |||
## 0.6.0 (March 2, 2018) | |||
BREAKING CHANGES | |||
- Update Amino names from "com.tendermint/..." to "tendermint/" | |||
## 0.5.0 (March 2, 2018) | |||
BREAKING CHANGES | |||
- nano: moved to `_nano` now while we're having build issues | |||
- bcrypt: moved to `keys/bcrypt` | |||
- hd: moved to `keys/hd`; `BTC` added to some function names; other function cleanup | |||
- keys/cryptostore: moved to `keys`, renamed to `keybase`, and completely refactored | |||
- keys: moved BIP39 related code to `keys/words` | |||
FEATURE | |||
- `Address` is a type alias for `cmn.HexBytes` | |||
BUG FIX | |||
- PrivKey comparisons done in constant time | |||
## 0.4.1 (October 27, 2017) | |||
This release removes support for bcrypt as it was merged too soon without an upgrade plan | |||
for existing keys. | |||
REVERTS THE FOLLOWING COMMITS: | |||
- Parameterize and lower bcrypt cost - dfc4cdd2d71513e4a9922d679c74f36357c4c862 | |||
- Upgrade keys to use bcrypt with salts (#38) - 8e7f0e7701f92206679ad093d013b9b162427631 | |||
## 0.4.0 (October 27, 2017) | |||
BREAKING CHANGES: | |||
- `keys`: use bcrypt plus salt | |||
FEATURES: | |||
- add support for signing via Ledger Nano | |||
IMPROVEMENTS: | |||
- linting and comments | |||
## 0.3.0 (September 22, 2017) | |||
BREAKING CHANGES: | |||
- Remove `cmd` and `keys/tx` packages altogether: move it to the cosmos-sdk | |||
- `cryptostore.Generator` takes a secret | |||
- Remove `String()` from `Signature` interface | |||
FEATURES: | |||
- `keys`: add CRC16 error correcting code | |||
IMPROVEMENTS: | |||
- Allow no passwords on keys for development convenience | |||
## 0.2.1 (June 21, 2017) | |||
- Improve keys command | |||
- No password prompts in non-interactive mode (echo 'foobar' | keys new foo) | |||
- Added support for seed phrases | |||
- Seed phrase now returned on `keys new` | |||
- Add `keys restore` to restore private key from key phrase | |||
- Checksum to verify typos in the seed phrase (rather than just a useless key) | |||
- Add `keys delete` to remove a key if needed | |||
## 0.2.0 (May 18, 2017) | |||
BREAKING CHANGES: | |||
- [hd] The following functions no longer take a `coin string` as argument: `ComputeAddress`, `AddrFromPubKeyBytes`, `ComputeAddressForPrivKey`, `ComputeWIF`, `WIFFromPrivKeyBytes` | |||
- Changes to `PrivKey`, `PubKey`, and `Signature` (denoted `Xxx` below): | |||
- interfaces are renamed `XxxInner`, and are not for use outside the package, though they must be exposed for sake of serialization. | |||
- `Xxx` is now a struct that wraps the corresponding `XxxInner` interface | |||
FEATURES: | |||
- `github.com/tendermint/go-keys -> github.com/tendermint/go-crypto/keys` - command and lib for generating and managing encrypted keys | |||
- [hd] New function `WIFFromPrivKeyBytes(privKeyBytes []byte, compress bool) string` | |||
- Changes to `PrivKey`, `PubKey`, and `Signature` (denoted `Xxx` below): | |||
- Expose a new method `Unwrap() XxxInner` on the `Xxx` struct which returns the corresponding `XxxInner` interface | |||
- Expose a new method `Wrap() Xxx` on the `XxxInner` interface which returns the corresponding `Xxx` struct | |||
IMPROVEMENTS: | |||
- Update to use new `tmlibs` repository | |||
## 0.1.0 (April 14, 2017) | |||
Initial release | |||
@ -0,0 +1,137 @@ | |||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. | |||
[[projects]] | |||
branch = "master" | |||
name = "github.com/btcsuite/btcd" | |||
packages = ["btcec"] | |||
revision = "86fed781132ac890ee03e906e4ecd5d6fa180c64" | |||
[[projects]] | |||
branch = "master" | |||
name = "github.com/btcsuite/btcutil" | |||
packages = ["base58"] | |||
revision = "d4cc87b860166d00d6b5b9e0d3b3d71d6088d4d4" | |||
[[projects]] | |||
name = "github.com/davecgh/go-spew" | |||
packages = ["spew"] | |||
revision = "346938d642f2ec3594ed81d874461961cd0faa76" | |||
version = "v1.1.0" | |||
[[projects]] | |||
name = "github.com/go-kit/kit" | |||
packages = [ | |||
"log", | |||
"log/level", | |||
"log/term" | |||
] | |||
revision = "4dc7be5d2d12881735283bcab7352178e190fc71" | |||
version = "v0.6.0" | |||
[[projects]] | |||
name = "github.com/go-logfmt/logfmt" | |||
packages = ["."] | |||
revision = "390ab7935ee28ec6b286364bba9b4dd6410cb3d5" | |||
version = "v0.3.0" | |||
[[projects]] | |||
name = "github.com/go-stack/stack" | |||
packages = ["."] | |||
revision = "259ab82a6cad3992b4e21ff5cac294ccb06474bc" | |||
version = "v1.7.0" | |||
[[projects]] | |||
name = "github.com/gogo/protobuf" | |||
packages = [ | |||
"gogoproto", | |||
"proto", | |||
"protoc-gen-gogo/descriptor" | |||
] | |||
revision = "1adfc126b41513cc696b209667c8656ea7aac67c" | |||
version = "v1.0.0" | |||
[[projects]] | |||
branch = "master" | |||
name = "github.com/kr/logfmt" | |||
packages = ["."] | |||
revision = "b84e30acd515aadc4b783ad4ff83aff3299bdfe0" | |||
[[projects]] | |||
name = "github.com/pkg/errors" | |||
packages = ["."] | |||
revision = "645ef00459ed84a119197bfb8d8205042c6df63d" | |||
version = "v0.8.0" | |||
[[projects]] | |||
name = "github.com/pmezard/go-difflib" | |||
packages = ["difflib"] | |||
revision = "792786c7400a136282c1664665ae0a8db921c6c2" | |||
version = "v1.0.0" | |||
[[projects]] | |||
name = "github.com/stretchr/testify" | |||
packages = [ | |||
"assert", | |||
"require" | |||
] | |||
revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686" | |||
version = "v1.2.2" | |||
[[projects]] | |||
branch = "master" | |||
name = "github.com/tendermint/ed25519" | |||
packages = [ | |||
".", | |||
"edwards25519", | |||
"extra25519" | |||
] | |||
revision = "d8387025d2b9d158cf4efb07e7ebf814bcce2057" | |||
[[projects]] | |||
name = "github.com/tendermint/go-amino" | |||
packages = ["."] | |||
revision = "2106ca61d91029c931fd54968c2bb02dc96b1412" | |||
version = "0.10.1" | |||
[[projects]] | |||
name = "github.com/tendermint/tmlibs" | |||
packages = [ | |||
"common", | |||
"log", | |||
"test" | |||
] | |||
revision = "692f1d86a6e2c0efa698fd1e4541b68c74ffaf38" | |||
version = "v0.8.4" | |||
[[projects]] | |||
branch = "master" | |||
name = "golang.org/x/crypto" | |||
packages = [ | |||
"bcrypt", | |||
"blowfish", | |||
"chacha20poly1305", | |||
"hkdf", | |||
"internal/chacha20", | |||
"internal/subtle", | |||
"nacl/secretbox", | |||
"openpgp/armor", | |||
"openpgp/errors", | |||
"poly1305", | |||
"ripemd160", | |||
"salsa20/salsa" | |||
] | |||
revision = "7f39a6fea4fe9364fb61e1def6a268a51b4f3a06" | |||
[[projects]] | |||
branch = "master" | |||
name = "golang.org/x/sys" | |||
packages = ["cpu"] | |||
revision = "ad87a3a340fa7f3bed189293fbfa7a9b7e021ae1" | |||
[solve-meta] | |||
analyzer-name = "dep" | |||
analyzer-version = 1 | |||
inputs-digest = "027b22b86396a971d5d5c1d298947f531f39743975d65a22e98601140aa1b1a1" | |||
solver-name = "gps-cdcl" | |||
solver-version = 1 |
@ -0,0 +1,49 @@ | |||
# Gopkg.toml example | |||
# | |||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md | |||
# for detailed Gopkg.toml documentation. | |||
# | |||
# required = ["github.com/user/thing/cmd/thing"] | |||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] | |||
# | |||
# [[constraint]] | |||
# name = "github.com/user/project" | |||
# version = "1.0.0" | |||
# | |||
# [[constraint]] | |||
# name = "github.com/user/project2" | |||
# branch = "dev" | |||
# source = "github.com/myfork/project2" | |||
# | |||
# [[override]] | |||
# name = "github.com/x/y" | |||
# version = "2.4.0" | |||
# | |||
# [prune] | |||
# non-go = false | |||
# go-tests = true | |||
# unused-packages = true | |||
[[constraint]] | |||
name = "github.com/btcsuite/btcutil" | |||
branch = "master" | |||
[[constraint]] | |||
name = "github.com/stretchr/testify" | |||
version = "1.2.1" | |||
[[constraint]] | |||
name = "github.com/tendermint/ed25519" | |||
branch = "master" | |||
[[constraint]] | |||
name = "github.com/tendermint/go-amino" | |||
version = "0.10.0-rc2" | |||
[[constraint]] | |||
name = "github.com/tendermint/tmlibs" | |||
version = "0.8.1" | |||
[prune] | |||
go-tests = true | |||
unused-packages = true |
@ -0,0 +1,193 @@ | |||
Tendermint Go-Crypto | |||
Copyright (C) 2015 Tendermint | |||
Apache License | |||
Version 2.0, January 2004 | |||
https://www.apache.org/licenses/ | |||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |||
1. Definitions. | |||
"License" shall mean the terms and conditions for use, reproduction, | |||
and distribution as defined by Sections 1 through 9 of this document. | |||
"Licensor" shall mean the copyright owner or entity authorized by | |||
the copyright owner that is granting the License. | |||
"Legal Entity" shall mean the union of the acting entity and all | |||
other entities that control, are controlled by, or are under common | |||
control with that entity. For the purposes of this definition, | |||
"control" means (i) the power, direct or indirect, to cause the | |||
direction or management of such entity, whether by contract or | |||
otherwise, or (ii) ownership of fifty percent (50%) or more of the | |||
outstanding shares, or (iii) beneficial ownership of such entity. | |||
"You" (or "Your") shall mean an individual or Legal Entity | |||
exercising permissions granted by this License. | |||
"Source" form shall mean the preferred form for making modifications, | |||
including but not limited to software source code, documentation | |||
source, and configuration files. | |||
"Object" form shall mean any form resulting from mechanical | |||
transformation or translation of a Source form, including but | |||
not limited to compiled object code, generated documentation, | |||
and conversions to other media types. | |||
"Work" shall mean the work of authorship, whether in Source or | |||
Object form, made available under the License, as indicated by a | |||
copyright notice that is included in or attached to the work | |||
(an example is provided in the Appendix below). | |||
"Derivative Works" shall mean any work, whether in Source or Object | |||
form, that is based on (or derived from) the Work and for which the | |||
editorial revisions, annotations, elaborations, or other modifications | |||
represent, as a whole, an original work of authorship. For the purposes | |||
of this License, Derivative Works shall not include works that remain | |||
separable from, or merely link (or bind by name) to the interfaces of, | |||
the Work and Derivative Works thereof. | |||
"Contribution" shall mean any work of authorship, including | |||
the original version of the Work and any modifications or additions | |||
to that Work or Derivative Works thereof, that is intentionally | |||
submitted to Licensor for inclusion in the Work by the copyright owner | |||
or by an individual or Legal Entity authorized to submit on behalf of | |||
the copyright owner. For the purposes of this definition, "submitted" | |||
means any form of electronic, verbal, or written communication sent | |||
to the Licensor or its representatives, including but not limited to | |||
communication on electronic mailing lists, source code control systems, | |||
and issue tracking systems that are managed by, or on behalf of, the | |||
Licensor for the purpose of discussing and improving the Work, but | |||
excluding communication that is conspicuously marked or otherwise | |||
designated in writing by the copyright owner as "Not a Contribution." | |||
"Contributor" shall mean Licensor and any individual or Legal Entity | |||
on behalf of whom a Contribution has been received by Licensor and | |||
subsequently incorporated within the Work. | |||
2. Grant of Copyright License. Subject to the terms and conditions of | |||
this License, each Contributor hereby grants to You a perpetual, | |||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |||
copyright license to reproduce, prepare Derivative Works of, | |||
publicly display, publicly perform, sublicense, and distribute the | |||
Work and such Derivative Works in Source or Object form. | |||
3. Grant of Patent License. Subject to the terms and conditions of | |||
this License, each Contributor hereby grants to You a perpetual, | |||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |||
(except as stated in this section) patent license to make, have made, | |||
use, offer to sell, sell, import, and otherwise transfer the Work, | |||
where such license applies only to those patent claims licensable | |||
by such Contributor that are necessarily infringed by their | |||
Contribution(s) alone or by combination of their Contribution(s) | |||
with the Work to which such Contribution(s) was submitted. If You | |||
institute patent litigation against any entity (including a | |||
cross-claim or counterclaim in a lawsuit) alleging that the Work | |||
or a Contribution incorporated within the Work constitutes direct | |||
or contributory patent infringement, then any patent licenses | |||
granted to You under this License for that Work shall terminate | |||
as of the date such litigation is filed. | |||
4. Redistribution. You may reproduce and distribute copies of the | |||
Work or Derivative Works thereof in any medium, with or without | |||
modifications, and in Source or Object form, provided that You | |||
meet the following conditions: | |||
(a) You must give any other recipients of the Work or | |||
Derivative Works a copy of this License; and | |||
(b) You must cause any modified files to carry prominent notices | |||
stating that You changed the files; and | |||
(c) You must retain, in the Source form of any Derivative Works | |||
that You distribute, all copyright, patent, trademark, and | |||
attribution notices from the Source form of the Work, | |||
excluding those notices that do not pertain to any part of | |||
the Derivative Works; and | |||
(d) If the Work includes a "NOTICE" text file as part of its | |||
distribution, then any Derivative Works that You distribute must | |||
include a readable copy of the attribution notices contained | |||
within such NOTICE file, excluding those notices that do not | |||
pertain to any part of the Derivative Works, in at least one | |||
of the following places: within a NOTICE text file distributed | |||
as part of the Derivative Works; within the Source form or | |||
documentation, if provided along with the Derivative Works; or, | |||
within a display generated by the Derivative Works, if and | |||
wherever such third-party notices normally appear. The contents | |||
of the NOTICE file are for informational purposes only and | |||
do not modify the License. You may add Your own attribution | |||
notices within Derivative Works that You distribute, alongside | |||
or as an addendum to the NOTICE text from the Work, provided | |||
that such additional attribution notices cannot be construed | |||
as modifying the License. | |||
You may add Your own copyright statement to Your modifications and | |||
may provide additional or different license terms and conditions | |||
for use, reproduction, or distribution of Your modifications, or | |||
for any such Derivative Works as a whole, provided Your use, | |||
reproduction, and distribution of the Work otherwise complies with | |||
the conditions stated in this License. | |||
5. Submission of Contributions. Unless You explicitly state otherwise, | |||
any Contribution intentionally submitted for inclusion in the Work | |||
by You to the Licensor shall be under the terms and conditions of | |||
this License, without any additional terms or conditions. | |||
Notwithstanding the above, nothing herein shall supersede or modify | |||
the terms of any separate license agreement you may have executed | |||
with Licensor regarding such Contributions. | |||
6. Trademarks. This License does not grant permission to use the trade | |||
names, trademarks, service marks, or product names of the Licensor, | |||
except as required for reasonable and customary use in describing the | |||
origin of the Work and reproducing the content of the NOTICE file. | |||
7. Disclaimer of Warranty. Unless required by applicable law or | |||
agreed to in writing, Licensor provides the Work (and each | |||
Contributor provides its Contributions) on an "AS IS" BASIS, | |||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |||
implied, including, without limitation, any warranties or conditions | |||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |||
PARTICULAR PURPOSE. You are solely responsible for determining the | |||
appropriateness of using or redistributing the Work and assume any | |||
risks associated with Your exercise of permissions under this License. | |||
8. Limitation of Liability. In no event and under no legal theory, | |||
whether in tort (including negligence), contract, or otherwise, | |||
unless required by applicable law (such as deliberate and grossly | |||
negligent acts) or agreed to in writing, shall any Contributor be | |||
liable to You for damages, including any direct, indirect, special, | |||
incidental, or consequential damages of any character arising as a | |||
result of this License or out of the use or inability to use the | |||
Work (including but not limited to damages for loss of goodwill, | |||
work stoppage, computer failure or malfunction, or any and all | |||
other commercial damages or losses), even if such Contributor | |||
has been advised of the possibility of such damages. | |||
9. Accepting Warranty or Additional Liability. While redistributing | |||
the Work or Derivative Works thereof, You may choose to offer, | |||
and charge a fee for, acceptance of support, warranty, indemnity, | |||
or other liability obligations and/or rights consistent with this | |||
License. However, in accepting such obligations, You may act only | |||
on Your own behalf and on Your sole responsibility, not on behalf | |||
of any other Contributor, and only if You agree to indemnify, | |||
defend, and hold each Contributor harmless for any liability | |||
incurred by, or claims asserted against, such Contributor by reason | |||
of your accepting any such warranty or additional liability. | |||
END OF TERMS AND CONDITIONS | |||
Licensed under the Apache License, Version 2.0 (the "License"); | |||
you may not use this file except in compliance with the License. | |||
You may obtain a copy of the License at | |||
https://www.apache.org/licenses/LICENSE-2.0 | |||
Unless required by applicable law or agreed to in writing, software | |||
distributed under the License is distributed on an "AS IS" BASIS, | |||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
See the License for the specific language governing permissions and | |||
limitations under the License. |
@ -0,0 +1,99 @@ | |||
GOTOOLS = \ | |||
github.com/golang/dep/cmd/dep \ | |||
# gopkg.in/alecthomas/gometalinter.v2 \ | |||
GOTOOLS_CHECK = dep #gometalinter.v2 | |||
all: check get_vendor_deps build test install | |||
check: check_tools | |||
######################################## | |||
### Build | |||
# Command to generate the workd list (kept here for documentation purposes only): | |||
wordlist: | |||
# To re-generate wordlist.go run: | |||
# go-bindata -ignore ".*\.go" -o keys/words/bip39/wordlist.go -pkg "wordlist" keys/bip39/wordlist/... | |||
build: wordlist | |||
# Nothing else to build! | |||
install: | |||
# Nothing to install! | |||
######################################## | |||
### Tools & dependencies | |||
check_tools: | |||
@# https://stackoverflow.com/a/25668869 | |||
@echo "Found tools: $(foreach tool,$(GOTOOLS_CHECK),\ | |||
$(if $(shell which $(tool)),$(tool),$(error "No $(tool) in PATH")))" | |||
get_tools: | |||
@echo "--> Installing tools" | |||
go get -u -v $(GOTOOLS) | |||
#@gometalinter.v2 --install | |||
update_tools: | |||
@echo "--> Updating tools" | |||
@go get -u $(GOTOOLS) | |||
get_vendor_deps: | |||
@rm -rf vendor/ | |||
@echo "--> Running dep ensure" | |||
@dep ensure | |||
######################################## | |||
### Testing | |||
test: | |||
CGO_ENABLED=0 go test -p 1 $(shell go list ./... | grep -v vendor) | |||
######################################## | |||
### Formatting, linting, and vetting | |||
fmt: | |||
@go fmt ./... | |||
metalinter: | |||
@echo "==> Running linter" | |||
gometalinter.v2 --vendor --deadline=600s --disable-all \ | |||
--enable=maligned \ | |||
--enable=deadcode \ | |||
--enable=goconst \ | |||
--enable=goimports \ | |||
--enable=gosimple \ | |||
--enable=ineffassign \ | |||
--enable=megacheck \ | |||
--enable=misspell \ | |||
--enable=staticcheck \ | |||
--enable=safesql \ | |||
--enable=structcheck \ | |||
--enable=unconvert \ | |||
--enable=unused \ | |||
--enable=varcheck \ | |||
--enable=vetshadow \ | |||
./... | |||
#--enable=gas \ | |||
#--enable=dupl \ | |||
#--enable=errcheck \ | |||
#--enable=gocyclo \ | |||
#--enable=golint \ <== comments on anything exported | |||
#--enable=gotype \ | |||
#--enable=interfacer \ | |||
#--enable=unparam \ | |||
#--enable=vet \ | |||
metalinter_all: | |||
protoc $(INCLUDE) --lint_out=. types/*.proto | |||
gometalinter.v2 --vendor --deadline=600s --enable-all --disable=lll ./... | |||
# To avoid unintended conflicts with file names, always add to .PHONY | |||
# unless there is a reason not to. | |||
# https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html | |||
.PHONEY: check build install check_tools get_tools update_tools get_vendor_deps test fmt metalinter metalinter_all |
@ -0,0 +1,25 @@ | |||
# go-crypto [![GoDoc](https://godoc.org/github.com/tendermint/go-crypto?status.svg)](https://godoc.org/github.com/tendermint/go-crypto) | |||
go-crypto is the cryptographic package adapted for Tendermint's uses | |||
## Importing it | |||
`import "github.com/tendermint/go-crypto"` | |||
## Binary encoding | |||
For Binary encoding, please refer to the [Tendermint encoding spec](https://github.com/tendermint/tendermint/blob/master/docs/spec/blockchain/encoding.md). | |||
## JSON Encoding | |||
go-crypto `.Bytes()` uses Amino:binary encoding, but Amino:JSON is also supported. | |||
```go | |||
Example Amino:JSON encodings: | |||
crypto.PrivKeyEd25519 - {"type":"954568A3288910","value":"EVkqJO/jIXp3rkASXfh9YnyToYXRXhBr6g9cQVxPFnQBP/5povV4HTjvsy530kybxKHwEi85iU8YL0qQhSYVoQ=="} | |||
crypto.SignatureEd25519 - {"type":"6BF5903DA1DB28","value":"77sQNZOrf7ltExpf7AV1WaYPCHbyRLgjBsoWVzcduuLk+jIGmYk+s5R6Emm29p12HeiNAuhUJgdFGmwkpeGJCA=="} | |||
crypto.PubKeyEd25519 - {"type":"AC26791624DE60","value":"AT/+aaL1eB0477Mud9JMm8Sh8BIvOYlPGC9KkIUmFaE="} | |||
crypto.PrivKeySecp256k1 - {"type":"019E82E1B0F798","value":"zx4Pnh67N+g2V+5vZbQzEyRerX9c4ccNZOVzM9RvJ0Y="} | |||
crypto.SignatureSecp256k1 - {"type":"6D1EA416E1FEE8","value":"MEUCIQCIg5TqS1l7I+MKTrSPIuUN2+4m5tA29dcauqn3NhEJ2wIgICaZ+lgRc5aOTVahU/XoLopXKn8BZcl0bnuYWLvohR8="} | |||
crypto.PubKeySecp256k1 - {"type":"F8CCEAEB5AE980","value":"A8lPKJXcNl5VHt1FK8a244K9EJuS4WX1hFBnwisi0IJx"} | |||
``` |
@ -0,0 +1,37 @@ | |||
package crypto | |||
import ( | |||
amino "github.com/tendermint/go-amino" | |||
) | |||
var cdc = amino.NewCodec() | |||
func init() { | |||
// NOTE: It's important that there be no conflicts here, | |||
// as that would change the canonical representations, | |||
// and therefore change the address. | |||
// TODO: Add feature to go-amino to ensure that there | |||
// are no conflicts. | |||
RegisterAmino(cdc) | |||
} | |||
// RegisterAmino registers all go-crypto related types in the given (amino) codec. | |||
func RegisterAmino(cdc *amino.Codec) { | |||
cdc.RegisterInterface((*PubKey)(nil), nil) | |||
cdc.RegisterConcrete(PubKeyEd25519{}, | |||
"tendermint/PubKeyEd25519", nil) | |||
cdc.RegisterConcrete(PubKeySecp256k1{}, | |||
"tendermint/PubKeySecp256k1", nil) | |||
cdc.RegisterInterface((*PrivKey)(nil), nil) | |||
cdc.RegisterConcrete(PrivKeyEd25519{}, | |||
"tendermint/PrivKeyEd25519", nil) | |||
cdc.RegisterConcrete(PrivKeySecp256k1{}, | |||
"tendermint/PrivKeySecp256k1", nil) | |||
cdc.RegisterInterface((*Signature)(nil), nil) | |||
cdc.RegisterConcrete(SignatureEd25519{}, | |||
"tendermint/SignatureEd25519", nil) | |||
cdc.RegisterConcrete(SignatureSecp256k1{}, | |||
"tendermint/SignatureSecp256k1", nil) | |||
} |
@ -0,0 +1,39 @@ | |||
package crypto | |||
import ( | |||
"bytes" | |||
"fmt" | |||
"io/ioutil" | |||
"golang.org/x/crypto/openpgp/armor" | |||
) | |||
func EncodeArmor(blockType string, headers map[string]string, data []byte) string { | |||
buf := new(bytes.Buffer) | |||
w, err := armor.Encode(buf, blockType, headers) | |||
if err != nil { | |||
panic(fmt.Errorf("could not encode ascii armor: %s", err)) | |||
} | |||
_, err = w.Write(data) | |||
if err != nil { | |||
panic(fmt.Errorf("could not encode ascii armor: %s", err)) | |||
} | |||
err = w.Close() | |||
if err != nil { | |||
panic(fmt.Errorf("could not encode ascii armor: %s", err)) | |||
} | |||
return buf.String() | |||
} | |||
func DecodeArmor(armorStr string) (blockType string, headers map[string]string, data []byte, err error) { | |||
buf := bytes.NewBufferString(armorStr) | |||
block, err := armor.Decode(buf) | |||
if err != nil { | |||
return "", nil, nil, err | |||
} | |||
data, err = ioutil.ReadAll(block.Body) | |||
if err != nil { | |||
return "", nil, nil, err | |||
} | |||
return block.Type, block.Header, data, nil | |||
} |
@ -0,0 +1,20 @@ | |||
package crypto | |||
import ( | |||
"testing" | |||
"github.com/stretchr/testify/assert" | |||
"github.com/stretchr/testify/require" | |||
) | |||
func TestSimpleArmor(t *testing.T) { | |||
blockType := "MINT TEST" | |||
data := []byte("somedata") | |||
armorStr := EncodeArmor(blockType, nil, data) | |||
// Decode armorStr and test for equivalence. | |||
blockType2, _, data2, err := DecodeArmor(armorStr) | |||
require.Nil(t, err, "%+v", err) | |||
assert.Equal(t, blockType, blockType2) | |||
assert.Equal(t, data, data2) | |||
} |
@ -0,0 +1,154 @@ | |||
# Changelog | |||
## 0.9.0 | |||
BREAKING CHANGES | |||
- `priv.PubKey()` no longer returns an error. Any applicable errors (such as when fetching the public key from a hardware wallet) should be checked and returned when constructing the private key. | |||
## 0.8.0 | |||
**TBD** | |||
## 0.7.0 | |||
**May 30th, 2018** | |||
BREAKING CHANGES | |||
No breaking changes compared to 0.6.2, but making up for the version bump that | |||
should have happened in 0.6.1. | |||
We also bring in the `tmlibs/merkle` package with breaking changes: | |||
- change the hash function from RIPEMD160 to tmhash (first 20-bytes of SHA256) | |||
- remove unused funcs and unexport SimpleMap | |||
FEATURES | |||
- [xchacha20poly1305] New authenticated encryption module | |||
- [merkle] Moved in from tmlibs | |||
- [merkle/tmhash] New hash function: the first 20-bytes of SHA256 | |||
IMPROVEMENTS | |||
- Remove some dead code | |||
- Use constant-time compare for signatures | |||
BUG FIXES | |||
- Fix MixEntropy weakness | |||
- Fix PrivKeyEd25519.Generate() | |||
## 0.6.2 (April 9, 2018) | |||
IMPROVEMENTS | |||
- Update for latest go-amino | |||
## 0.6.1 (March 26, 2018) | |||
BREAKING CHANGES | |||
- Encoding uses MarshalBinaryBare rather than MarshalBinary (which auto-length-prefixes) for pub/priv/sig. | |||
## 0.6.0 (March 2, 2018) | |||
BREAKING CHANGES | |||
- Update Amino names from "com.tendermint/..." to "tendermint/" | |||
## 0.5.0 (March 2, 2018) | |||
BREAKING CHANGES | |||
- nano: moved to `_nano` now while we're having build issues | |||
- bcrypt: moved to `keys/bcrypt` | |||
- hd: moved to `keys/hd`; `BTC` added to some function names; other function cleanup | |||
- keys/cryptostore: moved to `keys`, renamed to `keybase`, and completely refactored | |||
- keys: moved BIP39 related code to `keys/words` | |||
FEATURE | |||
- `Address` is a type alias for `cmn.HexBytes` | |||
BUG FIX | |||
- PrivKey comparisons done in constant time | |||
## 0.4.1 (October 27, 2017) | |||
This release removes support for bcrypt as it was merged too soon without an upgrade plan | |||
for existing keys. | |||
REVERTS THE FOLLOWING COMMITS: | |||
- Parameterize and lower bcrypt cost - dfc4cdd2d71513e4a9922d679c74f36357c4c862 | |||
- Upgrade keys to use bcrypt with salts (#38) - 8e7f0e7701f92206679ad093d013b9b162427631 | |||
## 0.4.0 (October 27, 2017) | |||
BREAKING CHANGES: | |||
- `keys`: use bcrypt plus salt | |||
FEATURES: | |||
- add support for signing via Ledger Nano | |||
IMPROVEMENTS: | |||
- linting and comments | |||
## 0.3.0 (September 22, 2017) | |||
BREAKING CHANGES: | |||
- Remove `cmd` and `keys/tx` packages altogether: move it to the cosmos-sdk | |||
- `cryptostore.Generator` takes a secret | |||
- Remove `String()` from `Signature` interface | |||
FEATURES: | |||
- `keys`: add CRC16 error correcting code | |||
IMPROVEMENTS: | |||
- Allow no passwords on keys for development convenience | |||
## 0.2.1 (June 21, 2017) | |||
- Improve keys command | |||
- No password prompts in non-interactive mode (echo 'foobar' | keys new foo) | |||
- Added support for seed phrases | |||
- Seed phrase now returned on `keys new` | |||
- Add `keys restore` to restore private key from key phrase | |||
- Checksum to verify typos in the seed phrase (rather than just a useless key) | |||
- Add `keys delete` to remove a key if needed | |||
## 0.2.0 (May 18, 2017) | |||
BREAKING CHANGES: | |||
- [hd] The following functions no longer take a `coin string` as argument: `ComputeAddress`, `AddrFromPubKeyBytes`, `ComputeAddressForPrivKey`, `ComputeWIF`, `WIFFromPrivKeyBytes` | |||
- Changes to `PrivKey`, `PubKey`, and `Signature` (denoted `Xxx` below): | |||
- interfaces are renamed `XxxInner`, and are not for use outside the package, though they must be exposed for sake of serialization. | |||
- `Xxx` is now a struct that wraps the corresponding `XxxInner` interface | |||
FEATURES: | |||
- `github.com/tendermint/go-keys -> github.com/tendermint/go-crypto/keys` - command and lib for generating and managing encrypted keys | |||
- [hd] New function `WIFFromPrivKeyBytes(privKeyBytes []byte, compress bool) string` | |||
- Changes to `PrivKey`, `PubKey`, and `Signature` (denoted `Xxx` below): | |||
- Expose a new method `Unwrap() XxxInner` on the `Xxx` struct which returns the corresponding `XxxInner` interface | |||
- Expose a new method `Wrap() Xxx` on the `XxxInner` interface which returns the corresponding `Xxx` struct | |||
IMPROVEMENTS: | |||
- Update to use new `tmlibs` repository | |||
## 0.1.0 (April 14, 2017) | |||
Initial release | |||
@ -0,0 +1,137 @@ | |||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. | |||
[[projects]] | |||
branch = "master" | |||
name = "github.com/btcsuite/btcd" | |||
packages = ["btcec"] | |||
revision = "86fed781132ac890ee03e906e4ecd5d6fa180c64" | |||
[[projects]] | |||
branch = "master" | |||
name = "github.com/btcsuite/btcutil" | |||
packages = ["base58"] | |||
revision = "d4cc87b860166d00d6b5b9e0d3b3d71d6088d4d4" | |||
[[projects]] | |||
name = "github.com/davecgh/go-spew" | |||
packages = ["spew"] | |||
revision = "346938d642f2ec3594ed81d874461961cd0faa76" | |||
version = "v1.1.0" | |||
[[projects]] | |||
name = "github.com/go-kit/kit" | |||
packages = [ | |||
"log", | |||
"log/level", | |||
"log/term" | |||
] | |||
revision = "4dc7be5d2d12881735283bcab7352178e190fc71" | |||
version = "v0.6.0" | |||
[[projects]] | |||
name = "github.com/go-logfmt/logfmt" | |||
packages = ["."] | |||
revision = "390ab7935ee28ec6b286364bba9b4dd6410cb3d5" | |||
version = "v0.3.0" | |||
[[projects]] | |||
name = "github.com/go-stack/stack" | |||
packages = ["."] | |||
revision = "259ab82a6cad3992b4e21ff5cac294ccb06474bc" | |||
version = "v1.7.0" | |||
[[projects]] | |||
name = "github.com/gogo/protobuf" | |||
packages = [ | |||
"gogoproto", | |||
"proto", | |||
"protoc-gen-gogo/descriptor" | |||
] | |||
revision = "1adfc126b41513cc696b209667c8656ea7aac67c" | |||
version = "v1.0.0" | |||
[[projects]] | |||
branch = "master" | |||
name = "github.com/kr/logfmt" | |||
packages = ["."] | |||
revision = "b84e30acd515aadc4b783ad4ff83aff3299bdfe0" | |||
[[projects]] | |||
name = "github.com/pkg/errors" | |||
packages = ["."] | |||
revision = "645ef00459ed84a119197bfb8d8205042c6df63d" | |||
version = "v0.8.0" | |||
[[projects]] | |||
name = "github.com/pmezard/go-difflib" | |||
packages = ["difflib"] | |||
revision = "792786c7400a136282c1664665ae0a8db921c6c2" | |||
version = "v1.0.0" | |||
[[projects]] | |||
name = "github.com/stretchr/testify" | |||
packages = [ | |||
"assert", | |||
"require" | |||
] | |||
revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686" | |||
version = "v1.2.2" | |||
[[projects]] | |||
branch = "master" | |||
name = "github.com/tendermint/ed25519" | |||
packages = [ | |||
".", | |||
"edwards25519", | |||
"extra25519" | |||
] | |||
revision = "d8387025d2b9d158cf4efb07e7ebf814bcce2057" | |||
[[projects]] | |||
name = "github.com/tendermint/go-amino" | |||
packages = ["."] | |||
revision = "2106ca61d91029c931fd54968c2bb02dc96b1412" | |||
version = "0.10.1" | |||
[[projects]] | |||
name = "github.com/tendermint/tmlibs" | |||
packages = [ | |||
"common", | |||
"log", | |||
"test" | |||
] | |||
revision = "692f1d86a6e2c0efa698fd1e4541b68c74ffaf38" | |||
version = "v0.8.4" | |||
[[projects]] | |||
branch = "master" | |||
name = "golang.org/x/crypto" | |||
packages = [ | |||
"bcrypt", | |||
"blowfish", | |||
"chacha20poly1305", | |||
"hkdf", | |||
"internal/chacha20", | |||
"internal/subtle", | |||
"nacl/secretbox", | |||
"openpgp/armor", | |||
"openpgp/errors", | |||
"poly1305", | |||
"ripemd160", | |||
"salsa20/salsa" | |||
] | |||
revision = "7f39a6fea4fe9364fb61e1def6a268a51b4f3a06" | |||
[[projects]] | |||
branch = "master" | |||
name = "golang.org/x/sys" | |||
packages = ["cpu"] | |||
revision = "ad87a3a340fa7f3bed189293fbfa7a9b7e021ae1" | |||
[solve-meta] | |||
analyzer-name = "dep" | |||
analyzer-version = 1 | |||
inputs-digest = "027b22b86396a971d5d5c1d298947f531f39743975d65a22e98601140aa1b1a1" | |||
solver-name = "gps-cdcl" | |||
solver-version = 1 |
@ -0,0 +1,49 @@ | |||
# Gopkg.toml example | |||
# | |||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md | |||
# for detailed Gopkg.toml documentation. | |||
# | |||
# required = ["github.com/user/thing/cmd/thing"] | |||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] | |||
# | |||
# [[constraint]] | |||
# name = "github.com/user/project" | |||
# version = "1.0.0" | |||
# | |||
# [[constraint]] | |||
# name = "github.com/user/project2" | |||
# branch = "dev" | |||
# source = "github.com/myfork/project2" | |||
# | |||
# [[override]] | |||
# name = "github.com/x/y" | |||
# version = "2.4.0" | |||
# | |||
# [prune] | |||
# non-go = false | |||
# go-tests = true | |||
# unused-packages = true | |||
[[constraint]] | |||
name = "github.com/btcsuite/btcutil" | |||
branch = "master" | |||
[[constraint]] | |||
name = "github.com/stretchr/testify" | |||
version = "1.2.1" | |||
[[constraint]] | |||
name = "github.com/tendermint/ed25519" | |||
branch = "master" | |||
[[constraint]] | |||
name = "github.com/tendermint/go-amino" | |||
version = "0.10.0-rc2" | |||
[[constraint]] | |||
name = "github.com/tendermint/tmlibs" | |||
version = "0.8.1" | |||
[prune] | |||
go-tests = true | |||
unused-packages = true |
@ -0,0 +1,193 @@ | |||
Tendermint Go-Crypto | |||
Copyright (C) 2015 Tendermint | |||
Apache License | |||
Version 2.0, January 2004 | |||
https://www.apache.org/licenses/ | |||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |||
1. Definitions. | |||
"License" shall mean the terms and conditions for use, reproduction, | |||
and distribution as defined by Sections 1 through 9 of this document. | |||
"Licensor" shall mean the copyright owner or entity authorized by | |||
the copyright owner that is granting the License. | |||
"Legal Entity" shall mean the union of the acting entity and all | |||
other entities that control, are controlled by, or are under common | |||
control with that entity. For the purposes of this definition, | |||
"control" means (i) the power, direct or indirect, to cause the | |||
direction or management of such entity, whether by contract or | |||
otherwise, or (ii) ownership of fifty percent (50%) or more of the | |||
outstanding shares, or (iii) beneficial ownership of such entity. | |||
"You" (or "Your") shall mean an individual or Legal Entity | |||
exercising permissions granted by this License. | |||
"Source" form shall mean the preferred form for making modifications, | |||
including but not limited to software source code, documentation | |||
source, and configuration files. | |||
"Object" form shall mean any form resulting from mechanical | |||
transformation or translation of a Source form, including but | |||
not limited to compiled object code, generated documentation, | |||
and conversions to other media types. | |||
"Work" shall mean the work of authorship, whether in Source or | |||
Object form, made available under the License, as indicated by a | |||
copyright notice that is included in or attached to the work | |||
(an example is provided in the Appendix below). | |||
"Derivative Works" shall mean any work, whether in Source or Object | |||
form, that is based on (or derived from) the Work and for which the | |||
editorial revisions, annotations, elaborations, or other modifications | |||
represent, as a whole, an original work of authorship. For the purposes | |||
of this License, Derivative Works shall not include works that remain | |||
separable from, or merely link (or bind by name) to the interfaces of, | |||
the Work and Derivative Works thereof. | |||
"Contribution" shall mean any work of authorship, including | |||
the original version of the Work and any modifications or additions | |||
to that Work or Derivative Works thereof, that is intentionally | |||
submitted to Licensor for inclusion in the Work by the copyright owner | |||
or by an individual or Legal Entity authorized to submit on behalf of | |||
the copyright owner. For the purposes of this definition, "submitted" | |||
means any form of electronic, verbal, or written communication sent | |||
to the Licensor or its representatives, including but not limited to | |||
communication on electronic mailing lists, source code control systems, | |||
and issue tracking systems that are managed by, or on behalf of, the | |||
Licensor for the purpose of discussing and improving the Work, but | |||
excluding communication that is conspicuously marked or otherwise | |||
designated in writing by the copyright owner as "Not a Contribution." | |||
"Contributor" shall mean Licensor and any individual or Legal Entity | |||
on behalf of whom a Contribution has been received by Licensor and | |||
subsequently incorporated within the Work. | |||
2. Grant of Copyright License. Subject to the terms and conditions of | |||
this License, each Contributor hereby grants to You a perpetual, | |||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |||
copyright license to reproduce, prepare Derivative Works of, | |||
publicly display, publicly perform, sublicense, and distribute the | |||
Work and such Derivative Works in Source or Object form. | |||
3. Grant of Patent License. Subject to the terms and conditions of | |||
this License, each Contributor hereby grants to You a perpetual, | |||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |||
(except as stated in this section) patent license to make, have made, | |||
use, offer to sell, sell, import, and otherwise transfer the Work, | |||
where such license applies only to those patent claims licensable | |||
by such Contributor that are necessarily infringed by their | |||
Contribution(s) alone or by combination of their Contribution(s) | |||
with the Work to which such Contribution(s) was submitted. If You | |||
institute patent litigation against any entity (including a | |||
cross-claim or counterclaim in a lawsuit) alleging that the Work | |||
or a Contribution incorporated within the Work constitutes direct | |||
or contributory patent infringement, then any patent licenses | |||
granted to You under this License for that Work shall terminate | |||
as of the date such litigation is filed. | |||
4. Redistribution. You may reproduce and distribute copies of the | |||
Work or Derivative Works thereof in any medium, with or without | |||
modifications, and in Source or Object form, provided that You | |||
meet the following conditions: | |||
(a) You must give any other recipients of the Work or | |||
Derivative Works a copy of this License; and | |||
(b) You must cause any modified files to carry prominent notices | |||
stating that You changed the files; and | |||
(c) You must retain, in the Source form of any Derivative Works | |||
that You distribute, all copyright, patent, trademark, and | |||
attribution notices from the Source form of the Work, | |||
excluding those notices that do not pertain to any part of | |||
the Derivative Works; and | |||
(d) If the Work includes a "NOTICE" text file as part of its | |||
distribution, then any Derivative Works that You distribute must | |||
include a readable copy of the attribution notices contained | |||
within such NOTICE file, excluding those notices that do not | |||
pertain to any part of the Derivative Works, in at least one | |||
of the following places: within a NOTICE text file distributed | |||
as part of the Derivative Works; within the Source form or | |||
documentation, if provided along with the Derivative Works; or, | |||
within a display generated by the Derivative Works, if and | |||
wherever such third-party notices normally appear. The contents | |||
of the NOTICE file are for informational purposes only and | |||
do not modify the License. You may add Your own attribution | |||
notices within Derivative Works that You distribute, alongside | |||
or as an addendum to the NOTICE text from the Work, provided | |||
that such additional attribution notices cannot be construed | |||
as modifying the License. | |||
You may add Your own copyright statement to Your modifications and | |||
may provide additional or different license terms and conditions | |||
for use, reproduction, or distribution of Your modifications, or | |||
for any such Derivative Works as a whole, provided Your use, | |||
reproduction, and distribution of the Work otherwise complies with | |||
the conditions stated in this License. | |||
5. Submission of Contributions. Unless You explicitly state otherwise, | |||
any Contribution intentionally submitted for inclusion in the Work | |||
by You to the Licensor shall be under the terms and conditions of | |||
this License, without any additional terms or conditions. | |||
Notwithstanding the above, nothing herein shall supersede or modify | |||
the terms of any separate license agreement you may have executed | |||
with Licensor regarding such Contributions. | |||
6. Trademarks. This License does not grant permission to use the trade | |||
names, trademarks, service marks, or product names of the Licensor, | |||
except as required for reasonable and customary use in describing the | |||
origin of the Work and reproducing the content of the NOTICE file. | |||
7. Disclaimer of Warranty. Unless required by applicable law or | |||
agreed to in writing, Licensor provides the Work (and each | |||
Contributor provides its Contributions) on an "AS IS" BASIS, | |||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |||
implied, including, without limitation, any warranties or conditions | |||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |||
PARTICULAR PURPOSE. You are solely responsible for determining the | |||
appropriateness of using or redistributing the Work and assume any | |||
risks associated with Your exercise of permissions under this License. | |||
8. Limitation of Liability. In no event and under no legal theory, | |||
whether in tort (including negligence), contract, or otherwise, | |||
unless required by applicable law (such as deliberate and grossly | |||
negligent acts) or agreed to in writing, shall any Contributor be | |||
liable to You for damages, including any direct, indirect, special, | |||
incidental, or consequential damages of any character arising as a | |||
result of this License or out of the use or inability to use the | |||
Work (including but not limited to damages for loss of goodwill, | |||
work stoppage, computer failure or malfunction, or any and all | |||
other commercial damages or losses), even if such Contributor | |||
has been advised of the possibility of such damages. | |||
9. Accepting Warranty or Additional Liability. While redistributing | |||
the Work or Derivative Works thereof, You may choose to offer, | |||
and charge a fee for, acceptance of support, warranty, indemnity, | |||
or other liability obligations and/or rights consistent with this | |||
License. However, in accepting such obligations, You may act only | |||
on Your own behalf and on Your sole responsibility, not on behalf | |||
of any other Contributor, and only if You agree to indemnify, | |||
defend, and hold each Contributor harmless for any liability | |||
incurred by, or claims asserted against, such Contributor by reason | |||
of your accepting any such warranty or additional liability. | |||
END OF TERMS AND CONDITIONS | |||
Licensed under the Apache License, Version 2.0 (the "License"); | |||
you may not use this file except in compliance with the License. | |||
You may obtain a copy of the License at | |||
https://www.apache.org/licenses/LICENSE-2.0 | |||
Unless required by applicable law or agreed to in writing, software | |||
distributed under the License is distributed on an "AS IS" BASIS, | |||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
See the License for the specific language governing permissions and | |||
limitations under the License. |
@ -0,0 +1,99 @@ | |||
GOTOOLS = \ | |||
github.com/golang/dep/cmd/dep \ | |||
# gopkg.in/alecthomas/gometalinter.v2 \ | |||
GOTOOLS_CHECK = dep #gometalinter.v2 | |||
all: check get_vendor_deps build test install | |||
check: check_tools | |||
######################################## | |||
### Build | |||
# Command to generate the workd list (kept here for documentation purposes only): | |||
wordlist: | |||
# To re-generate wordlist.go run: | |||
# go-bindata -ignore ".*\.go" -o keys/words/bip39/wordlist.go -pkg "wordlist" keys/bip39/wordlist/... | |||
build: wordlist | |||
# Nothing else to build! | |||
install: | |||
# Nothing to install! | |||
######################################## | |||
### Tools & dependencies | |||
check_tools: | |||
@# https://stackoverflow.com/a/25668869 | |||
@echo "Found tools: $(foreach tool,$(GOTOOLS_CHECK),\ | |||
$(if $(shell which $(tool)),$(tool),$(error "No $(tool) in PATH")))" | |||
get_tools: | |||
@echo "--> Installing tools" | |||
go get -u -v $(GOTOOLS) | |||
#@gometalinter.v2 --install | |||
update_tools: | |||
@echo "--> Updating tools" | |||
@go get -u $(GOTOOLS) | |||
get_vendor_deps: | |||
@rm -rf vendor/ | |||
@echo "--> Running dep ensure" | |||
@dep ensure | |||
######################################## | |||
### Testing | |||
test: | |||
CGO_ENABLED=0 go test -p 1 $(shell go list ./... | grep -v vendor) | |||
######################################## | |||
### Formatting, linting, and vetting | |||
fmt: | |||
@go fmt ./... | |||
metalinter: | |||
@echo "==> Running linter" | |||
gometalinter.v2 --vendor --deadline=600s --disable-all \ | |||
--enable=maligned \ | |||
--enable=deadcode \ | |||
--enable=goconst \ | |||
--enable=goimports \ | |||
--enable=gosimple \ | |||
--enable=ineffassign \ | |||
--enable=megacheck \ | |||
--enable=misspell \ | |||
--enable=staticcheck \ | |||
--enable=safesql \ | |||
--enable=structcheck \ | |||
--enable=unconvert \ | |||
--enable=unused \ | |||
--enable=varcheck \ | |||
--enable=vetshadow \ | |||
./... | |||
#--enable=gas \ | |||
#--enable=dupl \ | |||
#--enable=errcheck \ | |||
#--enable=gocyclo \ | |||
#--enable=golint \ <== comments on anything exported | |||
#--enable=gotype \ | |||
#--enable=interfacer \ | |||
#--enable=unparam \ | |||
#--enable=vet \ | |||
metalinter_all: | |||
protoc $(INCLUDE) --lint_out=. types/*.proto | |||
gometalinter.v2 --vendor --deadline=600s --enable-all --disable=lll ./... | |||
# To avoid unintended conflicts with file names, always add to .PHONY | |||
# unless there is a reason not to. | |||
# https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html | |||
.PHONEY: check build install check_tools get_tools update_tools get_vendor_deps test fmt metalinter metalinter_all |
@ -0,0 +1,25 @@ | |||
# go-crypto [![GoDoc](https://godoc.org/github.com/tendermint/go-crypto?status.svg)](https://godoc.org/github.com/tendermint/go-crypto) | |||
go-crypto is the cryptographic package adapted for Tendermint's uses | |||
## Importing it | |||
`import "github.com/tendermint/go-crypto"` | |||
## Binary encoding | |||
For Binary encoding, please refer to the [Tendermint encoding spec](https://github.com/tendermint/tendermint/blob/master/docs/spec/blockchain/encoding.md). | |||
## JSON Encoding | |||
go-crypto `.Bytes()` uses Amino:binary encoding, but Amino:JSON is also supported. | |||
```go | |||
Example Amino:JSON encodings: | |||
crypto.PrivKeyEd25519 - {"type":"954568A3288910","value":"EVkqJO/jIXp3rkASXfh9YnyToYXRXhBr6g9cQVxPFnQBP/5povV4HTjvsy530kybxKHwEi85iU8YL0qQhSYVoQ=="} | |||
crypto.SignatureEd25519 - {"type":"6BF5903DA1DB28","value":"77sQNZOrf7ltExpf7AV1WaYPCHbyRLgjBsoWVzcduuLk+jIGmYk+s5R6Emm29p12HeiNAuhUJgdFGmwkpeGJCA=="} | |||
crypto.PubKeyEd25519 - {"type":"AC26791624DE60","value":"AT/+aaL1eB0477Mud9JMm8Sh8BIvOYlPGC9KkIUmFaE="} | |||
crypto.PrivKeySecp256k1 - {"type":"019E82E1B0F798","value":"zx4Pnh67N+g2V+5vZbQzEyRerX9c4ccNZOVzM9RvJ0Y="} | |||
crypto.SignatureSecp256k1 - {"type":"6D1EA416E1FEE8","value":"MEUCIQCIg5TqS1l7I+MKTrSPIuUN2+4m5tA29dcauqn3NhEJ2wIgICaZ+lgRc5aOTVahU/XoLopXKn8BZcl0bnuYWLvohR8="} | |||
crypto.PubKeySecp256k1 - {"type":"F8CCEAEB5AE980","value":"A8lPKJXcNl5VHt1FK8a244K9EJuS4WX1hFBnwisi0IJx"} | |||
``` |
@ -0,0 +1,37 @@ | |||
package crypto | |||
import ( | |||
amino "github.com/tendermint/go-amino" | |||
) | |||
var cdc = amino.NewCodec() | |||
func init() { | |||
// NOTE: It's important that there be no conflicts here, | |||
// as that would change the canonical representations, | |||
// and therefore change the address. | |||
// TODO: Add feature to go-amino to ensure that there | |||
// are no conflicts. | |||
RegisterAmino(cdc) | |||
} | |||
// RegisterAmino registers all go-crypto related types in the given (amino) codec. | |||
func RegisterAmino(cdc *amino.Codec) { | |||
cdc.RegisterInterface((*PubKey)(nil), nil) | |||
cdc.RegisterConcrete(PubKeyEd25519{}, | |||
"tendermint/PubKeyEd25519", nil) | |||
cdc.RegisterConcrete(PubKeySecp256k1{}, | |||
"tendermint/PubKeySecp256k1", nil) | |||
cdc.RegisterInterface((*PrivKey)(nil), nil) | |||
cdc.RegisterConcrete(PrivKeyEd25519{}, | |||
"tendermint/PrivKeyEd25519", nil) | |||
cdc.RegisterConcrete(PrivKeySecp256k1{}, | |||
"tendermint/PrivKeySecp256k1", nil) | |||
cdc.RegisterInterface((*Signature)(nil), nil) | |||
cdc.RegisterConcrete(SignatureEd25519{}, | |||
"tendermint/SignatureEd25519", nil) | |||
cdc.RegisterConcrete(SignatureSecp256k1{}, | |||
"tendermint/SignatureSecp256k1", nil) | |||
} |
@ -0,0 +1,39 @@ | |||
package crypto | |||
import ( | |||
"bytes" | |||
"fmt" | |||
"io/ioutil" | |||
"golang.org/x/crypto/openpgp/armor" | |||
) | |||
func EncodeArmor(blockType string, headers map[string]string, data []byte) string { | |||
buf := new(bytes.Buffer) | |||
w, err := armor.Encode(buf, blockType, headers) | |||
if err != nil { | |||
panic(fmt.Errorf("could not encode ascii armor: %s", err)) | |||
} | |||
_, err = w.Write(data) | |||
if err != nil { | |||
panic(fmt.Errorf("could not encode ascii armor: %s", err)) | |||
} | |||
err = w.Close() | |||
if err != nil { | |||
panic(fmt.Errorf("could not encode ascii armor: %s", err)) | |||
} | |||
return buf.String() | |||
} | |||
func DecodeArmor(armorStr string) (blockType string, headers map[string]string, data []byte, err error) { | |||
buf := bytes.NewBufferString(armorStr) | |||
block, err := armor.Decode(buf) | |||
if err != nil { | |||
return "", nil, nil, err | |||
} | |||
data, err = ioutil.ReadAll(block.Body) | |||
if err != nil { | |||
return "", nil, nil, err | |||
} | |||
return block.Type, block.Header, data, nil | |||
} |
@ -0,0 +1,20 @@ | |||
package crypto | |||
import ( | |||
"testing" | |||
"github.com/stretchr/testify/assert" | |||
"github.com/stretchr/testify/require" | |||
) | |||
func TestSimpleArmor(t *testing.T) { | |||
blockType := "MINT TEST" | |||
data := []byte("somedata") | |||
armorStr := EncodeArmor(blockType, nil, data) | |||
// Decode armorStr and test for equivalence. | |||
blockType2, _, data2, err := DecodeArmor(armorStr) | |||
require.Nil(t, err, "%+v", err) | |||
assert.Equal(t, blockType, blockType2) | |||
assert.Equal(t, data, data2) | |||
} |
@ -0,0 +1,48 @@ | |||
/* | |||
go-crypto is a customized/convenience cryptography package | |||
for supporting Tendermint. | |||
It wraps select functionality of equivalent functions in the | |||
Go standard library, for easy usage with our libraries. | |||
Keys: | |||
All key generation functions return an instance of the PrivKey interface | |||
which implements methods | |||
AssertIsPrivKeyInner() | |||
Bytes() []byte | |||
Sign(msg []byte) Signature | |||
PubKey() PubKey | |||
Equals(PrivKey) bool | |||
Wrap() PrivKey | |||
From the above method we can: | |||
a) Retrieve the public key if needed | |||
pubKey := key.PubKey() | |||
For example: | |||
privKey, err := crypto.GenPrivKeyEd25519() | |||
if err != nil { | |||
... | |||
} | |||
pubKey := privKey.PubKey() | |||
... | |||
// And then you can use the private and public key | |||
doSomething(privKey, pubKey) | |||
We also provide hashing wrappers around algorithms: | |||
Sha256 | |||
sum := crypto.Sha256([]byte("This is Tendermint")) | |||
fmt.Printf("%x\n", sum) | |||
Ripemd160 | |||
sum := crypto.Ripemd160([]byte("This is consensus")) | |||
fmt.Printf("%x\n", sum) | |||
*/ | |||
package crypto | |||
// TODO: Add more docs in here |
@ -0,0 +1,119 @@ | |||
package crypto | |||
import ( | |||
"os" | |||
"testing" | |||
"github.com/stretchr/testify/assert" | |||
"github.com/stretchr/testify/require" | |||
) | |||
type byter interface { | |||
Bytes() []byte | |||
} | |||
func checkAminoBinary(t *testing.T, src byter, dst interface{}, size int) { | |||
// Marshal to binary bytes. | |||
bz, err := cdc.MarshalBinaryBare(src) | |||
require.Nil(t, err, "%+v", err) | |||
// Make sure this is compatible with current (Bytes()) encoding. | |||
assert.Equal(t, src.Bytes(), bz, "Amino binary vs Bytes() mismatch") | |||
// Make sure we have the expected length. | |||
if size != -1 { | |||
assert.Equal(t, size, len(bz), "Amino binary size mismatch") | |||
} | |||
// Unmarshal. | |||
err = cdc.UnmarshalBinaryBare(bz, dst) | |||
require.Nil(t, err, "%+v", err) | |||
} | |||
func checkAminoJSON(t *testing.T, src interface{}, dst interface{}, isNil bool) { | |||
// Marshal to JSON bytes. | |||
js, err := cdc.MarshalJSON(src) | |||
require.Nil(t, err, "%+v", err) | |||
if isNil { | |||
assert.Equal(t, string(js), `null`) | |||
} else { | |||
assert.Contains(t, string(js), `"type":`) | |||
assert.Contains(t, string(js), `"value":`) | |||
} | |||
// Unmarshal. | |||
err = cdc.UnmarshalJSON(js, dst) | |||
require.Nil(t, err, "%+v", err) | |||
} | |||
func ExamplePrintRegisteredTypes() { | |||
cdc.PrintTypes(os.Stdout) | |||
// Output: | Type | Name | Prefix | Length | Notes | | |||
//| ---- | ---- | ------ | ----- | ------ | | |||
//| PubKeyEd25519 | tendermint/PubKeyEd25519 | 0x1624DE64 | 0x20 | | | |||
//| PubKeySecp256k1 | tendermint/PubKeySecp256k1 | 0xEB5AE987 | 0x21 | | | |||
//| PrivKeyEd25519 | tendermint/PrivKeyEd25519 | 0xA3288910 | 0x40 | | | |||
//| PrivKeySecp256k1 | tendermint/PrivKeySecp256k1 | 0xE1B0F79B | 0x20 | | | |||
//| SignatureEd25519 | tendermint/SignatureEd25519 | 0x2031EA53 | 0x40 | | | |||
//| SignatureSecp256k1 | tendermint/SignatureSecp256k1 | 0x7FC4A495 | variable | | | |||
} | |||
func TestKeyEncodings(t *testing.T) { | |||
cases := []struct { | |||
privKey PrivKey | |||
privSize, pubSize int // binary sizes | |||
}{ | |||
{ | |||
privKey: GenPrivKeyEd25519(), | |||
privSize: 69, | |||
pubSize: 37, | |||
}, | |||
{ | |||
privKey: GenPrivKeySecp256k1(), | |||
privSize: 37, | |||
pubSize: 38, | |||
}, | |||
} | |||
for _, tc := range cases { | |||
// Check (de/en)codings of PrivKeys. | |||
var priv2, priv3 PrivKey | |||
checkAminoBinary(t, tc.privKey, &priv2, tc.privSize) | |||
assert.EqualValues(t, tc.privKey, priv2) | |||
checkAminoJSON(t, tc.privKey, &priv3, false) // TODO also check Prefix bytes. | |||
assert.EqualValues(t, tc.privKey, priv3) | |||
// Check (de/en)codings of Signatures. | |||
var sig1, sig2, sig3 Signature | |||
sig1, err := tc.privKey.Sign([]byte("something")) | |||
assert.NoError(t, err) | |||
checkAminoBinary(t, sig1, &sig2, -1) // Siganture size changes for Secp anyways. | |||
assert.EqualValues(t, sig1, sig2) | |||
checkAminoJSON(t, sig1, &sig3, false) // TODO also check Prefix bytes. | |||
assert.EqualValues(t, sig1, sig3) | |||
// Check (de/en)codings of PubKeys. | |||
pubKey := tc.privKey.PubKey() | |||
var pub2, pub3 PubKey | |||
checkAminoBinary(t, pubKey, &pub2, tc.pubSize) | |||
assert.EqualValues(t, pubKey, pub2) | |||
checkAminoJSON(t, pubKey, &pub3, false) // TODO also check Prefix bytes. | |||
assert.EqualValues(t, pubKey, pub3) | |||
} | |||
} | |||
func TestNilEncodings(t *testing.T) { | |||
// Check nil Signature. | |||
var a, b Signature | |||
checkAminoJSON(t, &a, &b, true) | |||
assert.EqualValues(t, a, b) | |||
// Check nil PubKey. | |||
var c, d PubKey | |||
checkAminoJSON(t, &c, &d, true) | |||
assert.EqualValues(t, c, d) | |||
// Check nil PrivKey. | |||
var e, f PrivKey | |||
checkAminoJSON(t, &e, &f, true) | |||
assert.EqualValues(t, e, f) | |||
} |
@ -0,0 +1,35 @@ | |||
// Copyright 2017 Tendermint. All Rights Reserved. | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
package crypto_test | |||
import ( | |||
"fmt" | |||
"github.com/tendermint/go-crypto" | |||
) | |||
func ExampleSha256() { | |||
sum := crypto.Sha256([]byte("This is Tendermint")) | |||
fmt.Printf("%x\n", sum) | |||
// Output: | |||
// f91afb642f3d1c87c17eb01aae5cb65c242dfdbe7cf1066cc260f4ce5d33b94e | |||
} | |||
func ExampleRipemd160() { | |||
sum := crypto.Ripemd160([]byte("This is Tendermint")) | |||
fmt.Printf("%x\n", sum) | |||
// Output: | |||
// 051e22663e8f0fd2f2302f1210f954adff009005 | |||
} |
@ -0,0 +1,18 @@ | |||
package crypto | |||
import ( | |||
"crypto/sha256" | |||
"golang.org/x/crypto/ripemd160" | |||
) | |||
func Sha256(bytes []byte) []byte { | |||
hasher := sha256.New() | |||
hasher.Write(bytes) | |||
return hasher.Sum(nil) | |||
} | |||
func Ripemd160(bytes []byte) []byte { | |||
hasher := ripemd160.New() | |||
hasher.Write(bytes) | |||
return hasher.Sum(nil) | |||
} |
@ -0,0 +1,105 @@ | |||
// Package hkdfchacha20poly1305 creates an AEAD using hkdf, chacha20, and poly1305 | |||
// When sealing and opening, the hkdf is used to obtain the nonce and subkey for | |||
// chacha20. Other than the change for the how the subkey and nonce for chacha | |||
// are obtained, this is the same as chacha20poly1305 | |||
package hkdfchacha20poly1305 | |||
import ( | |||
"crypto/cipher" | |||
"crypto/sha256" | |||
"errors" | |||
"io" | |||
"golang.org/x/crypto/chacha20poly1305" | |||
"golang.org/x/crypto/hkdf" | |||
) | |||
type hkdfchacha20poly1305 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 | |||
// TagSize is the size added from poly1305 | |||
TagSize = 16 | |||
// MaxPlaintextSize is the max size that can be passed into a single call of Seal | |||
MaxPlaintextSize = (1 << 38) - 64 | |||
// MaxCiphertextSize is the max size that can be passed into a single call of Open, | |||
// this differs from plaintext size due to the tag | |||
MaxCiphertextSize = (1 << 38) - 48 | |||
// HkdfInfo is the parameter used internally for Hkdf's info parameter. | |||
HkdfInfo = "TENDERMINT_SECRET_CONNECTION_FRAME_KEY_DERIVE" | |||
) | |||
//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(hkdfchacha20poly1305) | |||
copy(ret.key[:], key) | |||
return ret, nil | |||
} | |||
func (c *hkdfchacha20poly1305) NonceSize() int { | |||
return NonceSize | |||
} | |||
func (c *hkdfchacha20poly1305) Overhead() int { | |||
return TagSize | |||
} | |||
func (c *hkdfchacha20poly1305) Seal(dst, nonce, plaintext, additionalData []byte) []byte { | |||
if len(nonce) != NonceSize { | |||
panic("hkdfchacha20poly1305: bad nonce length passed to Seal") | |||
} | |||
if uint64(len(plaintext)) > MaxPlaintextSize { | |||
panic("hkdfchacha20poly1305: plaintext too large") | |||
} | |||
subKey, chachaNonce := getSubkeyAndChachaNonceFromHkdf(&c.key, &nonce) | |||
aead, err := chacha20poly1305.New(subKey[:]) | |||
if err != nil { | |||
panic("hkdfchacha20poly1305: failed to initialize chacha20poly1305") | |||
} | |||
return aead.Seal(dst, chachaNonce[:], plaintext, additionalData) | |||
} | |||
func (c *hkdfchacha20poly1305) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) { | |||
if len(nonce) != NonceSize { | |||
return nil, errors.New("hkdfchacha20poly1305: bad nonce length passed to Open") | |||
} | |||
if uint64(len(ciphertext)) > MaxCiphertextSize { | |||
return nil, errors.New("hkdfchacha20poly1305: ciphertext too large") | |||
} | |||
subKey, chachaNonce := getSubkeyAndChachaNonceFromHkdf(&c.key, &nonce) | |||
aead, err := chacha20poly1305.New(subKey[:]) | |||
if err != nil { | |||
panic("hkdfchacha20poly1305: failed to initialize chacha20poly1305") | |||
} | |||
return aead.Open(dst, chachaNonce[:], ciphertext, additionalData) | |||
} | |||
func getSubkeyAndChachaNonceFromHkdf(cKey *[32]byte, nonce *[]byte) ( | |||
subKey [KeySize]byte, chachaNonce [chacha20poly1305.NonceSize]byte) { | |||
hash := sha256.New | |||
hkdf := hkdf.New(hash, (*cKey)[:], *nonce, []byte(HkdfInfo)) | |||
_, err := io.ReadFull(hkdf, subKey[:]) | |||
if err != nil { | |||
panic("hkdfchacha20poly1305: failed to read subkey from hkdf") | |||
} | |||
_, err = io.ReadFull(hkdf, chachaNonce[:]) | |||
if err != nil { | |||
panic("hkdfchacha20poly1305: failed to read chachaNonce from hkdf") | |||
} | |||
return | |||
} |
@ -0,0 +1,139 @@ | |||
package hkdfchacha20poly1305 | |||
import ( | |||
"bytes" | |||
cr "crypto/rand" | |||
"encoding/hex" | |||
mr "math/rand" | |||
"testing" | |||
"github.com/stretchr/testify/assert" | |||
) | |||
// Test that a test vector we generated is valid. (Ensures backwards | |||
// compatability) | |||
func TestVector(t *testing.T) { | |||
key, _ := hex.DecodeString("56f8de45d3c294c7675bcaf457bdd4b71c380b9b2408ce9412b348d0f08b69ee") | |||
aead, err := New(key[:]) | |||
if err != nil { | |||
t.Fatal(err) | |||
} | |||
cts := []string{"e20a8bf42c535ac30125cfc52031577f0b", | |||
"657695b37ba30f67b25860d90a6f1d00d8", | |||
"e9aa6f3b7f625d957fd50f05bcdf20d014", | |||
"8a00b3b5a6014e0d2033bebc5935086245", | |||
"aadd74867b923879e6866ea9e03c009039", | |||
"fc59773c2c864ee3b4cc971876b3c7bed4", | |||
"caec14e3a9a52ce1a2682c6737defa4752", | |||
"0b89511ffe490d2049d6950494ee51f919", | |||
"7de854ea71f43ca35167a07566c769083d", | |||
"cd477327f4ea4765c71e311c5fec1edbfb"} | |||
for i := 0; i < 10; i++ { | |||
ct, _ := hex.DecodeString(cts[i]) | |||
byteArr := []byte{byte(i)} | |||
nonce := make([]byte, 24, 24) | |||
nonce[0] = byteArr[0] | |||
plaintext, err := aead.Open(nil, nonce, ct, byteArr) | |||
if err != nil { | |||
t.Errorf("%dth Open failed", i) | |||
continue | |||
} | |||
assert.Equal(t, byteArr, plaintext) | |||
} | |||
} | |||
// The following test is taken from | |||
// https://github.com/golang/crypto/blob/master/chacha20poly1305/chacha20poly1305_test.go#L69 | |||
// It requires the below copyright notice, where "this source code" refers to the following function. | |||
// Copyright 2016 The Go Authors. All rights reserved. | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found at the bottom of this file. | |||
func TestRandom(t *testing.T) { | |||
// Some random tests to verify Open(Seal) == Plaintext | |||
for i := 0; i < 256; i++ { | |||
var nonce [24]byte | |||
var key [32]byte | |||
al := mr.Intn(128) | |||
pl := mr.Intn(16384) | |||
ad := make([]byte, al) | |||
plaintext := make([]byte, pl) | |||
cr.Read(key[:]) | |||
cr.Read(nonce[:]) | |||
cr.Read(ad) | |||
cr.Read(plaintext) | |||
aead, err := New(key[:]) | |||
if err != nil { | |||
t.Fatal(err) | |||
} | |||
ct := aead.Seal(nil, nonce[:], plaintext, ad) | |||
plaintext2, err := aead.Open(nil, nonce[:], ct, ad) | |||
if err != nil { | |||
t.Errorf("Random #%d: Open failed", i) | |||
continue | |||
} | |||
if !bytes.Equal(plaintext, plaintext2) { | |||
t.Errorf("Random #%d: plaintext's don't match: got %x vs %x", i, plaintext2, plaintext) | |||
continue | |||
} | |||
if len(ad) > 0 { | |||
alterAdIdx := mr.Intn(len(ad)) | |||
ad[alterAdIdx] ^= 0x80 | |||
if _, err := aead.Open(nil, nonce[:], ct, ad); err == nil { | |||
t.Errorf("Random #%d: Open was successful after altering additional data", i) | |||
} | |||
ad[alterAdIdx] ^= 0x80 | |||
} | |||
alterNonceIdx := mr.Intn(aead.NonceSize()) | |||
nonce[alterNonceIdx] ^= 0x80 | |||
if _, err := aead.Open(nil, nonce[:], ct, ad); err == nil { | |||
t.Errorf("Random #%d: Open was successful after altering nonce", i) | |||
} | |||
nonce[alterNonceIdx] ^= 0x80 | |||
alterCtIdx := mr.Intn(len(ct)) | |||
ct[alterCtIdx] ^= 0x80 | |||
if _, err := aead.Open(nil, nonce[:], ct, ad); err == nil { | |||
t.Errorf("Random #%d: Open was successful after altering ciphertext", i) | |||
} | |||
ct[alterCtIdx] ^= 0x80 | |||
} | |||
} | |||
// AFOREMENTIONED LICENCE | |||
// Copyright (c) 2009 The Go Authors. All rights reserved. | |||
// | |||
// Redistribution and use in source and binary forms, with or without | |||
// modification, are permitted provided that the following conditions are | |||
// met: | |||
// | |||
// * Redistributions of source code must retain the above copyright | |||
// notice, this list of conditions and the following disclaimer. | |||
// * Redistributions in binary form must reproduce the above | |||
// copyright notice, this list of conditions and the following disclaimer | |||
// in the documentation and/or other materials provided with the | |||
// distribution. | |||
// * Neither the name of Google Inc. nor the names of its | |||
// contributors may be used to endorse or promote products derived from | |||
// this software without specific prior written permission. | |||
// | |||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
@ -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,88 @@ | |||
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 | |||
// The value is hashed, 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: []byte(key), | |||
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, "fa9bc106ffd932d919bee935ceb6cf2b3dd72d8f", fmt.Sprintf("%x", db.Hash()), "Hash didn't match") | |||
} | |||
{ | |||
db := newSimpleMap() | |||
db.Set("key1", strHasher("value2")) | |||
assert.Equal(t, "e00e7dcfe54e9fafef5111e813a587f01ba9c3e8", fmt.Sprintf("%x", db.Hash()), "Hash didn't match") | |||
} | |||
{ | |||
db := newSimpleMap() | |||
db.Set("key1", strHasher("value1")) | |||
db.Set("key2", strHasher("value2")) | |||
assert.Equal(t, "eff12d1c703a1022ab509287c0f196130123d786", 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, "eff12d1c703a1022ab509287c0f196130123d786", 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, "b2c62a277c08dbd2ad73ca53cd1d6bfdf5830d26", 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, "b2c62a277c08dbd2ad73ca53cd1d6bfdf5830d26", fmt.Sprintf("%x", db.Hash()), "Hash didn't match") | |||
} | |||
} |
@ -0,0 +1,160 @@ | |||
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 map[string]*SimpleProof, keys []string) { | |||
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)) | |||
} | |||
rootHash, proofList := SimpleProofsFromHashers(kvsH) | |||
proofs = make(map[string]*SimpleProof) | |||
keys = make([]string, len(proofList)) | |||
for i, kvp := range kvs { | |||
proofs[string(kvp.Key)] = proofList[i] | |||
keys[i] = string(kvp.Key) | |||
} | |||
return | |||
} | |||
// 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,164 @@ | |||
package crypto | |||
import ( | |||
"crypto/subtle" | |||
secp256k1 "github.com/btcsuite/btcd/btcec" | |||
"github.com/tendermint/ed25519" | |||
"github.com/tendermint/ed25519/extra25519" | |||
) | |||
func PrivKeyFromBytes(privKeyBytes []byte) (privKey PrivKey, err error) { | |||
err = cdc.UnmarshalBinaryBare(privKeyBytes, &privKey) | |||
return | |||
} | |||
//---------------------------------------- | |||
type PrivKey interface { | |||
Bytes() []byte | |||
Sign(msg []byte) (Signature, error) | |||
PubKey() PubKey | |||
Equals(PrivKey) bool | |||
} | |||
//------------------------------------- | |||
var _ PrivKey = PrivKeyEd25519{} | |||
// Implements PrivKey | |||
type PrivKeyEd25519 [64]byte | |||
func (privKey PrivKeyEd25519) Bytes() []byte { | |||
return cdc.MustMarshalBinaryBare(privKey) | |||
} | |||
func (privKey PrivKeyEd25519) Sign(msg []byte) (Signature, error) { | |||
privKeyBytes := [64]byte(privKey) | |||
signatureBytes := ed25519.Sign(&privKeyBytes, msg) | |||
return SignatureEd25519(*signatureBytes), nil | |||
} | |||
func (privKey PrivKeyEd25519) PubKey() PubKey { | |||
privKeyBytes := [64]byte(privKey) | |||
pubBytes := *ed25519.MakePublicKey(&privKeyBytes) | |||
return PubKeyEd25519(pubBytes) | |||
} | |||
// Equals - you probably don't need to use this. | |||
// Runs in constant time based on length of the keys. | |||
func (privKey PrivKeyEd25519) Equals(other PrivKey) bool { | |||
if otherEd, ok := other.(PrivKeyEd25519); ok { | |||
return subtle.ConstantTimeCompare(privKey[:], otherEd[:]) == 1 | |||
} else { | |||
return false | |||
} | |||
} | |||
func (privKey PrivKeyEd25519) ToCurve25519() *[32]byte { | |||
keyCurve25519 := new([32]byte) | |||
privKeyBytes := [64]byte(privKey) | |||
extra25519.PrivateKeyToCurve25519(keyCurve25519, &privKeyBytes) | |||
return keyCurve25519 | |||
} | |||
// Deterministically generates new priv-key bytes from key. | |||
func (privKey PrivKeyEd25519) Generate(index int) PrivKeyEd25519 { | |||
bz, err := cdc.MarshalBinaryBare(struct { | |||
PrivKey [64]byte | |||
Index int | |||
}{privKey, index}) | |||
if err != nil { | |||
panic(err) | |||
} | |||
newBytes := Sha256(bz) | |||
newKey := new([64]byte) | |||
copy(newKey[:32], newBytes) | |||
ed25519.MakePublicKey(newKey) | |||
return PrivKeyEd25519(*newKey) | |||
} | |||
func GenPrivKeyEd25519() PrivKeyEd25519 { | |||
privKeyBytes := new([64]byte) | |||
copy(privKeyBytes[:32], CRandBytes(32)) | |||
ed25519.MakePublicKey(privKeyBytes) | |||
return PrivKeyEd25519(*privKeyBytes) | |||
} | |||
// NOTE: secret should be the output of a KDF like bcrypt, | |||
// if it's derived from user input. | |||
func GenPrivKeyEd25519FromSecret(secret []byte) PrivKeyEd25519 { | |||
privKey32 := Sha256(secret) // Not Ripemd160 because we want 32 bytes. | |||
privKeyBytes := new([64]byte) | |||
copy(privKeyBytes[:32], privKey32) | |||
ed25519.MakePublicKey(privKeyBytes) | |||
return PrivKeyEd25519(*privKeyBytes) | |||
} | |||
//------------------------------------- | |||
var _ PrivKey = PrivKeySecp256k1{} | |||
// Implements PrivKey | |||
type PrivKeySecp256k1 [32]byte | |||
func (privKey PrivKeySecp256k1) Bytes() []byte { | |||
return cdc.MustMarshalBinaryBare(privKey) | |||
} | |||
func (privKey PrivKeySecp256k1) Sign(msg []byte) (Signature, error) { | |||
priv__, _ := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKey[:]) | |||
sig__, err := priv__.Sign(Sha256(msg)) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return SignatureSecp256k1(sig__.Serialize()), nil | |||
} | |||
func (privKey PrivKeySecp256k1) PubKey() PubKey { | |||
_, pub__ := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKey[:]) | |||
var pub PubKeySecp256k1 | |||
copy(pub[:], pub__.SerializeCompressed()) | |||
return pub | |||
} | |||
// Equals - you probably don't need to use this. | |||
// Runs in constant time based on length of the keys. | |||
func (privKey PrivKeySecp256k1) Equals(other PrivKey) bool { | |||
if otherSecp, ok := other.(PrivKeySecp256k1); ok { | |||
return subtle.ConstantTimeCompare(privKey[:], otherSecp[:]) == 1 | |||
} else { | |||
return false | |||
} | |||
} | |||
/* | |||
// Deterministically generates new priv-key bytes from key. | |||
func (key PrivKeySecp256k1) Generate(index int) PrivKeySecp256k1 { | |||
newBytes := cdc.BinarySha256(struct { | |||
PrivKey [64]byte | |||
Index int | |||
}{key, index}) | |||
var newKey [64]byte | |||
copy(newKey[:], newBytes) | |||
return PrivKeySecp256k1(newKey) | |||
} | |||
*/ | |||
func GenPrivKeySecp256k1() PrivKeySecp256k1 { | |||
privKeyBytes := [32]byte{} | |||
copy(privKeyBytes[:], CRandBytes(32)) | |||
priv, _ := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKeyBytes[:]) | |||
copy(privKeyBytes[:], priv.Serialize()) | |||
return PrivKeySecp256k1(privKeyBytes) | |||
} | |||
// NOTE: secret should be the output of a KDF like bcrypt, | |||
// if it's derived from user input. | |||
func GenPrivKeySecp256k1FromSecret(secret []byte) PrivKeySecp256k1 { | |||
privKey32 := Sha256(secret) // Not Ripemd160 because we want 32 bytes. | |||
priv, _ := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKey32) | |||
privKeyBytes := [32]byte{} | |||
copy(privKeyBytes[:], priv.Serialize()) | |||
return PrivKeySecp256k1(privKeyBytes) | |||
} |
@ -0,0 +1,60 @@ | |||
package crypto_test | |||
import ( | |||
"testing" | |||
"github.com/stretchr/testify/assert" | |||
crypto "github.com/tendermint/go-crypto" | |||
) | |||
func TestGeneratePrivKey(t *testing.T) { | |||
testPriv := crypto.GenPrivKeyEd25519() | |||
testGenerate := testPriv.Generate(1) | |||
signBytes := []byte("something to sign") | |||
pub := testGenerate.PubKey() | |||
sig, err := testGenerate.Sign(signBytes) | |||
assert.NoError(t, err) | |||
assert.True(t, pub.VerifyBytes(signBytes, sig)) | |||
} | |||
/* | |||
type BadKey struct { | |||
PrivKeyEd25519 | |||
} | |||
func TestReadPrivKey(t *testing.T) { | |||
assert, require := assert.New(t), require.New(t) | |||
// garbage in, garbage out | |||
garbage := []byte("hjgewugfbiewgofwgewr") | |||
XXX This test wants to register BadKey globally to go-crypto, | |||
but we don't want to support that. | |||
_, err := PrivKeyFromBytes(garbage) | |||
require.Error(err) | |||
edKey := GenPrivKeyEd25519() | |||
badKey := BadKey{edKey} | |||
cases := []struct { | |||
key PrivKey | |||
valid bool | |||
}{ | |||
{edKey, true}, | |||
{badKey, false}, | |||
} | |||
for i, tc := range cases { | |||
data := tc.key.Bytes() | |||
fmt.Println(">>>", data) | |||
key, err := PrivKeyFromBytes(data) | |||
fmt.Printf("!!! %#v\n", key, err) | |||
if tc.valid { | |||
assert.NoError(err, "%d", i) | |||
assert.Equal(tc.key, key, "%d", i) | |||
} else { | |||
assert.Error(err, "%d: %#v", i, key) | |||
} | |||
} | |||
} | |||
*/ |
@ -0,0 +1,149 @@ | |||
package crypto | |||
import ( | |||
"bytes" | |||
"crypto/sha256" | |||
"fmt" | |||
"golang.org/x/crypto/ripemd160" | |||
secp256k1 "github.com/btcsuite/btcd/btcec" | |||
"github.com/tendermint/ed25519" | |||
"github.com/tendermint/ed25519/extra25519" | |||
cmn "github.com/tendermint/tmlibs/common" | |||
"github.com/tendermint/go-crypto/tmhash" | |||
) | |||
// An address is a []byte, but hex-encoded even in JSON. | |||
// []byte leaves us the option to change the address length. | |||
// Use an alias so Unmarshal methods (with ptr receivers) are available too. | |||
type Address = cmn.HexBytes | |||
func PubKeyFromBytes(pubKeyBytes []byte) (pubKey PubKey, err error) { | |||
err = cdc.UnmarshalBinaryBare(pubKeyBytes, &pubKey) | |||
return | |||
} | |||
//---------------------------------------- | |||
type PubKey interface { | |||
Address() Address | |||
Bytes() []byte | |||
VerifyBytes(msg []byte, sig Signature) bool | |||
Equals(PubKey) bool | |||
} | |||
//------------------------------------- | |||
var _ PubKey = PubKeyEd25519{} | |||
// Implements PubKeyInner | |||
type PubKeyEd25519 [32]byte | |||
// Address is the SHA256-20 of the raw pubkey bytes. | |||
func (pubKey PubKeyEd25519) Address() Address { | |||
return Address(tmhash.Sum(pubKey[:])) | |||
} | |||
func (pubKey PubKeyEd25519) Bytes() []byte { | |||
bz, err := cdc.MarshalBinaryBare(pubKey) | |||
if err != nil { | |||
panic(err) | |||
} | |||
return bz | |||
} | |||
func (pubKey PubKeyEd25519) VerifyBytes(msg []byte, sig_ Signature) bool { | |||
// make sure we use the same algorithm to sign | |||
sig, ok := sig_.(SignatureEd25519) | |||
if !ok { | |||
return false | |||
} | |||
pubKeyBytes := [32]byte(pubKey) | |||
sigBytes := [64]byte(sig) | |||
return ed25519.Verify(&pubKeyBytes, msg, &sigBytes) | |||
} | |||
// For use with golang/crypto/nacl/box | |||
// If error, returns nil. | |||
func (pubKey PubKeyEd25519) ToCurve25519() *[32]byte { | |||
keyCurve25519, pubKeyBytes := new([32]byte), [32]byte(pubKey) | |||
ok := extra25519.PublicKeyToCurve25519(keyCurve25519, &pubKeyBytes) | |||
if !ok { | |||
return nil | |||
} | |||
return keyCurve25519 | |||
} | |||
func (pubKey PubKeyEd25519) String() string { | |||
return fmt.Sprintf("PubKeyEd25519{%X}", pubKey[:]) | |||
} | |||
func (pubKey PubKeyEd25519) Equals(other PubKey) bool { | |||
if otherEd, ok := other.(PubKeyEd25519); ok { | |||
return bytes.Equal(pubKey[:], otherEd[:]) | |||
} else { | |||
return false | |||
} | |||
} | |||
//------------------------------------- | |||
var _ PubKey = PubKeySecp256k1{} | |||
// Implements PubKey. | |||
// Compressed pubkey (just the x-cord), | |||
// prefixed with 0x02 or 0x03, depending on the y-cord. | |||
type PubKeySecp256k1 [33]byte | |||
// Implements Bitcoin style addresses: RIPEMD160(SHA256(pubkey)) | |||
func (pubKey PubKeySecp256k1) Address() Address { | |||
hasherSHA256 := sha256.New() | |||
hasherSHA256.Write(pubKey[:]) // does not error | |||
sha := hasherSHA256.Sum(nil) | |||
hasherRIPEMD160 := ripemd160.New() | |||
hasherRIPEMD160.Write(sha) // does not error | |||
return Address(hasherRIPEMD160.Sum(nil)) | |||
} | |||
func (pubKey PubKeySecp256k1) Bytes() []byte { | |||
bz, err := cdc.MarshalBinaryBare(pubKey) | |||
if err != nil { | |||
panic(err) | |||
} | |||
return bz | |||
} | |||
func (pubKey PubKeySecp256k1) VerifyBytes(msg []byte, sig_ Signature) bool { | |||
// and assert same algorithm to sign and verify | |||
sig, ok := sig_.(SignatureSecp256k1) | |||
if !ok { | |||
return false | |||
} | |||
pub__, err := secp256k1.ParsePubKey(pubKey[:], secp256k1.S256()) | |||
if err != nil { | |||
return false | |||
} | |||
sig__, err := secp256k1.ParseDERSignature(sig[:], secp256k1.S256()) | |||
if err != nil { | |||
return false | |||
} | |||
return sig__.Verify(Sha256(msg), pub__) | |||
} | |||
func (pubKey PubKeySecp256k1) String() string { | |||
return fmt.Sprintf("PubKeySecp256k1{%X}", pubKey[:]) | |||
} | |||
func (pubKey PubKeySecp256k1) Equals(other PubKey) bool { | |||
if otherSecp, ok := other.(PubKeySecp256k1); ok { | |||
return bytes.Equal(pubKey[:], otherSecp[:]) | |||
} else { | |||
return false | |||
} | |||
} |
@ -0,0 +1,50 @@ | |||
package crypto | |||
import ( | |||
"encoding/hex" | |||
"testing" | |||
"github.com/btcsuite/btcutil/base58" | |||
"github.com/stretchr/testify/assert" | |||
"github.com/stretchr/testify/require" | |||
) | |||
type keyData struct { | |||
priv string | |||
pub string | |||
addr string | |||
} | |||
var secpDataTable = []keyData{ | |||
{ | |||
priv: "a96e62ed3955e65be32703f12d87b6b5cf26039ecfa948dc5107a495418e5330", | |||
pub: "02950e1cdfcb133d6024109fd489f734eeb4502418e538c28481f22bce276f248c", | |||
addr: "1CKZ9Nx4zgds8tU7nJHotKSDr4a9bYJCa3", | |||
}, | |||
} | |||
func TestPubKeySecp256k1Address(t *testing.T) { | |||
for _, d := range secpDataTable { | |||
privB, _ := hex.DecodeString(d.priv) | |||
pubB, _ := hex.DecodeString(d.pub) | |||
addrBbz, _, _ := base58.CheckDecode(d.addr) | |||
addrB := Address(addrBbz) | |||
var priv PrivKeySecp256k1 | |||
copy(priv[:], privB) | |||
pubKey := priv.PubKey() | |||
pubT, _ := pubKey.(PubKeySecp256k1) | |||
pub := pubT[:] | |||
addr := pubKey.Address() | |||
assert.Equal(t, pub, pubB, "Expected pub keys to match") | |||
assert.Equal(t, addr, addrB, "Expected addresses to match") | |||
} | |||
} | |||
func TestPubKeyInvalidDataProperReturnsEmpty(t *testing.T) { | |||
pk, err := PubKeyFromBytes([]byte("foo")) | |||
require.NotNil(t, err, "expecting a non-nil error") | |||
require.Nil(t, pk, "expecting an empty public key on error") | |||
} |
@ -0,0 +1,108 @@ | |||
package crypto | |||
import ( | |||
"crypto/aes" | |||
"crypto/cipher" | |||
crand "crypto/rand" | |||
"crypto/sha256" | |||
"encoding/hex" | |||
"io" | |||
"sync" | |||
. "github.com/tendermint/tmlibs/common" | |||
) | |||
var gRandInfo *randInfo | |||
func init() { | |||
gRandInfo = &randInfo{} | |||
gRandInfo.MixEntropy(randBytes(32)) // Init | |||
} | |||
// Mix additional bytes of randomness, e.g. from hardware, user-input, etc. | |||
// It is OK to call it multiple times. It does not diminish security. | |||
func MixEntropy(seedBytes []byte) { | |||
gRandInfo.MixEntropy(seedBytes) | |||
} | |||
// This only uses the OS's randomness | |||
func randBytes(numBytes int) []byte { | |||
b := make([]byte, numBytes) | |||
_, err := crand.Read(b) | |||
if err != nil { | |||
PanicCrisis(err) | |||
} | |||
return b | |||
} | |||
// This uses the OS and the Seed(s). | |||
func CRandBytes(numBytes int) []byte { | |||
b := make([]byte, numBytes) | |||
_, err := gRandInfo.Read(b) | |||
if err != nil { | |||
PanicCrisis(err) | |||
} | |||
return b | |||
} | |||
// CRandHex returns a hex encoded string that's floor(numDigits/2) * 2 long. | |||
// | |||
// Note: CRandHex(24) gives 96 bits of randomness that | |||
// are usually strong enough for most purposes. | |||
func CRandHex(numDigits int) string { | |||
return hex.EncodeToString(CRandBytes(numDigits / 2)) | |||
} | |||
// Returns a crand.Reader mixed with user-supplied entropy | |||
func CReader() io.Reader { | |||
return gRandInfo | |||
} | |||
//-------------------------------------------------------------------------------- | |||
type randInfo struct { | |||
mtx sync.Mutex | |||
seedBytes [32]byte | |||
cipherAES256 cipher.Block | |||
streamAES256 cipher.Stream | |||
reader io.Reader | |||
} | |||
// You can call this as many times as you'd like. | |||
// XXX TODO review | |||
func (ri *randInfo) MixEntropy(seedBytes []byte) { | |||
ri.mtx.Lock() | |||
defer ri.mtx.Unlock() | |||
// Make new ri.seedBytes using passed seedBytes and current ri.seedBytes: | |||
// ri.seedBytes = sha256( seedBytes || ri.seedBytes ) | |||
h := sha256.New() | |||
h.Write(seedBytes) | |||
h.Write(ri.seedBytes[:]) | |||
hashBytes := h.Sum(nil) | |||
hashBytes32 := [32]byte{} | |||
copy(hashBytes32[:], hashBytes) | |||
ri.seedBytes = xorBytes32(ri.seedBytes, hashBytes32) | |||
// Create new cipher.Block | |||
var err error | |||
ri.cipherAES256, err = aes.NewCipher(ri.seedBytes[:]) | |||
if err != nil { | |||
PanicSanity("Error creating AES256 cipher: " + err.Error()) | |||
} | |||
// Create new stream | |||
ri.streamAES256 = cipher.NewCTR(ri.cipherAES256, randBytes(aes.BlockSize)) | |||
// Create new reader | |||
ri.reader = &cipher.StreamReader{S: ri.streamAES256, R: crand.Reader} | |||
} | |||
func (ri *randInfo) Read(b []byte) (n int, err error) { | |||
ri.mtx.Lock() | |||
defer ri.mtx.Unlock() | |||
return ri.reader.Read(b) | |||
} | |||
func xorBytes32(bytesA [32]byte, bytesB [32]byte) (res [32]byte) { | |||
for i, b := range bytesA { | |||
res[i] = b ^ bytesB[i] | |||
} | |||
return res | |||
} |
@ -0,0 +1,88 @@ | |||
package crypto | |||
import ( | |||
"fmt" | |||
"crypto/subtle" | |||
. "github.com/tendermint/tmlibs/common" | |||
) | |||
func SignatureFromBytes(pubKeyBytes []byte) (pubKey Signature, err error) { | |||
err = cdc.UnmarshalBinaryBare(pubKeyBytes, &pubKey) | |||
return | |||
} | |||
//---------------------------------------- | |||
type Signature interface { | |||
Bytes() []byte | |||
IsZero() bool | |||
Equals(Signature) bool | |||
} | |||
//------------------------------------- | |||
var _ Signature = SignatureEd25519{} | |||
// Implements Signature | |||
type SignatureEd25519 [64]byte | |||
func (sig SignatureEd25519) Bytes() []byte { | |||
bz, err := cdc.MarshalBinaryBare(sig) | |||
if err != nil { | |||
panic(err) | |||
} | |||
return bz | |||
} | |||
func (sig SignatureEd25519) IsZero() bool { return len(sig) == 0 } | |||
func (sig SignatureEd25519) String() string { return fmt.Sprintf("/%X.../", Fingerprint(sig[:])) } | |||
func (sig SignatureEd25519) Equals(other Signature) bool { | |||
if otherEd, ok := other.(SignatureEd25519); ok { | |||
return subtle.ConstantTimeCompare(sig[:], otherEd[:]) == 1 | |||
} else { | |||
return false | |||
} | |||
} | |||
func SignatureEd25519FromBytes(data []byte) Signature { | |||
var sig SignatureEd25519 | |||
copy(sig[:], data) | |||
return sig | |||
} | |||
//------------------------------------- | |||
var _ Signature = SignatureSecp256k1{} | |||
// Implements Signature | |||
type SignatureSecp256k1 []byte | |||
func (sig SignatureSecp256k1) Bytes() []byte { | |||
bz, err := cdc.MarshalBinaryBare(sig) | |||
if err != nil { | |||
panic(err) | |||
} | |||
return bz | |||
} | |||
func (sig SignatureSecp256k1) IsZero() bool { return len(sig) == 0 } | |||
func (sig SignatureSecp256k1) String() string { return fmt.Sprintf("/%X.../", Fingerprint(sig[:])) } | |||
func (sig SignatureSecp256k1) Equals(other Signature) bool { | |||
if otherSecp, ok := other.(SignatureSecp256k1); ok { | |||
return subtle.ConstantTimeCompare(sig[:], otherSecp[:]) == 1 | |||
} else { | |||
return false | |||
} | |||
} | |||
func SignatureSecp256k1FromBytes(data []byte) Signature { | |||
sig := make(SignatureSecp256k1, len(data)) | |||
copy(sig[:], data) | |||
return sig | |||
} |
@ -0,0 +1,46 @@ | |||
package crypto | |||
import ( | |||
"testing" | |||
"github.com/stretchr/testify/assert" | |||
"github.com/stretchr/testify/require" | |||
) | |||
func TestSignAndValidateEd25519(t *testing.T) { | |||
privKey := GenPrivKeyEd25519() | |||
pubKey := privKey.PubKey() | |||
msg := CRandBytes(128) | |||
sig, err := privKey.Sign(msg) | |||
require.Nil(t, err) | |||
// Test the signature | |||
assert.True(t, pubKey.VerifyBytes(msg, sig)) | |||
// Mutate the signature, just one bit. | |||
sigEd := sig.(SignatureEd25519) | |||
sigEd[7] ^= byte(0x01) | |||
sig = sigEd | |||
assert.False(t, pubKey.VerifyBytes(msg, sig)) | |||
} | |||
func TestSignAndValidateSecp256k1(t *testing.T) { | |||
privKey := GenPrivKeySecp256k1() | |||
pubKey := privKey.PubKey() | |||
msg := CRandBytes(128) | |||
sig, err := privKey.Sign(msg) | |||
require.Nil(t, err) | |||
assert.True(t, pubKey.VerifyBytes(msg, sig)) | |||
// Mutate the signature, just one bit. | |||
sigEd := sig.(SignatureSecp256k1) | |||
sigEd[3] ^= byte(0x01) | |||
sig = sigEd | |||
assert.False(t, pubKey.VerifyBytes(msg, sig)) | |||
} |
@ -0,0 +1,51 @@ | |||
package crypto | |||
import ( | |||
"errors" | |||
. "github.com/tendermint/tmlibs/common" | |||
"golang.org/x/crypto/nacl/secretbox" | |||
) | |||
const nonceLen = 24 | |||
const secretLen = 32 | |||
// secret must be 32 bytes long. Use something like Sha256(Bcrypt(passphrase)) | |||
// The ciphertext is (secretbox.Overhead + 24) bytes longer than the plaintext. | |||
// NOTE: call crypto.MixEntropy() first. | |||
func EncryptSymmetric(plaintext []byte, secret []byte) (ciphertext []byte) { | |||
if len(secret) != secretLen { | |||
PanicSanity(Fmt("Secret must be 32 bytes long, got len %v", len(secret))) | |||
} | |||
nonce := CRandBytes(nonceLen) | |||
nonceArr := [nonceLen]byte{} | |||
copy(nonceArr[:], nonce) | |||
secretArr := [secretLen]byte{} | |||
copy(secretArr[:], secret) | |||
ciphertext = make([]byte, nonceLen+secretbox.Overhead+len(plaintext)) | |||
copy(ciphertext, nonce) | |||
secretbox.Seal(ciphertext[nonceLen:nonceLen], plaintext, &nonceArr, &secretArr) | |||
return ciphertext | |||
} | |||
// secret must be 32 bytes long. Use something like Sha256(Bcrypt(passphrase)) | |||
// The ciphertext is (secretbox.Overhead + 24) bytes longer than the plaintext. | |||
func DecryptSymmetric(ciphertext []byte, secret []byte) (plaintext []byte, err error) { | |||
if len(secret) != secretLen { | |||
PanicSanity(Fmt("Secret must be 32 bytes long, got len %v", len(secret))) | |||
} | |||
if len(ciphertext) <= secretbox.Overhead+nonceLen { | |||
return nil, errors.New("Ciphertext is too short") | |||
} | |||
nonce := ciphertext[:nonceLen] | |||
nonceArr := [nonceLen]byte{} | |||
copy(nonceArr[:], nonce) | |||
secretArr := [secretLen]byte{} | |||
copy(secretArr[:], secret) | |||
plaintext = make([]byte, len(ciphertext)-nonceLen-secretbox.Overhead) | |||
_, ok := secretbox.Open(plaintext[:0], ciphertext[nonceLen:], &nonceArr, &secretArr) | |||
if !ok { | |||
return nil, errors.New("Ciphertext decryption failed") | |||
} | |||
return plaintext, nil | |||
} |
@ -0,0 +1,42 @@ | |||
package crypto | |||
import ( | |||
"testing" | |||
"github.com/stretchr/testify/assert" | |||
"github.com/stretchr/testify/require" | |||
"golang.org/x/crypto/bcrypt" | |||
) | |||
func TestSimple(t *testing.T) { | |||
MixEntropy([]byte("someentropy")) | |||
plaintext := []byte("sometext") | |||
secret := []byte("somesecretoflengththirtytwo===32") | |||
ciphertext := EncryptSymmetric(plaintext, secret) | |||
plaintext2, err := DecryptSymmetric(ciphertext, secret) | |||
require.Nil(t, err, "%+v", err) | |||
assert.Equal(t, plaintext, plaintext2) | |||
} | |||
func TestSimpleWithKDF(t *testing.T) { | |||
MixEntropy([]byte("someentropy")) | |||
plaintext := []byte("sometext") | |||
secretPass := []byte("somesecret") | |||
secret, err := bcrypt.GenerateFromPassword(secretPass, 12) | |||
if err != nil { | |||
t.Error(err) | |||
} | |||
secret = Sha256(secret) | |||
ciphertext := EncryptSymmetric(plaintext, secret) | |||
plaintext2, err := DecryptSymmetric(ciphertext, secret) | |||
require.Nil(t, err, "%+v", err) | |||
assert.Equal(t, plaintext, plaintext2) | |||
} |
@ -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) | |||
} |
@ -0,0 +1,3 @@ | |||
package crypto | |||
const Version = "0.9.0-dev" |
@ -0,0 +1,48 @@ | |||
/* | |||
go-crypto is a customized/convenience cryptography package | |||
for supporting Tendermint. | |||
It wraps select functionality of equivalent functions in the | |||
Go standard library, for easy usage with our libraries. | |||
Keys: | |||
All key generation functions return an instance of the PrivKey interface | |||
which implements methods | |||
AssertIsPrivKeyInner() | |||
Bytes() []byte | |||
Sign(msg []byte) Signature | |||
PubKey() PubKey | |||
Equals(PrivKey) bool | |||
Wrap() PrivKey | |||
From the above method we can: | |||
a) Retrieve the public key if needed | |||
pubKey := key.PubKey() | |||
For example: | |||
privKey, err := crypto.GenPrivKeyEd25519() | |||
if err != nil { | |||
... | |||
} | |||
pubKey := privKey.PubKey() | |||
... | |||
// And then you can use the private and public key | |||
doSomething(privKey, pubKey) | |||
We also provide hashing wrappers around algorithms: | |||
Sha256 | |||
sum := crypto.Sha256([]byte("This is Tendermint")) | |||
fmt.Printf("%x\n", sum) | |||
Ripemd160 | |||
sum := crypto.Ripemd160([]byte("This is consensus")) | |||
fmt.Printf("%x\n", sum) | |||
*/ | |||
package crypto | |||
// TODO: Add more docs in here |
@ -0,0 +1,119 @@ | |||
package crypto | |||
import ( | |||
"os" | |||
"testing" | |||
"github.com/stretchr/testify/assert" | |||
"github.com/stretchr/testify/require" | |||
) | |||
type byter interface { | |||
Bytes() []byte | |||
} | |||
func checkAminoBinary(t *testing.T, src byter, dst interface{}, size int) { | |||
// Marshal to binary bytes. | |||
bz, err := cdc.MarshalBinaryBare(src) | |||
require.Nil(t, err, "%+v", err) | |||
// Make sure this is compatible with current (Bytes()) encoding. | |||
assert.Equal(t, src.Bytes(), bz, "Amino binary vs Bytes() mismatch") | |||
// Make sure we have the expected length. | |||
if size != -1 { | |||
assert.Equal(t, size, len(bz), "Amino binary size mismatch") | |||
} | |||
// Unmarshal. | |||
err = cdc.UnmarshalBinaryBare(bz, dst) | |||
require.Nil(t, err, "%+v", err) | |||
} | |||
func checkAminoJSON(t *testing.T, src interface{}, dst interface{}, isNil bool) { | |||
// Marshal to JSON bytes. | |||
js, err := cdc.MarshalJSON(src) | |||
require.Nil(t, err, "%+v", err) | |||
if isNil { | |||
assert.Equal(t, string(js), `null`) | |||
} else { | |||
assert.Contains(t, string(js), `"type":`) | |||
assert.Contains(t, string(js), `"value":`) | |||
} | |||
// Unmarshal. | |||
err = cdc.UnmarshalJSON(js, dst) | |||
require.Nil(t, err, "%+v", err) | |||
} | |||
func ExamplePrintRegisteredTypes() { | |||
cdc.PrintTypes(os.Stdout) | |||
// Output: | Type | Name | Prefix | Length | Notes | | |||
//| ---- | ---- | ------ | ----- | ------ | | |||
//| PubKeyEd25519 | tendermint/PubKeyEd25519 | 0x1624DE64 | 0x20 | | | |||
//| PubKeySecp256k1 | tendermint/PubKeySecp256k1 | 0xEB5AE987 | 0x21 | | | |||
//| PrivKeyEd25519 | tendermint/PrivKeyEd25519 | 0xA3288910 | 0x40 | | | |||
//| PrivKeySecp256k1 | tendermint/PrivKeySecp256k1 | 0xE1B0F79B | 0x20 | | | |||
//| SignatureEd25519 | tendermint/SignatureEd25519 | 0x2031EA53 | 0x40 | | | |||
//| SignatureSecp256k1 | tendermint/SignatureSecp256k1 | 0x7FC4A495 | variable | | | |||
} | |||
func TestKeyEncodings(t *testing.T) { | |||
cases := []struct { | |||
privKey PrivKey | |||
privSize, pubSize int // binary sizes | |||
}{ | |||
{ | |||
privKey: GenPrivKeyEd25519(), | |||
privSize: 69, | |||
pubSize: 37, | |||
}, | |||
{ | |||
privKey: GenPrivKeySecp256k1(), | |||
privSize: 37, | |||
pubSize: 38, | |||
}, | |||
} | |||
for _, tc := range cases { | |||
// Check (de/en)codings of PrivKeys. | |||
var priv2, priv3 PrivKey | |||
checkAminoBinary(t, tc.privKey, &priv2, tc.privSize) | |||
assert.EqualValues(t, tc.privKey, priv2) | |||
checkAminoJSON(t, tc.privKey, &priv3, false) // TODO also check Prefix bytes. | |||
assert.EqualValues(t, tc.privKey, priv3) | |||
// Check (de/en)codings of Signatures. | |||
var sig1, sig2, sig3 Signature | |||
sig1, err := tc.privKey.Sign([]byte("something")) | |||
assert.NoError(t, err) | |||
checkAminoBinary(t, sig1, &sig2, -1) // Siganture size changes for Secp anyways. | |||
assert.EqualValues(t, sig1, sig2) | |||
checkAminoJSON(t, sig1, &sig3, false) // TODO also check Prefix bytes. | |||
assert.EqualValues(t, sig1, sig3) | |||
// Check (de/en)codings of PubKeys. | |||
pubKey := tc.privKey.PubKey() | |||
var pub2, pub3 PubKey | |||
checkAminoBinary(t, pubKey, &pub2, tc.pubSize) | |||
assert.EqualValues(t, pubKey, pub2) | |||
checkAminoJSON(t, pubKey, &pub3, false) // TODO also check Prefix bytes. | |||
assert.EqualValues(t, pubKey, pub3) | |||
} | |||
} | |||
func TestNilEncodings(t *testing.T) { | |||
// Check nil Signature. | |||
var a, b Signature | |||
checkAminoJSON(t, &a, &b, true) | |||
assert.EqualValues(t, a, b) | |||
// Check nil PubKey. | |||
var c, d PubKey | |||
checkAminoJSON(t, &c, &d, true) | |||
assert.EqualValues(t, c, d) | |||
// Check nil PrivKey. | |||
var e, f PrivKey | |||
checkAminoJSON(t, &e, &f, true) | |||
assert.EqualValues(t, e, f) | |||
} |
@ -0,0 +1,35 @@ | |||
// Copyright 2017 Tendermint. All Rights Reserved. | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
package crypto_test | |||
import ( | |||
"fmt" | |||
"github.com/tendermint/go-crypto" | |||
) | |||
func ExampleSha256() { | |||
sum := crypto.Sha256([]byte("This is Tendermint")) | |||
fmt.Printf("%x\n", sum) | |||
// Output: | |||
// f91afb642f3d1c87c17eb01aae5cb65c242dfdbe7cf1066cc260f4ce5d33b94e | |||
} | |||
func ExampleRipemd160() { | |||
sum := crypto.Ripemd160([]byte("This is Tendermint")) | |||
fmt.Printf("%x\n", sum) | |||
// Output: | |||
// 051e22663e8f0fd2f2302f1210f954adff009005 | |||
} |
@ -0,0 +1,18 @@ | |||
package crypto | |||
import ( | |||
"crypto/sha256" | |||
"golang.org/x/crypto/ripemd160" | |||
) | |||
func Sha256(bytes []byte) []byte { | |||
hasher := sha256.New() | |||
hasher.Write(bytes) | |||
return hasher.Sum(nil) | |||
} | |||
func Ripemd160(bytes []byte) []byte { | |||
hasher := ripemd160.New() | |||
hasher.Write(bytes) | |||
return hasher.Sum(nil) | |||
} |
@ -0,0 +1,105 @@ | |||
// Package hkdfchacha20poly1305 creates an AEAD using hkdf, chacha20, and poly1305 | |||
// When sealing and opening, the hkdf is used to obtain the nonce and subkey for | |||
// chacha20. Other than the change for the how the subkey and nonce for chacha | |||
// are obtained, this is the same as chacha20poly1305 | |||
package hkdfchacha20poly1305 | |||
import ( | |||
"crypto/cipher" | |||
"crypto/sha256" | |||
"errors" | |||
"io" | |||
"golang.org/x/crypto/chacha20poly1305" | |||
"golang.org/x/crypto/hkdf" | |||
) | |||
type hkdfchacha20poly1305 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 | |||
// TagSize is the size added from poly1305 | |||
TagSize = 16 | |||
// MaxPlaintextSize is the max size that can be passed into a single call of Seal | |||
MaxPlaintextSize = (1 << 38) - 64 | |||
// MaxCiphertextSize is the max size that can be passed into a single call of Open, | |||
// this differs from plaintext size due to the tag | |||
MaxCiphertextSize = (1 << 38) - 48 | |||
// HkdfInfo is the parameter used internally for Hkdf's info parameter. | |||
HkdfInfo = "TENDERMINT_SECRET_CONNECTION_FRAME_KEY_DERIVE" | |||
) | |||
//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(hkdfchacha20poly1305) | |||
copy(ret.key[:], key) | |||
return ret, nil | |||
} | |||
func (c *hkdfchacha20poly1305) NonceSize() int { | |||
return NonceSize | |||
} | |||
func (c *hkdfchacha20poly1305) Overhead() int { | |||
return TagSize | |||
} | |||
func (c *hkdfchacha20poly1305) Seal(dst, nonce, plaintext, additionalData []byte) []byte { | |||
if len(nonce) != NonceSize { | |||
panic("hkdfchacha20poly1305: bad nonce length passed to Seal") | |||
} | |||
if uint64(len(plaintext)) > MaxPlaintextSize { | |||
panic("hkdfchacha20poly1305: plaintext too large") | |||
} | |||
subKey, chachaNonce := getSubkeyAndChachaNonceFromHkdf(&c.key, &nonce) | |||
aead, err := chacha20poly1305.New(subKey[:]) | |||
if err != nil { | |||
panic("hkdfchacha20poly1305: failed to initialize chacha20poly1305") | |||
} | |||
return aead.Seal(dst, chachaNonce[:], plaintext, additionalData) | |||
} | |||
func (c *hkdfchacha20poly1305) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) { | |||
if len(nonce) != NonceSize { | |||
return nil, errors.New("hkdfchacha20poly1305: bad nonce length passed to Open") | |||
} | |||
if uint64(len(ciphertext)) > MaxCiphertextSize { | |||
return nil, errors.New("hkdfchacha20poly1305: ciphertext too large") | |||
} | |||
subKey, chachaNonce := getSubkeyAndChachaNonceFromHkdf(&c.key, &nonce) | |||
aead, err := chacha20poly1305.New(subKey[:]) | |||
if err != nil { | |||
panic("hkdfchacha20poly1305: failed to initialize chacha20poly1305") | |||
} | |||
return aead.Open(dst, chachaNonce[:], ciphertext, additionalData) | |||
} | |||
func getSubkeyAndChachaNonceFromHkdf(cKey *[32]byte, nonce *[]byte) ( | |||
subKey [KeySize]byte, chachaNonce [chacha20poly1305.NonceSize]byte) { | |||
hash := sha256.New | |||
hkdf := hkdf.New(hash, (*cKey)[:], *nonce, []byte(HkdfInfo)) | |||
_, err := io.ReadFull(hkdf, subKey[:]) | |||
if err != nil { | |||
panic("hkdfchacha20poly1305: failed to read subkey from hkdf") | |||
} | |||
_, err = io.ReadFull(hkdf, chachaNonce[:]) | |||
if err != nil { | |||
panic("hkdfchacha20poly1305: failed to read chachaNonce from hkdf") | |||
} | |||
return | |||
} |
@ -0,0 +1,139 @@ | |||
package hkdfchacha20poly1305 | |||
import ( | |||
"bytes" | |||
cr "crypto/rand" | |||
"encoding/hex" | |||
mr "math/rand" | |||
"testing" | |||
"github.com/stretchr/testify/assert" | |||
) | |||
// Test that a test vector we generated is valid. (Ensures backwards | |||
// compatability) | |||
func TestVector(t *testing.T) { | |||
key, _ := hex.DecodeString("56f8de45d3c294c7675bcaf457bdd4b71c380b9b2408ce9412b348d0f08b69ee") | |||
aead, err := New(key[:]) | |||
if err != nil { | |||
t.Fatal(err) | |||
} | |||
cts := []string{"e20a8bf42c535ac30125cfc52031577f0b", | |||
"657695b37ba30f67b25860d90a6f1d00d8", | |||
"e9aa6f3b7f625d957fd50f05bcdf20d014", | |||
"8a00b3b5a6014e0d2033bebc5935086245", | |||
"aadd74867b923879e6866ea9e03c009039", | |||
"fc59773c2c864ee3b4cc971876b3c7bed4", | |||
"caec14e3a9a52ce1a2682c6737defa4752", | |||
"0b89511ffe490d2049d6950494ee51f919", | |||
"7de854ea71f43ca35167a07566c769083d", | |||
"cd477327f4ea4765c71e311c5fec1edbfb"} | |||
for i := 0; i < 10; i++ { | |||
ct, _ := hex.DecodeString(cts[i]) | |||
byteArr := []byte{byte(i)} | |||
nonce := make([]byte, 24, 24) | |||
nonce[0] = byteArr[0] | |||
plaintext, err := aead.Open(nil, nonce, ct, byteArr) | |||
if err != nil { | |||
t.Errorf("%dth Open failed", i) | |||
continue | |||
} | |||
assert.Equal(t, byteArr, plaintext) | |||
} | |||
} | |||
// The following test is taken from | |||
// https://github.com/golang/crypto/blob/master/chacha20poly1305/chacha20poly1305_test.go#L69 | |||
// It requires the below copyright notice, where "this source code" refers to the following function. | |||
// Copyright 2016 The Go Authors. All rights reserved. | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found at the bottom of this file. | |||
func TestRandom(t *testing.T) { | |||
// Some random tests to verify Open(Seal) == Plaintext | |||
for i := 0; i < 256; i++ { | |||
var nonce [24]byte | |||
var key [32]byte | |||
al := mr.Intn(128) | |||
pl := mr.Intn(16384) | |||
ad := make([]byte, al) | |||
plaintext := make([]byte, pl) | |||
cr.Read(key[:]) | |||
cr.Read(nonce[:]) | |||
cr.Read(ad) | |||
cr.Read(plaintext) | |||
aead, err := New(key[:]) | |||
if err != nil { | |||
t.Fatal(err) | |||
} | |||
ct := aead.Seal(nil, nonce[:], plaintext, ad) | |||
plaintext2, err := aead.Open(nil, nonce[:], ct, ad) | |||
if err != nil { | |||
t.Errorf("Random #%d: Open failed", i) | |||
continue | |||
} | |||
if !bytes.Equal(plaintext, plaintext2) { | |||
t.Errorf("Random #%d: plaintext's don't match: got %x vs %x", i, plaintext2, plaintext) | |||
continue | |||
} | |||
if len(ad) > 0 { | |||
alterAdIdx := mr.Intn(len(ad)) | |||
ad[alterAdIdx] ^= 0x80 | |||
if _, err := aead.Open(nil, nonce[:], ct, ad); err == nil { | |||
t.Errorf("Random #%d: Open was successful after altering additional data", i) | |||
} | |||
ad[alterAdIdx] ^= 0x80 | |||
} | |||
alterNonceIdx := mr.Intn(aead.NonceSize()) | |||
nonce[alterNonceIdx] ^= 0x80 | |||
if _, err := aead.Open(nil, nonce[:], ct, ad); err == nil { | |||
t.Errorf("Random #%d: Open was successful after altering nonce", i) | |||
} | |||
nonce[alterNonceIdx] ^= 0x80 | |||
alterCtIdx := mr.Intn(len(ct)) | |||
ct[alterCtIdx] ^= 0x80 | |||
if _, err := aead.Open(nil, nonce[:], ct, ad); err == nil { | |||
t.Errorf("Random #%d: Open was successful after altering ciphertext", i) | |||
} | |||
ct[alterCtIdx] ^= 0x80 | |||
} | |||
} | |||
// AFOREMENTIONED LICENCE | |||
// Copyright (c) 2009 The Go Authors. All rights reserved. | |||
// | |||
// Redistribution and use in source and binary forms, with or without | |||
// modification, are permitted provided that the following conditions are | |||
// met: | |||
// | |||
// * Redistributions of source code must retain the above copyright | |||
// notice, this list of conditions and the following disclaimer. | |||
// * Redistributions in binary form must reproduce the above | |||
// copyright notice, this list of conditions and the following disclaimer | |||
// in the documentation and/or other materials provided with the | |||
// distribution. | |||
// * Neither the name of Google Inc. nor the names of its | |||
// contributors may be used to endorse or promote products derived from | |||
// this software without specific prior written permission. | |||
// | |||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
@ -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,88 @@ | |||
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 | |||
// The value is hashed, 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: []byte(key), | |||
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, "fa9bc106ffd932d919bee935ceb6cf2b3dd72d8f", fmt.Sprintf("%x", db.Hash()), "Hash didn't match") | |||
} | |||
{ | |||
db := newSimpleMap() | |||
db.Set("key1", strHasher("value2")) | |||
assert.Equal(t, "e00e7dcfe54e9fafef5111e813a587f01ba9c3e8", fmt.Sprintf("%x", db.Hash()), "Hash didn't match") | |||
} | |||
{ | |||
db := newSimpleMap() | |||
db.Set("key1", strHasher("value1")) | |||
db.Set("key2", strHasher("value2")) | |||
assert.Equal(t, "eff12d1c703a1022ab509287c0f196130123d786", 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, "eff12d1c703a1022ab509287c0f196130123d786", 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, "b2c62a277c08dbd2ad73ca53cd1d6bfdf5830d26", 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, "b2c62a277c08dbd2ad73ca53cd1d6bfdf5830d26", fmt.Sprintf("%x", db.Hash()), "Hash didn't match") | |||
} | |||
} |
@ -0,0 +1,160 @@ | |||
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 map[string]*SimpleProof, keys []string) { | |||
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)) | |||
} | |||
rootHash, proofList := SimpleProofsFromHashers(kvsH) | |||
proofs = make(map[string]*SimpleProof) | |||
keys = make([]string, len(proofList)) | |||
for i, kvp := range kvs { | |||
proofs[string(kvp.Key)] = proofList[i] | |||
keys[i] = string(kvp.Key) | |||
} | |||
return | |||
} | |||
// 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,164 @@ | |||
package crypto | |||
import ( | |||
"crypto/subtle" | |||
secp256k1 "github.com/btcsuite/btcd/btcec" | |||
"github.com/tendermint/ed25519" | |||
"github.com/tendermint/ed25519/extra25519" | |||
) | |||
func PrivKeyFromBytes(privKeyBytes []byte) (privKey PrivKey, err error) { | |||
err = cdc.UnmarshalBinaryBare(privKeyBytes, &privKey) | |||
return | |||
} | |||
//---------------------------------------- | |||
type PrivKey interface { | |||
Bytes() []byte | |||
Sign(msg []byte) (Signature, error) | |||
PubKey() PubKey | |||
Equals(PrivKey) bool | |||
} | |||
//------------------------------------- | |||
var _ PrivKey = PrivKeyEd25519{} | |||
// Implements PrivKey | |||
type PrivKeyEd25519 [64]byte | |||
func (privKey PrivKeyEd25519) Bytes() []byte { | |||
return cdc.MustMarshalBinaryBare(privKey) | |||
} | |||
func (privKey PrivKeyEd25519) Sign(msg []byte) (Signature, error) { | |||
privKeyBytes := [64]byte(privKey) | |||
signatureBytes := ed25519.Sign(&privKeyBytes, msg) | |||
return SignatureEd25519(*signatureBytes), nil | |||
} | |||
func (privKey PrivKeyEd25519) PubKey() PubKey { | |||
privKeyBytes := [64]byte(privKey) | |||
pubBytes := *ed25519.MakePublicKey(&privKeyBytes) | |||
return PubKeyEd25519(pubBytes) | |||
} | |||
// Equals - you probably don't need to use this. | |||
// Runs in constant time based on length of the keys. | |||
func (privKey PrivKeyEd25519) Equals(other PrivKey) bool { | |||
if otherEd, ok := other.(PrivKeyEd25519); ok { | |||
return subtle.ConstantTimeCompare(privKey[:], otherEd[:]) == 1 | |||
} else { | |||
return false | |||
} | |||
} | |||
func (privKey PrivKeyEd25519) ToCurve25519() *[32]byte { | |||
keyCurve25519 := new([32]byte) | |||
privKeyBytes := [64]byte(privKey) | |||
extra25519.PrivateKeyToCurve25519(keyCurve25519, &privKeyBytes) | |||
return keyCurve25519 | |||
} | |||
// Deterministically generates new priv-key bytes from key. | |||
func (privKey PrivKeyEd25519) Generate(index int) PrivKeyEd25519 { | |||
bz, err := cdc.MarshalBinaryBare(struct { | |||
PrivKey [64]byte | |||
Index int | |||
}{privKey, index}) | |||
if err != nil { | |||
panic(err) | |||
} | |||
newBytes := Sha256(bz) | |||
newKey := new([64]byte) | |||
copy(newKey[:32], newBytes) | |||
ed25519.MakePublicKey(newKey) | |||
return PrivKeyEd25519(*newKey) | |||
} | |||
func GenPrivKeyEd25519() PrivKeyEd25519 { | |||
privKeyBytes := new([64]byte) | |||
copy(privKeyBytes[:32], CRandBytes(32)) | |||
ed25519.MakePublicKey(privKeyBytes) | |||
return PrivKeyEd25519(*privKeyBytes) | |||
} | |||
// NOTE: secret should be the output of a KDF like bcrypt, | |||
// if it's derived from user input. | |||
func GenPrivKeyEd25519FromSecret(secret []byte) PrivKeyEd25519 { | |||
privKey32 := Sha256(secret) // Not Ripemd160 because we want 32 bytes. | |||
privKeyBytes := new([64]byte) | |||
copy(privKeyBytes[:32], privKey32) | |||
ed25519.MakePublicKey(privKeyBytes) | |||
return PrivKeyEd25519(*privKeyBytes) | |||
} | |||
//------------------------------------- | |||
var _ PrivKey = PrivKeySecp256k1{} | |||
// Implements PrivKey | |||
type PrivKeySecp256k1 [32]byte | |||
func (privKey PrivKeySecp256k1) Bytes() []byte { | |||
return cdc.MustMarshalBinaryBare(privKey) | |||
} | |||
func (privKey PrivKeySecp256k1) Sign(msg []byte) (Signature, error) { | |||
priv__, _ := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKey[:]) | |||
sig__, err := priv__.Sign(Sha256(msg)) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return SignatureSecp256k1(sig__.Serialize()), nil | |||
} | |||
func (privKey PrivKeySecp256k1) PubKey() PubKey { | |||
_, pub__ := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKey[:]) | |||
var pub PubKeySecp256k1 | |||
copy(pub[:], pub__.SerializeCompressed()) | |||
return pub | |||
} | |||
// Equals - you probably don't need to use this. | |||
// Runs in constant time based on length of the keys. | |||
func (privKey PrivKeySecp256k1) Equals(other PrivKey) bool { | |||
if otherSecp, ok := other.(PrivKeySecp256k1); ok { | |||
return subtle.ConstantTimeCompare(privKey[:], otherSecp[:]) == 1 | |||
} else { | |||
return false | |||
} | |||
} | |||
/* | |||
// Deterministically generates new priv-key bytes from key. | |||
func (key PrivKeySecp256k1) Generate(index int) PrivKeySecp256k1 { | |||
newBytes := cdc.BinarySha256(struct { | |||
PrivKey [64]byte | |||
Index int | |||
}{key, index}) | |||
var newKey [64]byte | |||
copy(newKey[:], newBytes) | |||
return PrivKeySecp256k1(newKey) | |||
} | |||
*/ | |||
func GenPrivKeySecp256k1() PrivKeySecp256k1 { | |||
privKeyBytes := [32]byte{} | |||
copy(privKeyBytes[:], CRandBytes(32)) | |||
priv, _ := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKeyBytes[:]) | |||
copy(privKeyBytes[:], priv.Serialize()) | |||
return PrivKeySecp256k1(privKeyBytes) | |||
} | |||
// NOTE: secret should be the output of a KDF like bcrypt, | |||
// if it's derived from user input. | |||
func GenPrivKeySecp256k1FromSecret(secret []byte) PrivKeySecp256k1 { | |||
privKey32 := Sha256(secret) // Not Ripemd160 because we want 32 bytes. | |||
priv, _ := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKey32) | |||
privKeyBytes := [32]byte{} | |||
copy(privKeyBytes[:], priv.Serialize()) | |||
return PrivKeySecp256k1(privKeyBytes) | |||
} |
@ -0,0 +1,60 @@ | |||
package crypto_test | |||
import ( | |||
"testing" | |||
"github.com/stretchr/testify/assert" | |||
crypto "github.com/tendermint/go-crypto" | |||
) | |||
func TestGeneratePrivKey(t *testing.T) { | |||
testPriv := crypto.GenPrivKeyEd25519() | |||
testGenerate := testPriv.Generate(1) | |||
signBytes := []byte("something to sign") | |||
pub := testGenerate.PubKey() | |||
sig, err := testGenerate.Sign(signBytes) | |||
assert.NoError(t, err) | |||
assert.True(t, pub.VerifyBytes(signBytes, sig)) | |||
} | |||
/* | |||
type BadKey struct { | |||
PrivKeyEd25519 | |||
} | |||
func TestReadPrivKey(t *testing.T) { | |||
assert, require := assert.New(t), require.New(t) | |||
// garbage in, garbage out | |||
garbage := []byte("hjgewugfbiewgofwgewr") | |||
XXX This test wants to register BadKey globally to go-crypto, | |||
but we don't want to support that. | |||
_, err := PrivKeyFromBytes(garbage) | |||
require.Error(err) | |||
edKey := GenPrivKeyEd25519() | |||
badKey := BadKey{edKey} | |||
cases := []struct { | |||
key PrivKey | |||
valid bool | |||
}{ | |||
{edKey, true}, | |||
{badKey, false}, | |||
} | |||
for i, tc := range cases { | |||
data := tc.key.Bytes() | |||
fmt.Println(">>>", data) | |||
key, err := PrivKeyFromBytes(data) | |||
fmt.Printf("!!! %#v\n", key, err) | |||
if tc.valid { | |||
assert.NoError(err, "%d", i) | |||
assert.Equal(tc.key, key, "%d", i) | |||
} else { | |||
assert.Error(err, "%d: %#v", i, key) | |||
} | |||
} | |||
} | |||
*/ |
@ -0,0 +1,149 @@ | |||
package crypto | |||
import ( | |||
"bytes" | |||
"crypto/sha256" | |||
"fmt" | |||
"golang.org/x/crypto/ripemd160" | |||
secp256k1 "github.com/btcsuite/btcd/btcec" | |||
"github.com/tendermint/ed25519" | |||
"github.com/tendermint/ed25519/extra25519" | |||
cmn "github.com/tendermint/tmlibs/common" | |||
"github.com/tendermint/go-crypto/tmhash" | |||
) | |||
// An address is a []byte, but hex-encoded even in JSON. | |||
// []byte leaves us the option to change the address length. | |||
// Use an alias so Unmarshal methods (with ptr receivers) are available too. | |||
type Address = cmn.HexBytes | |||
func PubKeyFromBytes(pubKeyBytes []byte) (pubKey PubKey, err error) { | |||
err = cdc.UnmarshalBinaryBare(pubKeyBytes, &pubKey) | |||
return | |||
} | |||
//---------------------------------------- | |||
type PubKey interface { | |||
Address() Address | |||
Bytes() []byte | |||
VerifyBytes(msg []byte, sig Signature) bool | |||
Equals(PubKey) bool | |||
} | |||
//------------------------------------- | |||
var _ PubKey = PubKeyEd25519{} | |||
// Implements PubKeyInner | |||
type PubKeyEd25519 [32]byte | |||
// Address is the SHA256-20 of the raw pubkey bytes. | |||
func (pubKey PubKeyEd25519) Address() Address { | |||
return Address(tmhash.Sum(pubKey[:])) | |||
} | |||
func (pubKey PubKeyEd25519) Bytes() []byte { | |||
bz, err := cdc.MarshalBinaryBare(pubKey) | |||
if err != nil { | |||
panic(err) | |||
} | |||
return bz | |||
} | |||
func (pubKey PubKeyEd25519) VerifyBytes(msg []byte, sig_ Signature) bool { | |||
// make sure we use the same algorithm to sign | |||
sig, ok := sig_.(SignatureEd25519) | |||
if !ok { | |||
return false | |||
} | |||
pubKeyBytes := [32]byte(pubKey) | |||
sigBytes := [64]byte(sig) | |||
return ed25519.Verify(&pubKeyBytes, msg, &sigBytes) | |||
} | |||
// For use with golang/crypto/nacl/box | |||
// If error, returns nil. | |||
func (pubKey PubKeyEd25519) ToCurve25519() *[32]byte { | |||
keyCurve25519, pubKeyBytes := new([32]byte), [32]byte(pubKey) | |||
ok := extra25519.PublicKeyToCurve25519(keyCurve25519, &pubKeyBytes) | |||
if !ok { | |||
return nil | |||
} | |||
return keyCurve25519 | |||
} | |||
func (pubKey PubKeyEd25519) String() string { | |||
return fmt.Sprintf("PubKeyEd25519{%X}", pubKey[:]) | |||
} | |||
func (pubKey PubKeyEd25519) Equals(other PubKey) bool { | |||
if otherEd, ok := other.(PubKeyEd25519); ok { | |||
return bytes.Equal(pubKey[:], otherEd[:]) | |||
} else { | |||
return false | |||
} | |||
} | |||
//------------------------------------- | |||
var _ PubKey = PubKeySecp256k1{} | |||
// Implements PubKey. | |||
// Compressed pubkey (just the x-cord), | |||
// prefixed with 0x02 or 0x03, depending on the y-cord. | |||
type PubKeySecp256k1 [33]byte | |||
// Implements Bitcoin style addresses: RIPEMD160(SHA256(pubkey)) | |||
func (pubKey PubKeySecp256k1) Address() Address { | |||
hasherSHA256 := sha256.New() | |||
hasherSHA256.Write(pubKey[:]) // does not error | |||
sha := hasherSHA256.Sum(nil) | |||
hasherRIPEMD160 := ripemd160.New() | |||
hasherRIPEMD160.Write(sha) // does not error | |||
return Address(hasherRIPEMD160.Sum(nil)) | |||
} | |||
func (pubKey PubKeySecp256k1) Bytes() []byte { | |||
bz, err := cdc.MarshalBinaryBare(pubKey) | |||
if err != nil { | |||
panic(err) | |||
} | |||
return bz | |||
} | |||
func (pubKey PubKeySecp256k1) VerifyBytes(msg []byte, sig_ Signature) bool { | |||
// and assert same algorithm to sign and verify | |||
sig, ok := sig_.(SignatureSecp256k1) | |||
if !ok { | |||
return false | |||
} | |||
pub__, err := secp256k1.ParsePubKey(pubKey[:], secp256k1.S256()) | |||
if err != nil { | |||
return false | |||
} | |||
sig__, err := secp256k1.ParseDERSignature(sig[:], secp256k1.S256()) | |||
if err != nil { | |||
return false | |||
} | |||
return sig__.Verify(Sha256(msg), pub__) | |||
} | |||
func (pubKey PubKeySecp256k1) String() string { | |||
return fmt.Sprintf("PubKeySecp256k1{%X}", pubKey[:]) | |||
} | |||
func (pubKey PubKeySecp256k1) Equals(other PubKey) bool { | |||
if otherSecp, ok := other.(PubKeySecp256k1); ok { | |||
return bytes.Equal(pubKey[:], otherSecp[:]) | |||
} else { | |||
return false | |||
} | |||
} |
@ -0,0 +1,50 @@ | |||
package crypto | |||
import ( | |||
"encoding/hex" | |||
"testing" | |||
"github.com/btcsuite/btcutil/base58" | |||
"github.com/stretchr/testify/assert" | |||
"github.com/stretchr/testify/require" | |||
) | |||
type keyData struct { | |||
priv string | |||
pub string | |||
addr string | |||
} | |||
var secpDataTable = []keyData{ | |||
{ | |||
priv: "a96e62ed3955e65be32703f12d87b6b5cf26039ecfa948dc5107a495418e5330", | |||
pub: "02950e1cdfcb133d6024109fd489f734eeb4502418e538c28481f22bce276f248c", | |||
addr: "1CKZ9Nx4zgds8tU7nJHotKSDr4a9bYJCa3", | |||
}, | |||
} | |||
func TestPubKeySecp256k1Address(t *testing.T) { | |||
for _, d := range secpDataTable { | |||
privB, _ := hex.DecodeString(d.priv) | |||
pubB, _ := hex.DecodeString(d.pub) | |||
addrBbz, _, _ := base58.CheckDecode(d.addr) | |||
addrB := Address(addrBbz) | |||
var priv PrivKeySecp256k1 | |||
copy(priv[:], privB) | |||
pubKey := priv.PubKey() | |||
pubT, _ := pubKey.(PubKeySecp256k1) | |||
pub := pubT[:] | |||
addr := pubKey.Address() | |||
assert.Equal(t, pub, pubB, "Expected pub keys to match") | |||
assert.Equal(t, addr, addrB, "Expected addresses to match") | |||
} | |||
} | |||
func TestPubKeyInvalidDataProperReturnsEmpty(t *testing.T) { | |||
pk, err := PubKeyFromBytes([]byte("foo")) | |||
require.NotNil(t, err, "expecting a non-nil error") | |||
require.Nil(t, pk, "expecting an empty public key on error") | |||
} |
@ -0,0 +1,108 @@ | |||
package crypto | |||
import ( | |||
"crypto/aes" | |||
"crypto/cipher" | |||
crand "crypto/rand" | |||
"crypto/sha256" | |||
"encoding/hex" | |||
"io" | |||
"sync" | |||
. "github.com/tendermint/tmlibs/common" | |||
) | |||
var gRandInfo *randInfo | |||
func init() { | |||
gRandInfo = &randInfo{} | |||
gRandInfo.MixEntropy(randBytes(32)) // Init | |||
} | |||
// Mix additional bytes of randomness, e.g. from hardware, user-input, etc. | |||
// It is OK to call it multiple times. It does not diminish security. | |||
func MixEntropy(seedBytes []byte) { | |||
gRandInfo.MixEntropy(seedBytes) | |||
} | |||
// This only uses the OS's randomness | |||
func randBytes(numBytes int) []byte { | |||
b := make([]byte, numBytes) | |||
_, err := crand.Read(b) | |||
if err != nil { | |||
PanicCrisis(err) | |||
} | |||
return b | |||
} | |||
// This uses the OS and the Seed(s). | |||
func CRandBytes(numBytes int) []byte { | |||
b := make([]byte, numBytes) | |||
_, err := gRandInfo.Read(b) | |||
if err != nil { | |||
PanicCrisis(err) | |||
} | |||
return b | |||
} | |||
// CRandHex returns a hex encoded string that's floor(numDigits/2) * 2 long. | |||
// | |||
// Note: CRandHex(24) gives 96 bits of randomness that | |||
// are usually strong enough for most purposes. | |||
func CRandHex(numDigits int) string { | |||
return hex.EncodeToString(CRandBytes(numDigits / 2)) | |||
} | |||
// Returns a crand.Reader mixed with user-supplied entropy | |||
func CReader() io.Reader { | |||
return gRandInfo | |||
} | |||
//-------------------------------------------------------------------------------- | |||
type randInfo struct { | |||
mtx sync.Mutex | |||
seedBytes [32]byte | |||
cipherAES256 cipher.Block | |||
streamAES256 cipher.Stream | |||
reader io.Reader | |||
} | |||
// You can call this as many times as you'd like. | |||
// XXX TODO review | |||
func (ri *randInfo) MixEntropy(seedBytes []byte) { | |||
ri.mtx.Lock() | |||
defer ri.mtx.Unlock() | |||
// Make new ri.seedBytes using passed seedBytes and current ri.seedBytes: | |||
// ri.seedBytes = sha256( seedBytes || ri.seedBytes ) | |||
h := sha256.New() | |||
h.Write(seedBytes) | |||
h.Write(ri.seedBytes[:]) | |||
hashBytes := h.Sum(nil) | |||
hashBytes32 := [32]byte{} | |||
copy(hashBytes32[:], hashBytes) | |||
ri.seedBytes = xorBytes32(ri.seedBytes, hashBytes32) | |||
// Create new cipher.Block | |||
var err error | |||
ri.cipherAES256, err = aes.NewCipher(ri.seedBytes[:]) | |||
if err != nil { | |||
PanicSanity("Error creating AES256 cipher: " + err.Error()) | |||
} | |||
// Create new stream | |||
ri.streamAES256 = cipher.NewCTR(ri.cipherAES256, randBytes(aes.BlockSize)) | |||
// Create new reader | |||
ri.reader = &cipher.StreamReader{S: ri.streamAES256, R: crand.Reader} | |||
} | |||
func (ri *randInfo) Read(b []byte) (n int, err error) { | |||
ri.mtx.Lock() | |||
defer ri.mtx.Unlock() | |||
return ri.reader.Read(b) | |||
} | |||
func xorBytes32(bytesA [32]byte, bytesB [32]byte) (res [32]byte) { | |||
for i, b := range bytesA { | |||
res[i] = b ^ bytesB[i] | |||
} | |||
return res | |||
} |
@ -0,0 +1,88 @@ | |||
package crypto | |||
import ( | |||
"fmt" | |||
"crypto/subtle" | |||
. "github.com/tendermint/tmlibs/common" | |||
) | |||
func SignatureFromBytes(pubKeyBytes []byte) (pubKey Signature, err error) { | |||
err = cdc.UnmarshalBinaryBare(pubKeyBytes, &pubKey) | |||
return | |||
} | |||
//---------------------------------------- | |||
type Signature interface { | |||
Bytes() []byte | |||
IsZero() bool | |||
Equals(Signature) bool | |||
} | |||
//------------------------------------- | |||
var _ Signature = SignatureEd25519{} | |||
// Implements Signature | |||
type SignatureEd25519 [64]byte | |||
func (sig SignatureEd25519) Bytes() []byte { | |||
bz, err := cdc.MarshalBinaryBare(sig) | |||
if err != nil { | |||
panic(err) | |||
} | |||
return bz | |||
} | |||
func (sig SignatureEd25519) IsZero() bool { return len(sig) == 0 } | |||
func (sig SignatureEd25519) String() string { return fmt.Sprintf("/%X.../", Fingerprint(sig[:])) } | |||
func (sig SignatureEd25519) Equals(other Signature) bool { | |||
if otherEd, ok := other.(SignatureEd25519); ok { | |||
return subtle.ConstantTimeCompare(sig[:], otherEd[:]) == 1 | |||
} else { | |||
return false | |||
} | |||
} | |||
func SignatureEd25519FromBytes(data []byte) Signature { | |||
var sig SignatureEd25519 | |||
copy(sig[:], data) | |||
return sig | |||
} | |||
//------------------------------------- | |||
var _ Signature = SignatureSecp256k1{} | |||
// Implements Signature | |||
type SignatureSecp256k1 []byte | |||
func (sig SignatureSecp256k1) Bytes() []byte { | |||
bz, err := cdc.MarshalBinaryBare(sig) | |||
if err != nil { | |||
panic(err) | |||
} | |||
return bz | |||
} | |||
func (sig SignatureSecp256k1) IsZero() bool { return len(sig) == 0 } | |||
func (sig SignatureSecp256k1) String() string { return fmt.Sprintf("/%X.../", Fingerprint(sig[:])) } | |||
func (sig SignatureSecp256k1) Equals(other Signature) bool { | |||
if otherSecp, ok := other.(SignatureSecp256k1); ok { | |||
return subtle.ConstantTimeCompare(sig[:], otherSecp[:]) == 1 | |||
} else { | |||
return false | |||
} | |||
} | |||
func SignatureSecp256k1FromBytes(data []byte) Signature { | |||
sig := make(SignatureSecp256k1, len(data)) | |||
copy(sig[:], data) | |||
return sig | |||
} |
@ -0,0 +1,46 @@ | |||
package crypto | |||
import ( | |||
"testing" | |||
"github.com/stretchr/testify/assert" | |||
"github.com/stretchr/testify/require" | |||
) | |||
func TestSignAndValidateEd25519(t *testing.T) { | |||
privKey := GenPrivKeyEd25519() | |||
pubKey := privKey.PubKey() | |||
msg := CRandBytes(128) | |||
sig, err := privKey.Sign(msg) | |||
require.Nil(t, err) | |||
// Test the signature | |||
assert.True(t, pubKey.VerifyBytes(msg, sig)) | |||
// Mutate the signature, just one bit. | |||
sigEd := sig.(SignatureEd25519) | |||
sigEd[7] ^= byte(0x01) | |||
sig = sigEd | |||
assert.False(t, pubKey.VerifyBytes(msg, sig)) | |||
} | |||
func TestSignAndValidateSecp256k1(t *testing.T) { | |||
privKey := GenPrivKeySecp256k1() | |||
pubKey := privKey.PubKey() | |||
msg := CRandBytes(128) | |||
sig, err := privKey.Sign(msg) | |||
require.Nil(t, err) | |||
assert.True(t, pubKey.VerifyBytes(msg, sig)) | |||
// Mutate the signature, just one bit. | |||
sigEd := sig.(SignatureSecp256k1) | |||
sigEd[3] ^= byte(0x01) | |||
sig = sigEd | |||
assert.False(t, pubKey.VerifyBytes(msg, sig)) | |||
} |
@ -0,0 +1,51 @@ | |||
package crypto | |||
import ( | |||
"errors" | |||
. "github.com/tendermint/tmlibs/common" | |||
"golang.org/x/crypto/nacl/secretbox" | |||
) | |||
const nonceLen = 24 | |||
const secretLen = 32 | |||
// secret must be 32 bytes long. Use something like Sha256(Bcrypt(passphrase)) | |||
// The ciphertext is (secretbox.Overhead + 24) bytes longer than the plaintext. | |||
// NOTE: call crypto.MixEntropy() first. | |||
func EncryptSymmetric(plaintext []byte, secret []byte) (ciphertext []byte) { | |||
if len(secret) != secretLen { | |||
PanicSanity(Fmt("Secret must be 32 bytes long, got len %v", len(secret))) | |||
} | |||
nonce := CRandBytes(nonceLen) | |||
nonceArr := [nonceLen]byte{} | |||
copy(nonceArr[:], nonce) | |||
secretArr := [secretLen]byte{} | |||
copy(secretArr[:], secret) | |||
ciphertext = make([]byte, nonceLen+secretbox.Overhead+len(plaintext)) | |||
copy(ciphertext, nonce) | |||
secretbox.Seal(ciphertext[nonceLen:nonceLen], plaintext, &nonceArr, &secretArr) | |||
return ciphertext | |||
} | |||
// secret must be 32 bytes long. Use something like Sha256(Bcrypt(passphrase)) | |||
// The ciphertext is (secretbox.Overhead + 24) bytes longer than the plaintext. | |||
func DecryptSymmetric(ciphertext []byte, secret []byte) (plaintext []byte, err error) { | |||
if len(secret) != secretLen { | |||
PanicSanity(Fmt("Secret must be 32 bytes long, got len %v", len(secret))) | |||
} | |||
if len(ciphertext) <= secretbox.Overhead+nonceLen { | |||
return nil, errors.New("Ciphertext is too short") | |||
} | |||
nonce := ciphertext[:nonceLen] | |||
nonceArr := [nonceLen]byte{} | |||
copy(nonceArr[:], nonce) | |||
secretArr := [secretLen]byte{} | |||
copy(secretArr[:], secret) | |||
plaintext = make([]byte, len(ciphertext)-nonceLen-secretbox.Overhead) | |||
_, ok := secretbox.Open(plaintext[:0], ciphertext[nonceLen:], &nonceArr, &secretArr) | |||
if !ok { | |||
return nil, errors.New("Ciphertext decryption failed") | |||
} | |||
return plaintext, nil | |||
} |
@ -0,0 +1,42 @@ | |||
package crypto | |||
import ( | |||
"testing" | |||
"github.com/stretchr/testify/assert" | |||
"github.com/stretchr/testify/require" | |||
"golang.org/x/crypto/bcrypt" | |||
) | |||
func TestSimple(t *testing.T) { | |||
MixEntropy([]byte("someentropy")) | |||
plaintext := []byte("sometext") | |||
secret := []byte("somesecretoflengththirtytwo===32") | |||
ciphertext := EncryptSymmetric(plaintext, secret) | |||
plaintext2, err := DecryptSymmetric(ciphertext, secret) | |||
require.Nil(t, err, "%+v", err) | |||
assert.Equal(t, plaintext, plaintext2) | |||
} | |||
func TestSimpleWithKDF(t *testing.T) { | |||
MixEntropy([]byte("someentropy")) | |||
plaintext := []byte("sometext") | |||
secretPass := []byte("somesecret") | |||
secret, err := bcrypt.GenerateFromPassword(secretPass, 12) | |||
if err != nil { | |||
t.Error(err) | |||
} | |||
secret = Sha256(secret) | |||
ciphertext := EncryptSymmetric(plaintext, secret) | |||
plaintext2, err := DecryptSymmetric(ciphertext, secret) | |||
require.Nil(t, err, "%+v", err) | |||
assert.Equal(t, plaintext, plaintext2) | |||
} |
@ -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) | |||
} |
@ -0,0 +1,3 @@ | |||
package crypto | |||
const Version = "0.9.0-dev" |