Creating a Blockchain: Part 3 - Crypto key pairs & signature

creating private key, public key, signature and address

Creating a Blockchain: Part 3 - Crypto key pairs & signature

Private key and public key

Creating a crypto package which will have all keys and signature-related functionalities.

mkdir crypto

crypto/keypair.go

package crypto

import "crypto/ecdsa"

type PrivateKey struct {
    key *ecdsa.PrivateKey
}

func GeneratePrivateKey() PrivateKey{
    key, err := ecdsa.GenerateKey(elliptic.P256(),rand.Reader)
    if err != nil{
        panic(err)
    }
    return PrivateKey{
        key: key,
    }
}

type PublicKey struct {
    key *ecdsa.PublicKey
}

ECDSA stands for Elliptic Curve Digital Signature Algorithm. It is a widely used public-key cryptography algorithm that provides a method for creating digital signatures.

The GeneratePrivateKey function in the crypto package generates a new ECDSA private key


Address and methods

The Address type in the types package represents a 20-byte array commonly used as a cryptographic address.

ToSlice method converts the Address to a byte slice.

String method returns the hexadecimal representation of the address.

AddressFromBytes function constructs an Address from a given byte slice.

types/address.go

package types

import (
    "encoding/hex"
    "fmt"
)

type Address [20]uint8

func (a Address) ToSlice() []byte {
    b := make([]byte, 20)
    for i := 0; i < 20; i++ {
        b[i] = a[i]
    }
    return b
}

func (a Address) String() string {
    return hex.EncodeToString(a.ToSlice())
}

func AddressFromBytes(b []byte) Address {
    if len(b) != 20{
        msg := fmt.Sprintf("given bytes with length %d should be 20",len(b))
        panic(msg)
    }
    var value [20]uint8
    for i := 0; i < 20; i++ {
        value[i] = b[i]
    }
    return Address(value)
}

Generate keys & address

Adding more functions to keypairs,

  • The PublicKey method of the PrivateKey struct returns the corresponding public key as a PublicKey struct, allowing access to the public key associated with a private key.

  • The ToSlice method of the PublicKey struct converts the compressed form of the public key.

  • The Address method of the PublicKey struct generates a cryptographic address by taking the SHA-256 hash of the compressed public key and returning the last 20 bytes as an instance of the types.Address type.

crypto/keypair.go

package crypto

import (
    "ProjectX/types"
    "crypto/ecdsa"
    "crypto/elliptic"
    "crypto/rand"
    "crypto/sha256"
)

type PrivateKey struct {
    key *ecdsa.PrivateKey
}

func GeneratePrivateKey() PrivateKey{
    key, err := ecdsa.GenerateKey(elliptic.P256(),rand.Reader)
    if err != nil{
        panic(err)
    }
    return PrivateKey{
        key: key,
    }
}

func (p PrivateKey) PublicKey() PublicKey{
    return PublicKey{
        key: &p.key.PublicKey,
    }
}

type PublicKey struct {
    key *ecdsa.PublicKey
}

func (k PublicKey) ToSlice() []byte{
    return elliptic.MarshalCompressed(k.key,k.key.X,k.key.Y)
}

func (k PublicKey) Address() types.Address {
    h:= sha256.Sum256(k.ToSlice())
    return types.AddressFromBytes(h[len(h)-20:])
}

Test generate key

crypto/keypair_test.go

package crypto

import (
    "fmt"
    "testing"
)

func TestGeneratePrivateKey(t *testing.T) {
    prKey := GeneratePrivateKey()
    pbKey := prKey.PublicKey()
    address := pbKey.Address()

    fmt.Println("address",address)
}


Signature

The Signature struct represents an ECDSA digital signature and is defined with two fields:

  • r: This field is a pointer to a big.Int represents one of the two components of the ECDSA signature.

  • s: This field is also a pointer to a big.Int represents the other component of the ECDSA signature.

In the context of ECDSA (Elliptic Curve Digital Signature Algorithm), a digital signature is generated using a private key and can be verified using the corresponding public key. The signature consists of two components, r and s, and their combination provides cryptographic assurance of the authenticity and integrity of the signed data.

  • The Sign method generates a digital signature for the given data using the private key.

  • The Verify method verifies the digital signature against the provided public key and data.

crypto/keypair.go

type Signature struct {
    r,s *big.Int
}

func (k PrivateKey) Sign(data []byte) (*Signature, error) {
    r,s, err := ecdsa.Sign(rand.Reader, k.key,data)

    if err != nil {
        return nil, err
    }

    return &Signature{
        r:r,
        s:s,
    }, nil
}

func (sig Signature) Verify(pubKey PublicKey,data []byte) bool{
    return ecdsa.Verify(pubKey.key, data, sig.r, sig.s)
}

Test sign and verify Signature

  • The TestKeypairSignVerifySuccess function tests the successful signing and verification of a message. It generates a private key, derives the corresponding public key, signs a message ("hello world"), and asserts that the signature is valid when verified using the public key.

  • The TestKeypairSignVerifyFail function tests the failure cases for signature verification. It signs a message with one private key, and then attempts to verify the signature using a different public key and a modified message, asserting that the verification fails in both cases.

crypto/keypair_test.go

func TestKeypairSignVerifySuccess(t *testing.T) {
    privKey := GeneratePrivateKey()
    publicKey := privKey.PublicKey()
    msg := []byte("hello world")

    sig, err := privKey.Sign(msg)
    assert.Nil(t, err)

    assert.True(t, sig.Verify(publicKey, msg))
}

func TestKeypairSignVerifyFail(t *testing.T) {
    privKey := GeneratePrivateKey()
    publicKey := privKey.PublicKey()
    msg := []byte("hello world")

    sig, err := privKey.Sign(msg)
    assert.Nil(t, err)

    otherPrivKey := GeneratePrivateKey()
    otherPublicKey := otherPrivKey.PublicKey()

    assert.False(t, sig.Verify(otherPublicKey, msg))
    assert.False(t, sig.Verify(publicKey, []byte("xxxxxx")))
}

We succeeded to sign and verify data using private key

The following blog post will explore the code related to Block and TX signing and verification✨


In this blog series, I'll be sharing code snippets related to blockchain architecture. While the code will be available on my GitHub, I want to highlight that the entire architecture isn't solely my own. I'm learning as I go, drawing inspiration and knowledge from various sources, including a helpful YouTube playlist that has contributed to my learning process.

Did you find this article valuable?

Support Siddharth Patel by becoming a sponsor. Any amount is appreciated!