6. Adding Digital Signatures
지갑에 디지털 서명을 추가해보자.
TxInput에 wallet의 private key를 이용해서 sign을 하고 검증하는 과정을 추가.
Sign
wallet의 privateKey와 해당 TxInput이 참조하는 TxOutput의 public key hash, random number를 섞어서 ecdsa를 이용해 만든다.
esdsa 함수를 이용하면 r, s 2개의 수가 나오는데 이 두 수를 바이트로 변환해서 합하면 signature가 된다.
이 과정을 반대로 하여 r, s를 뽑아내어 비교하는 것이 verify 함수다.
wallet.go
address 검증 함수 추가.
package wallet
import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"fmt"
"log"
"golang.org/x/crypto/ripemd160"
)
const (
// checksum length in byte.
// 4byte length
checksumLength = 4
// 0 byte를 16진법으로 표현.
version = byte(0x00)
)
// Wallet : PrivateKey와 PublicKey를 가지고 있는 Wallet structure.
type Wallet struct {
PrivateKey ecdsa.PrivateKey
PublicKey []byte
}
// Address : version, publicKeyHash, checksum 3가지를 concatenate한 후에 Base58로 인코딩해서 address를 만든다.
func (w Wallet) Address() []byte {
pubHash := PublicKeyHash(w.PublicKey)
versionedHash := append([]byte{version}, pubHash...)
checksum := Checksum(versionedHash)
fullHash := append(versionedHash, checksum...)
address := Base58Encode(fullHash)
fmt.Printf("pub key: %x\n", w.PublicKey)
fmt.Printf("pub hash: %x\n", pubHash)
fmt.Printf("address: %s\n", address)
fmt.Printf("fullHash: %x\n", fullHash)
fmt.Printf("checksum: %x\n", checksum)
fmt.Printf("version: %x\n", versionedHash)
return address
}
// Address: 1EwsppsVck2B5Ndf7nPyHi8uYtEhK44ndm
// FullHash: 0098fa85f9e6b04827b2ce6db7e8e18ff8e9882c99eefb8610
// [Version] 00
// [Pub Key Hash] 98fa85f9e6b04827b2ce6db7e8e18ff8e9882c99
// [CheckSum] eefb8610
// FullHash = Version + Pub Key Hash + CheckSum
// ValidateAddress : Address의 유효성을 검증한다.
// 1 : address의 checksum 부분 (address를 base58로 디코드하여 나온 배열에서 마지막 checksumLength만큼의 부분)
// 2 : address의 version부분과 pubKeyHash 부분을 뽑아서 만든 checksum 부분.
// 1과 2가 같은지 확인.
func ValidateAddress(address string) bool {
// 1 : address의 checksum 부분
pubKeyHash := Base58Decode([]byte(address))
actualChecksum := pubKeyHash[len(pubKeyHash)-checksumLength:]
// 2 : address의 version부분과 pubKeyHash 부분을 뽑아서 만든 checksum 부분.
version := pubKeyHash[0]
pubKeyHash = pubKeyHash[1 : len(pubKeyHash)-checksumLength]
targetChecksum := Checksum(append([]byte{version}, pubKeyHash...))
return bytes.Compare(actualChecksum, targetChecksum) == 0
}
// NewKeyPair : 새로운 키페어를 만든다.
// ecdsa라는 비대칭키 암호 알고리즘을 사용.
func NewKeyPair() (ecdsa.PrivateKey, []byte) {
curve := elliptic.P256()
// private key는 랜덤하게 뽑혀진 256bit의 숫자이다.
private, err := ecdsa.GenerateKey(curve, rand.Reader)
if err != nil {
log.Panic(err)
}
// X, Y가 concatenate 되어 pub이 됨.
// private key로부터 public key를 생성함.
pub := append(private.PublicKey.X.Bytes(), private.PublicKey.Y.Bytes()...)
return *private, pub
}
// MakeWallet : NewKeyPair를 이용해서 Wallet을 만든다.
func MakeWallet() *Wallet {
private, public := NewKeyPair()
wallet := Wallet{private, public}
return &wallet
}
// PublicKeyHash : sha256, ripemd160을 이용해 퍼블릭키를 해쉬로 변환.
// address 생성에 쓰임.
func PublicKeyHash(pubKey []byte) []byte {
pubHash := sha256.Sum256(pubKey)
hasher := ripemd160.New()
_, err := hasher.Write(pubHash[:])
if err != nil {
log.Panic(err)
}
publicRipMD := hasher.Sum(nil)
return publicRipMD
}
// Checksum : payload를 sha256을 이용해 해쉬로 변환하고 checksumLength만큼의 바이트만 사용한다.
func Checksum(payload []byte) []byte {
firstHash := sha256.Sum256(payload)
secondHash := sha256.Sum256(firstHash[:])
return secondHash[:checksumLength]
}
transaction.go
sign과 verify 함수 구현.
새로운 tx 생성시 sign과정 추가.
tx.go
blockchain.go
기존의 address로 TxInput, TxOutput을 검증하던 것을 pubKeyHash로 검증한다.
Result
Last updated
Was this helpful?