Files
kms/vendor/github.com/luxfi/go-bip32/utils.go
T
zeekayandHanzo Dev 1c8b627a25 build: resync vendor/ so the kms-operator image can build again
ghcr.io/luxfi/kms-operator has had no new image since v1.12.3. The Build KMS
Operator workflow failed on every tag since (v1.12.4 … v1.12.7) while its
sibling Build KMS succeeded on the same tag, so the tags looked published.

Dockerfile.operator:27 builds with -mod=vendor, and vendor/ had drifted from
go.mod in both directions:
  - go.opentelemetry.io/otel/{metric,trace}: in vendor/modules.txt, not in go.mod
  - github.com/luxfi/{crypto,geth,ids,keys,zap,address,age,cache,constants,
    container,formatting,math,go-bip32,go-bip39}: in go.mod, not marked explicit

`go mod vendor` alone could not fix it — it aborted first on go.sum entries
missing for the k8s packages cmd/kms-operator imports (k8s.io/api/core/v1,
k8s.io/apimachinery/..., k8s.io/client-go/{dynamic,kubernetes,rest}). So:
go mod tidy (adds them, drops now-unused indirects) then go mod vendor.

Verified by stash control:
  operator, -mod=vendor:  rc=1 -> rc=0   ("inconsistent vendoring" 1 -> 0)
  server,   -mod=vendor:  rc=1 -> rc=1   (unchanged)

The server's vendor build was already broken and stays broken, for an unrelated
reason: blst.h has never been vendored (confirmed absent from HEAD's tree, so it
could not have worked before either) — go mod vendor does not copy cgo headers
that live outside the package dir. It is not a regression and does not affect
CI: the server's Dockerfile builds with GOFLAGS=-mod=mod after `rm -f go.sum &&
go mod download`, i.e. from the module cache, never from vendor/. The operator
is the only -mod=vendor consumer, which is exactly why it was the only failure.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-25 17:39:26 -07:00

272 lines
5.1 KiB
Go

package bip32
import (
"bytes"
"crypto/sha256"
"encoding/binary"
"fmt"
"io"
"math/big"
"github.com/decred/dcrd/dcrec/secp256k1/v4"
"golang.org/x/crypto/ripemd160"
)
var (
curve = secp256k1.S256()
curveParams = curve.Params()
// Base58 alphabet used for Bitcoin addresses
base58Alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
)
//
// Hashes
//
func hashSha256(data []byte) ([]byte, error) {
hasher := sha256.New()
_, err := hasher.Write(data)
if err != nil {
return nil, err
}
return hasher.Sum(nil), nil
}
func hashDoubleSha256(data []byte) ([]byte, error) {
hash1, err := hashSha256(data)
if err != nil {
return nil, err
}
hash2, err := hashSha256(hash1)
if err != nil {
return nil, err
}
return hash2, nil
}
func hashRipeMD160(data []byte) ([]byte, error) {
hasher := ripemd160.New()
_, err := io.WriteString(hasher, string(data))
if err != nil {
return nil, err
}
return hasher.Sum(nil), nil
}
func hash160(data []byte) ([]byte, error) {
hash1, err := hashSha256(data)
if err != nil {
return nil, err
}
hash2, err := hashRipeMD160(hash1)
if err != nil {
return nil, err
}
return hash2, nil
}
//
// Base58 Encoding (Bitcoin style)
//
func base58Encode(data []byte) string {
// Count leading zeros
var zeros int
for _, b := range data {
if b == 0 {
zeros++
} else {
break
}
}
// Convert to big integer
num := new(big.Int).SetBytes(data)
base := big.NewInt(58)
zero := big.NewInt(0)
mod := new(big.Int)
var result []byte
for num.Cmp(zero) > 0 {
num.DivMod(num, base, mod)
result = append(result, base58Alphabet[mod.Int64()])
}
// Add leading '1's for each leading zero byte
for i := 0; i < zeros; i++ {
result = append(result, '1')
}
// Reverse the result
for i, j := 0, len(result)-1; i < j; i, j = i+1, j-1 {
result[i], result[j] = result[j], result[i]
}
return string(result)
}
func base58Decode(data string) ([]byte, error) {
// Count leading '1's (zeros in output)
var zeros int
for _, c := range data {
if c == '1' {
zeros++
} else {
break
}
}
// Build alphabet index map
alphabetMap := make(map[rune]int64)
for i, c := range base58Alphabet {
alphabetMap[c] = int64(i)
}
// Convert from base58
num := big.NewInt(0)
base := big.NewInt(58)
for _, c := range data {
idx, ok := alphabetMap[c]
if !ok {
return nil, fmt.Errorf("invalid base58 character: %c", c)
}
num.Mul(num, base)
num.Add(num, big.NewInt(idx))
}
// Convert to bytes
result := num.Bytes()
// Add leading zeros
if zeros > 0 {
prefix := make([]byte, zeros)
result = append(prefix, result...)
}
return result, nil
}
//
// Encoding
//
func checksum(data []byte) ([]byte, error) {
hash, err := hashDoubleSha256(data)
if err != nil {
return nil, err
}
return hash[:4], nil
}
func addChecksumToBytes(data []byte) ([]byte, error) {
checksum, err := checksum(data)
if err != nil {
return nil, err
}
return append(data, checksum...), nil
}
// Keys
func publicKeyForPrivateKey(key []byte) []byte {
x, y := curve.ScalarBaseMult(key)
return compressPublicKey(x, y)
}
func addPublicKeys(key1 []byte, key2 []byte) []byte {
x1, y1 := expandPublicKey(key1)
x2, y2 := expandPublicKey(key2)
x, y := curve.Add(x1, y1, x2, y2)
return compressPublicKey(x, y)
}
func addPrivateKeys(key1 []byte, key2 []byte) []byte {
var key1Int big.Int
var key2Int big.Int
key1Int.SetBytes(key1)
key2Int.SetBytes(key2)
key1Int.Add(&key1Int, &key2Int)
key1Int.Mod(&key1Int, curve.Params().N)
b := key1Int.Bytes()
if len(b) < 32 {
extra := make([]byte, 32-len(b))
b = append(extra, b...)
}
return b
}
func compressPublicKey(x *big.Int, y *big.Int) []byte {
var key bytes.Buffer
// Write header; 0x2 for even y value; 0x3 for odd
key.WriteByte(byte(0x2) + byte(y.Bit(0)))
// Write X coord; Pad the key so x is aligned with the LSB. Pad size is key length - header size (1) - xBytes size
xBytes := x.Bytes()
for i := 0; i < (PublicKeyCompressedLength - 1 - len(xBytes)); i++ {
key.WriteByte(0x0)
}
key.Write(xBytes)
return key.Bytes()
}
// As described at https://crypto.stackexchange.com/a/8916
func expandPublicKey(key []byte) (*big.Int, *big.Int) {
Y := big.NewInt(0)
X := big.NewInt(0)
X.SetBytes(key[1:])
// y^2 = x^3 + ax^2 + b
// a = 0
// => y^2 = x^3 + b
ySquared := big.NewInt(0)
ySquared.Exp(X, big.NewInt(3), nil)
ySquared.Add(ySquared, curveParams.B)
Y.ModSqrt(ySquared, curveParams.P)
Ymod2 := big.NewInt(0)
Ymod2.Mod(Y, big.NewInt(2))
signY := uint64(key[0]) - 2
if signY != Ymod2.Uint64() {
Y.Sub(curveParams.P, Y)
}
return X, Y
}
func validatePrivateKey(key []byte) error {
if fmt.Sprintf("%x", key) == "0000000000000000000000000000000000000000000000000000000000000000" || //if the key is zero
bytes.Compare(key, curveParams.N.Bytes()) >= 0 || //or is outside of the curve
len(key) != 32 { //or is too short
return ErrInvalidPrivateKey
}
return nil
}
func validateChildPublicKey(key []byte) error {
x, y := expandPublicKey(key)
if x.Sign() == 0 || y.Sign() == 0 {
return ErrInvalidPublicKey
}
return nil
}
// Numerical
func uint32Bytes(i uint32) []byte {
bytes := make([]byte, 4)
binary.BigEndian.PutUint32(bytes, i)
return bytes
}