Skip to content

Commit ce618c4

Browse files
refactor: reduce tendermint deps (backport #14616) (#14662)
Co-authored-by: Julien Robert <julien@rbrt.fr>
1 parent 9702d95 commit ce618c4

File tree

13 files changed

+350
-42
lines changed

13 files changed

+350
-42
lines changed

crypto/armor.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ import (
66
"fmt"
77
"io"
88

9-
"github.com/tendermint/crypto/bcrypt"
109
"github.com/tendermint/tendermint/crypto"
1110
"golang.org/x/crypto/openpgp/armor" //nolint:staticcheck
1211

1312
"github.com/cosmos/cosmos-sdk/codec/legacy"
13+
"github.com/cosmos/cosmos-sdk/crypto/keys/bcrypt"
1414
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
1515
"github.com/cosmos/cosmos-sdk/crypto/xsalsa20symmetric"
1616
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"

crypto/armor_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99

1010
"github.com/stretchr/testify/assert"
1111
"github.com/stretchr/testify/require"
12-
"github.com/tendermint/crypto/bcrypt"
1312
tmcrypto "github.com/tendermint/tendermint/crypto"
1413
"github.com/tendermint/tendermint/crypto/xsalsa20symmetric"
1514

@@ -19,6 +18,7 @@ import (
1918
"github.com/cosmos/cosmos-sdk/crypto"
2019
"github.com/cosmos/cosmos-sdk/crypto/hd"
2120
"github.com/cosmos/cosmos-sdk/crypto/keyring"
21+
"github.com/cosmos/cosmos-sdk/crypto/keys/bcrypt"
2222
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
2323
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
2424
_ "github.com/cosmos/cosmos-sdk/runtime"

crypto/keyring/keyring.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ import (
1212

1313
"github.com/99designs/keyring"
1414
"github.com/pkg/errors"
15-
"github.com/tendermint/crypto/bcrypt"
1615
tmcrypto "github.com/tendermint/tendermint/crypto"
1716

1817
"github.com/cosmos/cosmos-sdk/client/input"
1918
"github.com/cosmos/cosmos-sdk/codec"
2019
"github.com/cosmos/cosmos-sdk/crypto"
2120
"github.com/cosmos/cosmos-sdk/crypto/hd"
21+
"github.com/cosmos/cosmos-sdk/crypto/keys/bcrypt"
2222
"github.com/cosmos/cosmos-sdk/crypto/ledger"
2323
"github.com/cosmos/cosmos-sdk/crypto/types"
2424
sdk "github.com/cosmos/cosmos-sdk/types"

crypto/keys/bcrypt/base64.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright 2011 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package bcrypt
6+
7+
import "encoding/base64"
8+
9+
const alphabet = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
10+
11+
var bcEncoding = base64.NewEncoding(alphabet)
12+
13+
func base64Encode(src []byte) []byte {
14+
n := bcEncoding.EncodedLen(len(src))
15+
dst := make([]byte, n)
16+
bcEncoding.Encode(dst, src)
17+
for dst[n-1] == '=' {
18+
n--
19+
}
20+
return dst[:n]
21+
}
22+
23+
func base64Decode(src []byte) ([]byte, error) {
24+
numOfEquals := 4 - (len(src) % 4)
25+
for i := 0; i < numOfEquals; i++ {
26+
src = append(src, '=')
27+
}
28+
29+
dst := make([]byte, bcEncoding.DecodedLen(len(src)))
30+
n, err := bcEncoding.Decode(dst, src)
31+
if err != nil {
32+
return nil, err
33+
}
34+
return dst[:n], nil
35+
}

crypto/keys/bcrypt/bcrypt.go

Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
// MODIFIED BY TENDERMINT TO EXPOSE `salt` in `GenerateFromPassword`.
2+
// Copyright 2011 The Go Authors. All rights reserved.
3+
// Use of this source code is governed by a BSD-style
4+
// license that can be found in the LICENSE file.
5+
6+
// Package bcrypt implements Provos and Mazières's bcrypt adaptive hashing
7+
// algorithm. See http://www.usenix.org/event/usenix99/provos/provos.pdf
8+
package bcrypt
9+
10+
// The code is a port of Provos and Mazières's C implementation.
11+
import (
12+
"crypto/subtle"
13+
"errors"
14+
"fmt"
15+
"strconv"
16+
17+
"golang.org/x/crypto/blowfish" //nolint:staticcheck // used in original https://cs.opensource.google/go/x/crypto/+/master:bcrypt/bcrypt.go
18+
)
19+
20+
const (
21+
MinCost int = 4 // the minimum allowable cost as passed in to GenerateFromPassword
22+
MaxCost int = 31 // the maximum allowable cost as passed in to GenerateFromPassword
23+
DefaultCost int = 10 // the cost that will actually be set if a cost below MinCost is passed into GenerateFromPassword
24+
)
25+
26+
// ErrMismatchedHashAndPassword is returned from CompareHashAndPassword when a password and hash do
27+
// not match.
28+
var ErrMismatchedHashAndPassword = errors.New("crypto/bcrypt: hashedPassword is not the hash of the given password")
29+
30+
// ErrHashTooShort is returned from CompareHashAndPassword when a hash is too short to
31+
// be a bcrypt hash.
32+
var ErrHashTooShort = errors.New("crypto/bcrypt: hashedSecret too short to be a bcrypted password")
33+
34+
// The error returned from CompareHashAndPassword when a hash was created with
35+
// a bcrypt algorithm newer than this implementation.
36+
type HashVersionTooNewError byte
37+
38+
func (hv HashVersionTooNewError) Error() string {
39+
return fmt.Sprintf("crypto/bcrypt: bcrypt algorithm version '%c' requested is newer than current version '%c'", byte(hv), majorVersion)
40+
}
41+
42+
// The error returned from CompareHashAndPassword when a hash starts with something other than '$'
43+
type InvalidHashPrefixError byte
44+
45+
func (ih InvalidHashPrefixError) Error() string {
46+
return fmt.Sprintf("crypto/bcrypt: bcrypt hashes must start with '$', but hashedSecret started with '%c'", byte(ih))
47+
}
48+
49+
type InvalidCostError int
50+
51+
func (ic InvalidCostError) Error() string {
52+
return fmt.Sprintf("crypto/bcrypt: cost %d is outside allowed range (%d,%d)", int(ic), MinCost, MaxCost)
53+
}
54+
55+
const (
56+
majorVersion = '2'
57+
minorVersion = 'a'
58+
maxSaltSize = 16
59+
maxCryptedHashSize = 23
60+
encodedSaltSize = 22
61+
encodedHashSize = 31
62+
minHashSize = 59
63+
)
64+
65+
// magicCipherData is an IV for the 64 Blowfish encryption calls in
66+
// bcrypt(). It's the string "OrpheanBeholderScryDoubt" in big-endian bytes.
67+
var magicCipherData = []byte{
68+
0x4f, 0x72, 0x70, 0x68,
69+
0x65, 0x61, 0x6e, 0x42,
70+
0x65, 0x68, 0x6f, 0x6c,
71+
0x64, 0x65, 0x72, 0x53,
72+
0x63, 0x72, 0x79, 0x44,
73+
0x6f, 0x75, 0x62, 0x74,
74+
}
75+
76+
type hashed struct {
77+
hash []byte
78+
salt []byte
79+
cost int // allowed range is MinCost to MaxCost
80+
major byte
81+
minor byte
82+
}
83+
84+
// GenerateFromPassword returns the bcrypt hash of the password at the given
85+
// cost. If the cost given is less than MinCost, the cost will be set to
86+
// DefaultCost, instead. Use CompareHashAndPassword, as defined in this package,
87+
// to compare the returned hashed password with its cleartext version.
88+
func GenerateFromPassword(salt []byte, password []byte, cost int) ([]byte, error) {
89+
if len(salt) != maxSaltSize {
90+
return nil, fmt.Errorf("salt len must be %v", maxSaltSize)
91+
}
92+
p, err := newFromPassword(salt, password, cost)
93+
if err != nil {
94+
return nil, err
95+
}
96+
return p.Hash(), nil
97+
}
98+
99+
// CompareHashAndPassword compares a bcrypt hashed password with its possible
100+
// plaintext equivalent. Returns nil on success, or an error on failure.
101+
func CompareHashAndPassword(hashedPassword, password []byte) error {
102+
p, err := newFromHash(hashedPassword)
103+
if err != nil {
104+
return err
105+
}
106+
107+
otherHash, err := bcrypt(password, p.cost, p.salt)
108+
if err != nil {
109+
return err
110+
}
111+
112+
otherP := &hashed{otherHash, p.salt, p.cost, p.major, p.minor}
113+
if subtle.ConstantTimeCompare(p.Hash(), otherP.Hash()) == 1 {
114+
return nil
115+
}
116+
117+
return ErrMismatchedHashAndPassword
118+
}
119+
120+
// Cost returns the hashing cost used to create the given hashed
121+
// password. When, in the future, the hashing cost of a password system needs
122+
// to be increased in order to adjust for greater computational power, this
123+
// function allows one to establish which passwords need to be updated.
124+
func Cost(hashedPassword []byte) (int, error) {
125+
p, err := newFromHash(hashedPassword)
126+
if err != nil {
127+
return 0, err
128+
}
129+
return p.cost, nil
130+
}
131+
132+
func newFromPassword(salt []byte, password []byte, cost int) (*hashed, error) {
133+
if cost < MinCost {
134+
cost = DefaultCost
135+
}
136+
p := new(hashed)
137+
p.major = majorVersion
138+
p.minor = minorVersion
139+
140+
err := checkCost(cost)
141+
if err != nil {
142+
return nil, err
143+
}
144+
p.cost = cost
145+
146+
p.salt = base64Encode(salt)
147+
hash, err := bcrypt(password, p.cost, p.salt)
148+
if err != nil {
149+
return nil, err
150+
}
151+
p.hash = hash
152+
return p, err
153+
}
154+
155+
func newFromHash(hashedSecret []byte) (*hashed, error) {
156+
if len(hashedSecret) < minHashSize {
157+
return nil, ErrHashTooShort
158+
}
159+
p := new(hashed)
160+
n, err := p.decodeVersion(hashedSecret)
161+
if err != nil {
162+
return nil, err
163+
}
164+
hashedSecret = hashedSecret[n:]
165+
n, err = p.decodeCost(hashedSecret)
166+
if err != nil {
167+
return nil, err
168+
}
169+
hashedSecret = hashedSecret[n:]
170+
171+
// The "+2" is here because we'll have to append at most 2 '=' to the salt
172+
// when base64 decoding it in expensiveBlowfishSetup().
173+
p.salt = make([]byte, encodedSaltSize, encodedSaltSize+2)
174+
copy(p.salt, hashedSecret[:encodedSaltSize])
175+
176+
hashedSecret = hashedSecret[encodedSaltSize:]
177+
p.hash = make([]byte, len(hashedSecret))
178+
copy(p.hash, hashedSecret)
179+
180+
return p, nil
181+
}
182+
183+
func bcrypt(password []byte, cost int, salt []byte) ([]byte, error) {
184+
cipherData := make([]byte, len(magicCipherData))
185+
copy(cipherData, magicCipherData)
186+
187+
c, err := expensiveBlowfishSetup(password, uint32(cost), salt)
188+
if err != nil {
189+
return nil, err
190+
}
191+
192+
for i := 0; i < 24; i += 8 {
193+
for j := 0; j < 64; j++ {
194+
c.Encrypt(cipherData[i:i+8], cipherData[i:i+8])
195+
}
196+
}
197+
198+
// Bug compatibility with C bcrypt implementations. We only encode 23 of
199+
// the 24 bytes encrypted.
200+
hsh := base64Encode(cipherData[:maxCryptedHashSize])
201+
return hsh, nil
202+
}
203+
204+
func expensiveBlowfishSetup(key []byte, cost uint32, salt []byte) (*blowfish.Cipher, error) {
205+
csalt, err := base64Decode(salt)
206+
if err != nil {
207+
return nil, err
208+
}
209+
210+
// Bug compatibility with C bcrypt implementations. They use the trailing
211+
// NULL in the key string during expansion.
212+
// We copy the key to prevent changing the underlying array.
213+
ckey := append(key[:len(key):len(key)], 0) //nolint:gocritic // used in original https://cs.opensource.google/go/x/crypto/+/master:bcrypt/bcrypt.go
214+
215+
c, err := blowfish.NewSaltedCipher(ckey, csalt)
216+
if err != nil {
217+
return nil, err
218+
}
219+
220+
var i, rounds uint64
221+
rounds = 1 << cost
222+
for i = 0; i < rounds; i++ {
223+
blowfish.ExpandKey(ckey, c)
224+
blowfish.ExpandKey(csalt, c)
225+
}
226+
227+
return c, nil
228+
}
229+
230+
func (p *hashed) Hash() []byte {
231+
arr := make([]byte, 60)
232+
arr[0] = '$'
233+
arr[1] = p.major
234+
n := 2
235+
if p.minor != 0 {
236+
arr[2] = p.minor
237+
n = 3
238+
}
239+
arr[n] = '$'
240+
n++
241+
copy(arr[n:], []byte(fmt.Sprintf("%02d", p.cost)))
242+
n += 2
243+
arr[n] = '$'
244+
n++
245+
copy(arr[n:], p.salt)
246+
n += encodedSaltSize
247+
copy(arr[n:], p.hash)
248+
n += encodedHashSize
249+
return arr[:n]
250+
}
251+
252+
func (p *hashed) decodeVersion(sbytes []byte) (int, error) {
253+
if sbytes[0] != '$' {
254+
return -1, InvalidHashPrefixError(sbytes[0])
255+
}
256+
if sbytes[1] > majorVersion {
257+
return -1, HashVersionTooNewError(sbytes[1])
258+
}
259+
p.major = sbytes[1]
260+
n := 3
261+
if sbytes[2] != '$' {
262+
p.minor = sbytes[2]
263+
n++
264+
}
265+
return n, nil
266+
}
267+
268+
// sbytes should begin where decodeVersion left off.
269+
func (p *hashed) decodeCost(sbytes []byte) (int, error) {
270+
cost, err := strconv.Atoi(string(sbytes[0:2]))
271+
if err != nil {
272+
return -1, err
273+
}
274+
err = checkCost(cost)
275+
if err != nil {
276+
return -1, err
277+
}
278+
p.cost = cost
279+
return 3, nil
280+
}
281+
282+
func (p *hashed) String() string {
283+
return fmt.Sprintf("&{hash: %#v, salt: %#v, cost: %d, major: %c, minor: %c}", string(p.hash), p.salt, p.cost, p.major, p.minor)
284+
}
285+
286+
func checkCost(cost int) error {
287+
if cost < MinCost || cost > MaxCost {
288+
return InvalidCostError(cost)
289+
}
290+
return nil
291+
}

0 commit comments

Comments
 (0)