mirror of
https://github.com/fluencelabs/tendermint
synced 2025-04-24 14:22:16 +00:00
mv go-crypto files to crypto dir
This commit is contained in:
commit
368c236c75
1
.gitignore
vendored
1
.gitignore
vendored
@ -25,3 +25,4 @@ scripts/cutWALUntil/cutWALUntil
|
||||
*.iml
|
||||
|
||||
libs/pubsub/query/fuzz_test/output
|
||||
shunit2
|
||||
|
154
CHANGELOG.md
Normal file
154
CHANGELOG.md
Normal file
@ -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
|
||||
|
137
Gopkg.lock
generated
Normal file
137
Gopkg.lock
generated
Normal file
@ -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
|
49
Gopkg.toml
Normal file
49
Gopkg.toml
Normal file
@ -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
|
193
LICENSE
Normal file
193
LICENSE
Normal file
@ -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.
|
99
Makefile
Normal file
99
Makefile
Normal file
@ -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
|
25
README.md
Normal file
25
README.md
Normal file
@ -0,0 +1,25 @@
|
||||
# go-crypto [](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"}
|
||||
```
|
37
amino.go
Normal file
37
amino.go
Normal file
@ -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)
|
||||
}
|
39
armor.go
Normal file
39
armor.go
Normal file
@ -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
|
||||
}
|
20
armor_test.go
Normal file
20
armor_test.go
Normal file
@ -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)
|
||||
}
|
154
crypto/CHANGELOG.md
Normal file
154
crypto/CHANGELOG.md
Normal file
@ -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
|
||||
|
137
crypto/Gopkg.lock
generated
Normal file
137
crypto/Gopkg.lock
generated
Normal file
@ -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
|
49
crypto/Gopkg.toml
Normal file
49
crypto/Gopkg.toml
Normal file
@ -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
|
193
crypto/LICENSE
Normal file
193
crypto/LICENSE
Normal file
@ -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.
|
99
crypto/Makefile
Normal file
99
crypto/Makefile
Normal file
@ -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
|
25
crypto/README.md
Normal file
25
crypto/README.md
Normal file
@ -0,0 +1,25 @@
|
||||
# go-crypto [](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"}
|
||||
```
|
37
crypto/amino.go
Normal file
37
crypto/amino.go
Normal file
@ -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)
|
||||
}
|
39
crypto/armor.go
Normal file
39
crypto/armor.go
Normal file
@ -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
|
||||
}
|
20
crypto/armor_test.go
Normal file
20
crypto/armor_test.go
Normal file
@ -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)
|
||||
}
|
48
crypto/doc.go
Normal file
48
crypto/doc.go
Normal file
@ -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
|
119
crypto/encode_test.go
Normal file
119
crypto/encode_test.go
Normal file
@ -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)
|
||||
|
||||
}
|
35
crypto/example_test.go
Normal file
35
crypto/example_test.go
Normal file
@ -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
|
||||
}
|
18
crypto/hash.go
Normal file
18
crypto/hash.go
Normal file
@ -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)
|
||||
}
|
105
crypto/hkdfchacha20poly1305/hkdfchachapoly.go
Normal file
105
crypto/hkdfchacha20poly1305/hkdfchachapoly.go
Normal file
@ -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
|
||||
}
|
139
crypto/hkdfchacha20poly1305/hkdfchachapoly_test.go
Normal file
139
crypto/hkdfchacha20poly1305/hkdfchachapoly_test.go
Normal file
@ -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.
|
4
crypto/merkle/README.md
Normal file
4
crypto/merkle/README.md
Normal file
@ -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.
|
31
crypto/merkle/doc.go
Normal file
31
crypto/merkle/doc.go
Normal file
@ -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
|
88
crypto/merkle/simple_map.go
Normal file
88
crypto/merkle/simple_map.go
Normal file
@ -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)
|
||||
}
|
54
crypto/merkle/simple_map_test.go
Normal file
54
crypto/merkle/simple_map_test.go
Normal file
@ -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")
|
||||
}
|
||||
}
|
160
crypto/merkle/simple_proof.go
Normal file
160
crypto/merkle/simple_proof.go
Normal file
@ -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
|
||||
}
|
||||
}
|
58
crypto/merkle/simple_tree.go
Normal file
58
crypto/merkle/simple_tree.go
Normal file
@ -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)
|
||||
}
|
||||
}
|
88
crypto/merkle/simple_tree_test.go
Normal file
88
crypto/merkle/simple_tree_test.go
Normal file
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
38
crypto/merkle/types.go
Normal file
38
crypto/merkle/types.go
Normal file
@ -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)
|
||||
}
|
164
crypto/priv_key.go
Normal file
164
crypto/priv_key.go
Normal file
@ -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)
|
||||
}
|
60
crypto/priv_key_test.go
Normal file
60
crypto/priv_key_test.go
Normal file
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
149
crypto/pub_key.go
Normal file
149
crypto/pub_key.go
Normal file
@ -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
|
||||
}
|
||||
}
|
50
crypto/pub_key_test.go
Normal file
50
crypto/pub_key_test.go
Normal file
@ -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")
|
||||
}
|
108
crypto/random.go
Normal file
108
crypto/random.go
Normal file
@ -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
|
||||
}
|
88
crypto/signature.go
Normal file
88
crypto/signature.go
Normal file
@ -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
|
||||
}
|
46
crypto/signature_test.go
Normal file
46
crypto/signature_test.go
Normal file
@ -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))
|
||||
}
|
51
crypto/symmetric.go
Normal file
51
crypto/symmetric.go
Normal file
@ -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
|
||||
}
|
42
crypto/symmetric_test.go
Normal file
42
crypto/symmetric_test.go
Normal file
@ -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)
|
||||
}
|
48
crypto/tmhash/hash.go
Normal file
48
crypto/tmhash/hash.go
Normal file
@ -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]
|
||||
}
|
23
crypto/tmhash/hash_test.go
Normal file
23
crypto/tmhash/hash_test.go
Normal file
@ -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)
|
||||
}
|
3
crypto/version.go
Normal file
3
crypto/version.go
Normal file
@ -0,0 +1,3 @@
|
||||
package crypto
|
||||
|
||||
const Version = "0.9.0-dev"
|
48
doc.go
Normal file
48
doc.go
Normal file
@ -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
|
119
encode_test.go
Normal file
119
encode_test.go
Normal file
@ -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)
|
||||
|
||||
}
|
35
example_test.go
Normal file
35
example_test.go
Normal file
@ -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
|
||||
}
|
18
hash.go
Normal file
18
hash.go
Normal file
@ -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)
|
||||
}
|
105
hkdfchacha20poly1305/hkdfchachapoly.go
Normal file
105
hkdfchacha20poly1305/hkdfchachapoly.go
Normal file
@ -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
|
||||
}
|
139
hkdfchacha20poly1305/hkdfchachapoly_test.go
Normal file
139
hkdfchacha20poly1305/hkdfchachapoly_test.go
Normal file
@ -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.
|
4
merkle/README.md
Normal file
4
merkle/README.md
Normal file
@ -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.
|
31
merkle/doc.go
Normal file
31
merkle/doc.go
Normal file
@ -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
|
88
merkle/simple_map.go
Normal file
88
merkle/simple_map.go
Normal file
@ -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)
|
||||
}
|
54
merkle/simple_map_test.go
Normal file
54
merkle/simple_map_test.go
Normal file
@ -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")
|
||||
}
|
||||
}
|
160
merkle/simple_proof.go
Normal file
160
merkle/simple_proof.go
Normal file
@ -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
|
||||
}
|
||||
}
|
58
merkle/simple_tree.go
Normal file
58
merkle/simple_tree.go
Normal file
@ -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)
|
||||
}
|
||||
}
|
88
merkle/simple_tree_test.go
Normal file
88
merkle/simple_tree_test.go
Normal file
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
38
merkle/types.go
Normal file
38
merkle/types.go
Normal file
@ -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)
|
||||
}
|
164
priv_key.go
Normal file
164
priv_key.go
Normal file
@ -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)
|
||||
}
|
60
priv_key_test.go
Normal file
60
priv_key_test.go
Normal file
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
149
pub_key.go
Normal file
149
pub_key.go
Normal file
@ -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
|
||||
}
|
||||
}
|
50
pub_key_test.go
Normal file
50
pub_key_test.go
Normal file
@ -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")
|
||||
}
|
108
random.go
Normal file
108
random.go
Normal file
@ -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
|
||||
}
|
88
signature.go
Normal file
88
signature.go
Normal file
@ -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
|
||||
}
|
46
signature_test.go
Normal file
46
signature_test.go
Normal file
@ -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))
|
||||
}
|
51
symmetric.go
Normal file
51
symmetric.go
Normal file
@ -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
|
||||
}
|
42
symmetric_test.go
Normal file
42
symmetric_test.go
Normal file
@ -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)
|
||||
}
|
48
tmhash/hash.go
Normal file
48
tmhash/hash.go
Normal file
@ -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]
|
||||
}
|
23
tmhash/hash_test.go
Normal file
23
tmhash/hash_test.go
Normal file
@ -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)
|
||||
}
|
3
version.go
Normal file
3
version.go
Normal file
@ -0,0 +1,3 @@
|
||||
package crypto
|
||||
|
||||
const Version = "0.9.0-dev"
|
Loading…
x
Reference in New Issue
Block a user