Develop blockchain applications from scratch (11)--Ethereum address generation

1. Generate the private key of the Ethereum address

1.1 Generate Ethereum address private key function code

  • Create constants
const (
	BitcoinSeed = "Bitcoin seed"
	Mnemonic    = "search crime session tag file joke leaf express interest slender file hawk"
)
  • Create a generate address function
func NewAccount(password string) string {
	seed := pbkdf2.Key([]byte(Mnemonic), []byte("mnemonic"+password), 2048, 64, sha512.New)
	hmac := hmac.New(sha512.New, []byte(BitcoinSeed))
	_, err := hmac.Write([]byte(seed))
	if err != nil {
		return ""
	}
	intermediary := hmac.Sum(nil)
	keyBytes := intermediary[:32] // 私钥
	_, pub := ecdsa.PrivKeyFromBytes(ecdsa.S256(), keyBytes)
	return pub.ToAddress().String()
}

1.2 pbkdf2.Key() key generation function

PBKDF2(Password-Based Key Derivation Function)

is a function used to derive a key, often used to generate encrypted passwords.

Its basic principle is to pass a pseudo-random function (such as HMAC function, sha512, etc.), take the plaintext (password) and a salt value (salt) as an input parameter, then repeat the operation, and finally generate a secret key.

If the number of repetitions is large enough, the cost of cracking becomes very high. The addition of salt value will also increase the difficulty of the "rainbow table" attack.

User passwords are stored using the PBKDF2 algorithm, which is relatively secure.

Syntax Definition of PBKDF2 Function

DK = PBKDF2(PRF, Password, Salt, c, dkLen ,Hash algorithm)
  • PRF is a pseudo-random function, such as the HASH_HMAC function, which outputs a result of length hLen.
  • Password is the original password used to generate the key.
  • Salt is a salt used for encryption.
  • c is the number of times to repeat the calculation.
  • dkLen is the expected length of the key.
  • DK is the last generated key.

The following is the code to generate the private key using the mnemonic

package pbkdf2

import (
	"crypto/rand"
	"crypto/sha512"
	"golang.org/x/crypto/pbkdf2"
)

const (
    Mnemonic =  "search crime conversation tag directory joke leaf express interest password = ""
)
func encryptPwdWithSalt(password string) (*ecdsa.PrivateKey, *ecdsa.PublicKey) {
		seed := pbkdf2.Key([]byte(Mnemonic), []byte("mnemonic"+password), 2048, 64, sha512.New)
}

// []byte(Mnemonic):助记词
// []byte("mnemonic"+password) :salt盐值
// 2048:重复计算的次数
// 64:返回的秘钥长度
// sha512.New:哈希算法

1.3 HMAC Generation Digest Algorithm

The Chinese name of the HMAC algorithm is Hash Message Authentication Code, and the full English name is Hash-based Message Authentication Code. Its algorithm is based on a certain hash function (mainly SHA series and MD series), takes a key and a message as input, and generates a message digest as output . The biggest difference between the HMAC algorithm and other hashing algorithms is that a key is required. Its algorithm function is a one-way Hash function established by using block cipher.
The following table shows the length of the output digest corresponding to the specific algorithm.

algorithm digest length (bits) Remark
HmacMD5 128 BouncyCastle Implementation
HmacSHA1 160 (20 bytes) BouncyCastle implementation
HmacSHA256 256 BouncyCastle Implementation
HmacSHA384 384 BouncyCastle Implementation
HmacSHA512 512 JAVA6 implementation
HmacMD2 128 BouncyCastle Implementation
HmacMD4 128 BouncyCastle Implementation
HmacSHA224 224 BouncyCastle Implementation

The key of HMAC can be of any length. If the length of the key exceeds the length of the digest algorithm information packet, the digest of the key is first calculated using the digest algorithm as a new key. It is generally not recommended to use keys that are too short, because the length of the key is related to the strength of the security. Usually, the key length is not less than the length of the information digest output by the selected digest algorithm.

Detailed analysis of the code encapsulated in golang of the HMAC algorithm

//创建运算对象,HMAC需要两个参数:hash函数和key
hmac := hmac.New(sha512.New, []byte(BitcoinSeed))
//将明文写入到hmac中
	_, err := hmac.Write([]byte(seed))
	if err != nil {
		return nil, nil
	}
//hmac对象对写入数据的运算,生成的参数为字节	
intermediary := hmac.Sum(nil)

Example of distance using HMAC algorithm with golang

package main

import (
    "crypto/hmac"
    "crypto/md5"
    "crypto/sha1"
    "encoding/hex"
    "fmt"
)

func Md5(data string) string {
    md5 := md5.New()
    md5.Write([]byte(data))
    md5Data := md5.Sum([]byte(""))
    return hex.EncodeToString(md5Data)
}

func Hmac(key, data string) string {
    hmac := hmac.New(md5.New, []byte(key))
    hmac.Write([]byte(data))
    return hex.EncodeToString(hmac.Sum(nil)
}

func Sha1(data string) string {
    sha1 := sha1.New()
    sha1.Write([]byte(data))
    return hex.EncodeToString(sha1.Sum(nil))
}

func main() {
    fmt.Println(Md5("hello"))
    fmt.Println(Hmac("key2", "hello"))
    fmt.Println(Sha1("hello"))
}

2. Create a public and private key based on the private key

2.1 Create public and private key function code based on private key

  • Define the private key structure, and return the structure corresponding to the public key according to the private key
// ToPubKey 返回与此私钥对应的公钥
func (p *PrivateKey) ToPubKey() *PublicKey {
	return (*PublicKey)(&p.PublicKey)
}
  • Function code to create public key from private key
// PrivKeyFromBytes 根据私钥随机数D返回公私钥
func PrivKeyFromBytes(curve elliptic.Curve, pk []byte) (*PrivateKey, *PublicKey) {
	// 根据字节pk,返回x,y
	x, y := curve.ScalarBaseMult(pk)
	// 定义一个私钥结构体,结构体中包含:公钥结构体
	priv := &PrivateKey{
		PublicKey: e.PublicKey{
			Curve: curve,
			X:     x,
			Y:     y,
		},
		D: new(big.Int).SetBytes(pk),
	}
	return priv, (*PublicKey)(&priv.PublicKey)
}

Receive parameters:

  • curve elliptic.Curve: elliptic curve encryption algorithm
  • pk[]byte: private key bytes

Return parameter:

  • PrivateKey: ECDSA private key
  • PublicKey: ECDSA public key

2.2 PrivKeyFromBytes creates a private key and public key pair

Returns the "curve"'s private and public keys based on the private key passed as a byte slice as an argument.

We should know that a public key can be generated from a private key. So owning the private key is equivalent to owning the entire key pair.

*ecdsa.PrivateKey is the structure of PublicKey and PrivateKey. This is also the function that retrieves the key pair from the raw bytes PrivateKey.

3. Transfer address based on public key

3.1 Main function code

  • define structure
// Address 表示20字节地址
type Address [AddressLength]byte
  • Main function PubkeyToAddress()
// PubkeyToAddress 公钥转地址方法
func (p *PublicKey) ToAddress() Address {
	pubBytes := p.FromECDSAPub()
	i := sha3.Keccak256(pubBytes[1:])[12:]
	return BytesToAddress(i)
}

3.2 Subfunction code

  • Subfunction FromECDSAPub()
// FromECDSAPub 椭圆加密公钥转坐标
func (p *PublicKey) FromECDSAPub() []byte {
	if p == nil || p.X == nil || p.Y == nil {
		return nil
	}
	return elliptic.Marshal(S256(), p.X, p.Y)
}

// S256()是特定的椭圆曲线,在程序启动的时候进行初始化,后来调用只是获取其引用而已。

// elliptic.Marshal(...)为标准库函数,按照非压缩形式得到相应的[]byte
func Marshal(curve Curve, x, y *big.Int) []byte {
	byteLen := (curve.Params().BitSize + 7) / 8

	ret := make([]byte, 1+2*byteLen)
	ret[0] = 4 // uncompressed point

	x.FillBytes(ret[1 : 1+byteLen])
	y.FillBytes(ret[1+byteLen : 1+2*byteLen])

	return ret
}
  • Subfunction Keccak256()
// Keccak256 使用sha3 256加密内容
func Keccak256(data ...[]byte) []byte {
	d := NewKeccak256()
	for _, b := range data {
		d.Write(b)
	}
	return d.Sum(nil)
}

Perform sha3 processing on other byte arrays except the sign bit (the first byte).
// The result of sha3 has a total of 32 bytes.
// Take the last 20 bytes of the sha3 result and generate the address. In Ethereum, the address is: type Address [AddressLength]byte

  • Subfunction BytesToAddress()
 // BytesToAddress其实就是字节拷贝
// BytesToAddress byte转address
func BytesToAddress(b []byte) Address {
	var a Address
	a.SetBytes(b)
	return a
}


// SetBytes 将地址设置为b的值。如果b大于len(a),会宕机
// SetBytes()考虑到了字节数量不匹配的情况
func (a *Address) SetBytes(b []byte) {
	if len(b) > len(a) {
		b = b[len(b)-AddressLength:]
	}
	copy(a[AddressLength-len(b):], b)
}

Guess you like

Origin blog.csdn.net/cljdsc/article/details/122692601