mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2025-01-15 06:28:20 +00:00
a81c863ddb
* rename accounts v2 * rename keymanager and fix imports * rename accounts-v2 instances * imports * build * build fix * deepsource * fix up broken aliases * imports * gaz * Update validator/accounts/accounts_import_test.go Co-authored-by: Ivan Martinez <ivanthegreatdev@gmail.com> * fmt Co-authored-by: Ivan Martinez <ivanthegreatdev@gmail.com>
179 lines
5.2 KiB
Go
179 lines
5.2 KiB
Go
// Copyright 2019, 2020 Weald Technology Trading
|
|
// 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 v1derivation
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"fmt"
|
|
"math/big"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/pkg/errors"
|
|
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
|
"github.com/wealdtech/go-bytesutil"
|
|
e2types "github.com/wealdtech/go-eth2-types/v2"
|
|
"golang.org/x/crypto/hkdf"
|
|
)
|
|
|
|
func _bigInt(input string) *big.Int {
|
|
result, _ := new(big.Int).SetString(input, 10)
|
|
return result
|
|
}
|
|
|
|
var (
|
|
r = _bigInt("52435875175126190479447740508185965837690552500527637822603658699938581184513")
|
|
// 48 comes from ceil((1.5 * ceil(log2(r))) / 8)
|
|
l = 48
|
|
)
|
|
|
|
// PrivateKeyFromSeedAndPath generates a private key given a seed and a path.
|
|
// Follows ERC-2334.
|
|
func PrivateKeyFromSeedAndPath(seed []byte, path string) (*e2types.BLSPrivateKey, error) {
|
|
if path == "" {
|
|
return nil, errors.New("no path")
|
|
}
|
|
if len(seed) < 16 {
|
|
return nil, errors.New("seed must be at least 128 bits")
|
|
}
|
|
pathBits := strings.Split(path, "/")
|
|
var sk *big.Int
|
|
var err error
|
|
for i := range pathBits {
|
|
if pathBits[i] == "" {
|
|
return nil, fmt.Errorf("no entry at path component %d", i)
|
|
}
|
|
if pathBits[i] == "m" {
|
|
if i != 0 {
|
|
return nil, fmt.Errorf("invalid master at path component %d", i)
|
|
}
|
|
sk, err = DeriveMasterSK(seed)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "failed to generate master key at path component %d", i)
|
|
}
|
|
} else {
|
|
if i == 0 {
|
|
return nil, fmt.Errorf("not master at path component %d", i)
|
|
}
|
|
index, err := strconv.ParseInt(pathBits[i], 10, 32)
|
|
if err != nil || index < 0 {
|
|
return nil, fmt.Errorf("invalid index %q at path component %d", pathBits[i], i)
|
|
}
|
|
sk, err = DeriveChildSK(sk, uint32(index))
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "failed to derive child SK at path component %d", i)
|
|
}
|
|
}
|
|
}
|
|
|
|
// SK can be shorter than 32 bytes so left-pad it here.
|
|
bytes := make([]byte, 32)
|
|
skBytes := sk.Bytes()
|
|
copy(bytes[32-len(skBytes):], skBytes)
|
|
|
|
return e2types.BLSPrivateKeyFromBytes(bytes)
|
|
}
|
|
|
|
// DeriveMasterSK derives the master secret key from a seed.
|
|
// Follows ERC-2333.
|
|
func DeriveMasterSK(seed []byte) (*big.Int, error) {
|
|
if len(seed) < 16 {
|
|
return nil, errors.New("seed must be at least 128 bits")
|
|
}
|
|
return hkdfModR(seed, "")
|
|
}
|
|
|
|
// DeriveChildSK derives the child secret key from a parent key.
|
|
// Follows ERC-2333.
|
|
func DeriveChildSK(parentSK *big.Int, index uint32) (*big.Int, error) {
|
|
pk, err := parentSKToLamportPK(parentSK, index)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return hkdfModR(pk, "")
|
|
}
|
|
|
|
// ikmToLamportSK creates a Lamport secret key.
|
|
func ikmToLamportSK(ikm, salt []byte) ([255][32]byte, error) {
|
|
prk := hkdf.Extract(sha256.New, ikm, salt)
|
|
okm := hkdf.Expand(sha256.New, prk, nil)
|
|
var lamportSK [255][32]byte
|
|
for i := 0; i < 255; i++ {
|
|
var result [32]byte
|
|
read, err := okm.Read(result[:])
|
|
if err != nil {
|
|
return lamportSK, err
|
|
}
|
|
if read != 32 {
|
|
return lamportSK, fmt.Errorf("only read %d bytes", read)
|
|
}
|
|
lamportSK[i] = result
|
|
}
|
|
|
|
return lamportSK, nil
|
|
}
|
|
|
|
// parentSKToLamportPK generates the Lamport private key from a BLS secret key.
|
|
func parentSKToLamportPK(parentSK *big.Int, index uint32) ([]byte, error) {
|
|
salt := i2OSP(big.NewInt(int64(index)), 4)
|
|
ikm := i2OSP(parentSK, 32)
|
|
lamport0, err := ikmToLamportSK(ikm, salt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
notIKM := bytesutil.XOR(ikm)
|
|
lamport1, err := ikmToLamportSK(notIKM, salt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
lamportPK := make([]byte, (255+255)*32)
|
|
for i := 0; i < 255; i++ {
|
|
h := hashutil.Hash(lamport0[i][:])
|
|
copy(lamportPK[32*i:], h[:])
|
|
}
|
|
for i := 0; i < 255; i++ {
|
|
h := hashutil.Hash(lamport1[i][:])
|
|
copy(lamportPK[(i+255)*32:], h[:])
|
|
}
|
|
compressedLamportPK := hashutil.Hash(lamportPK)
|
|
return compressedLamportPK[:], nil
|
|
}
|
|
|
|
// hkdfModR hashes 32 random bytes into the subgroup of the BLS12-381 private keys.
|
|
func hkdfModR(ikm []byte, keyInfo string) (*big.Int, error) {
|
|
prk := hkdf.Extract(sha256.New, append(ikm, i2OSP(big.NewInt(0), 1)...), []byte("BLS-SIG-KEYGEN-SALT-"))
|
|
okm := hkdf.Expand(sha256.New, prk, append([]byte(keyInfo), i2OSP(big.NewInt(int64(l)), 2)...))
|
|
okmOut := make([]byte, l)
|
|
read, err := okm.Read(okmOut)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if read != l {
|
|
return nil, fmt.Errorf("only read %d bytes", read)
|
|
}
|
|
return new(big.Int).Mod(osToIP(okmOut), r), nil
|
|
}
|
|
|
|
// osToIP turns a byte array in to an integer as per https://ietf.org/rfc/rfc3447.txt
|
|
func osToIP(data []byte) *big.Int {
|
|
return new(big.Int).SetBytes(data)
|
|
}
|
|
|
|
// i2OSP turns an integer in to a byte array as per https://ietf.org/rfc/rfc3447.txt
|
|
func i2OSP(data *big.Int, resLen int) []byte {
|
|
res := make([]byte, resLen)
|
|
bytes := data.Bytes()
|
|
copy(res[resLen-len(bytes):], bytes)
|
|
return res
|
|
}
|