implement the api for generateOneTimeAddress

This commit is contained in:
fnaticwang 2019-09-24 11:49:34 +08:00
parent 523292bb53
commit f2f4fec6ea
4 changed files with 201 additions and 0 deletions

View file

@ -21,6 +21,7 @@ import (
"crypto/ecdsa"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
@ -29,6 +30,7 @@ import (
"strings"
"time"
"github.com/btcsuite/btcd/btcec"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
@ -272,6 +274,66 @@ func keyFileName(keyAddr common.Address) string {
return fmt.Sprintf("UTC--%s--%s", toISO8601(ts), hex.EncodeToString(keyAddr[:]))
}
// GeneratePKPairFromUAddress represents the keystore to retrieve public key-pair from given UAddress
func GeneratePKPairFromUAddress(w []byte) (*ecdsa.PublicKey, *ecdsa.PublicKey, error) {
if len(w) != common.UAddressLength {
return nil, nil, ErrUAddressInvalid
}
tmp := make([]byte, 33)
copy(tmp[:], w[:33])
curve := btcec.S256()
PK1, err := btcec.ParsePubKey(tmp, curve)
if err != nil {
return nil, nil, err
}
copy(tmp[:], w[33:])
PK2, err := btcec.ParsePubKey(tmp, curve)
if err != nil {
return nil, nil, err
}
return (*ecdsa.PublicKey)(PK1), (*ecdsa.PublicKey)(PK2), nil
}
func UaddrFromUncompressedRawBytes(raw []byte) (*common.UAddress, error) {
if len(raw) != 32*2*2 {
return nil, errors.New("invalid uncompressed use address len")
}
pub := make([]byte, 65)
pub[0] = 0x004
copy(pub[1:], raw[:64])
A := crypto.ToECDSAPub(pub)
copy(pub[1:], raw[64:])
B := crypto.ToECDSAPub(pub)
return GenerateUaddressFromPK(A, B), nil
}
func UaddrToUncompressedRawBytes(waddr []byte) ([]byte, error) {
if len(waddr) != common.UAddressLength {
return nil, ErrUAddressInvalid
}
A, B, err := GeneratePKPairFromUAddress(waddr)
if err != nil {
return nil, err
}
u := make([]byte, 32*2*2)
ax := math.PaddedBigBytes(A.X, 32)
ay := math.PaddedBigBytes(A.Y, 32)
bx := math.PaddedBigBytes(B.X, 32)
by := math.PaddedBigBytes(B.Y, 32)
copy(u[0:], ax[:32])
copy(u[32:], ay[:32])
copy(u[64:], bx[:32])
copy(u[96:], by[:32])
return u, nil
}
func toISO8601(t time.Time) string {
var tz string
name, offset := t.Zone()

View file

@ -31,10 +31,13 @@ encoding may be of uneven length. The number zero encodes as "0x0".
package hexutil
import (
"crypto/ecdsa"
"encoding/hex"
"fmt"
"math/big"
"strconv"
"github.com/ethereum/go-ethereum/common/math"
)
const uintBits = 32 << (uint64(^uint(0)) >> 63)
@ -238,3 +241,13 @@ func mapError(err error) error {
}
return err
}
// PKPair2HexSlice generate byte-slice based on given public key pair
func PKPair2HexSlice(pk1 *ecdsa.PublicKey, pk2 *ecdsa.PublicKey) []string {
return []string{
Encode(math.PaddedBigBytes(pk1.X, 32)),
Encode(math.PaddedBigBytes(pk1.Y, 32)),
Encode(math.PaddedBigBytes(pk2.X, 32)),
Encode(math.PaddedBigBytes(pk2.Y, 32)),
}
}

View file

@ -29,6 +29,7 @@ import (
"os"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/rlp"
"golang.org/x/crypto/sha3"
@ -148,6 +149,20 @@ func UnmarshalPubkey(pub []byte) (*ecdsa.PublicKey, error) {
return &ecdsa.PublicKey{Curve: S256(), X: x, Y: y}, nil
}
//check input error
func ToECDSAPub(pub []byte) *ecdsa.PublicKey {
if len(pub) != 65 {
return nil
}
x, y := elliptic.Unmarshal(S256(), pub)
if x == nil || y == nil {
return nil
}
return &ecdsa.PublicKey{Curve: S256(), X: x, Y: y}
}
func FromECDSAPub(pub *ecdsa.PublicKey) []byte {
if pub == nil || pub.X == nil || pub.Y == nil {
return nil
@ -219,3 +234,64 @@ func zeroBytes(bytes []byte) {
bytes[i] = 0
}
}
// A1=[hash([r]B)]G+A
func generateA1(r []byte, A *ecdsa.PublicKey, B *ecdsa.PublicKey) ecdsa.PublicKey {
A1 := new(ecdsa.PublicKey)
A1.X, A1.Y = S256().ScalarMult(B.X, B.Y, r) //A1=[r]B
A1Bytes := Keccak256(FromECDSAPub(A1)) //hash([r]B)
A1.X, A1.Y = S256().ScalarBaseMult(A1Bytes) //[hash([r]B)]G
A1.X, A1.Y = S256().Add(A1.X, A1.Y, A.X, A.Y) //A1=[hash([r]B)]G+A
A1.Curve = S256()
return *A1
}
func CompareA1(b []byte, A *ecdsa.PublicKey, S1 *ecdsa.PublicKey, A1 *ecdsa.PublicKey) bool {
A1n := generateA1(b, A, S1)
if A1.X.Cmp(A1n.X) == 0 && A1.Y.Cmp(A1n.Y) == 0 {
return true
}
return false
}
// generateOneTimeKey2528 generates an OTA account for receiver using receiver's publickey
func generateOneTimeKey2528(A *ecdsa.PublicKey, B *ecdsa.PublicKey) (A1 *ecdsa.PublicKey, R *ecdsa.PublicKey, err error) {
RPrivateKey, err := GenerateKey()
if err != nil {
return nil, nil, err
}
R = &RPrivateKey.PublicKey
A1 = new(ecdsa.PublicKey)
*A1 = generateA1(RPrivateKey.D.Bytes(), A, B)
return A1, R, err
}
// Generate OTA account interface
func GenerateOneTimeKey(AX string, AY string, BX string, BY string) (ret []string, err error) {
bytesAX, err := hexutil.Decode(AX)
if err != nil {
return
}
bytesAY, err := hexutil.Decode(AY)
if err != nil {
return
}
bytesBX, err := hexutil.Decode(BX)
if err != nil {
return
}
bytesBY, err := hexutil.Decode(BY)
if err != nil {
return
}
bnAX := new(big.Int).SetBytes(bytesAX)
bnAY := new(big.Int).SetBytes(bytesAY)
bnBX := new(big.Int).SetBytes(bytesBX)
bnBY := new(big.Int).SetBytes(bytesBY)
pa := &ecdsa.PublicKey{X: bnAX, Y: bnAY}
pb := &ecdsa.PublicKey{X: bnBX, Y: bnBY}
generatedA1, generatedR, err := generateOneTimeKey2528(pa, pb)
return hexutil.PKPair2HexSlice(generatedA1, generatedR), nil
}

View file

@ -52,6 +52,18 @@ const (
defaultGasPrice = params.GWei
)
var (
ErrInvalidUAddress = errors.New("Invalid Uaddress, try again")
ErrFailToGeneratePKPairFromUAddress = errors.New("Fail to generate publickey pair from UAddress")
ErrFailToGeneratePKPairSlice = errors.New("Fail to generate publickey pair hex slice")
ErrInvalidPrivateKey = errors.New("Invalid private key")
ErrInvalidOTAMixSet = errors.New("Invalid OTA mix set")
ErrInvalidOTAAddr = errors.New("Invalid OTA address")
ErrReqTooManyOTAMix = errors.New("Require too many OTA mix address")
ErrInvalidOTAMixNum = errors.New("Invalid required OTA mix address number")
ErrInvalidInput = errors.New("Invalid input")
)
// PublicEthereumAPI provides an API to access Ethereum related information.
// It offers only methods that operate on public data that is freely available to anyone.
type PublicEthereumAPI struct {
@ -1822,3 +1834,41 @@ func (s *PublicTransactionPoolAPI) GetUseAddress(ctx context.Context, a common.A
return hexutil.Encode(useAddr[:]), nil
}
// GenerateOneTimeAddress returns corresponding One-Time-Address for a given UseAddress
func (s *PublicTransactionPoolAPI) GenerateOneTimeAddress(ctx context.Context, uAddr string) (string, error) {
strlen := len(uAddr)
if strlen != (common.UAddressLength<<1)+2 {
return "", ErrInvalidUAddress
}
PKBytesSlice, err := hexutil.Decode(uAddr)
if err != nil {
return "", err
}
PK1, PK2, err := keystore.GeneratePKPairFromUAddress(PKBytesSlice)
if err != nil {
return "", ErrFailToGeneratePKPairFromUAddress
}
PKPairSlice := hexutil.PKPair2HexSlice(PK1, PK2)
SKOTA, err := crypto.GenerateOneTimeKey(PKPairSlice[0], PKPairSlice[1], PKPairSlice[2], PKPairSlice[3])
if err != nil {
return "", err
}
otaStr := strings.Replace(strings.Join(SKOTA, ""), "0x", "", -1)
raw, err := hexutil.Decode("0x" + otaStr)
if err != nil {
return "", err
}
rawUanAddr, err := keystore.UaddrFromUncompressedRawBytes(raw)
if err != nil || rawUanAddr == nil {
return "", err
}
return hexutil.Encode(rawUanAddr[:]), nil
}