@ -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" |