ECDSA in Go — A Simple Introduction

Luka Giorgadze
3 min readSep 4, 2023

--

The Elliptic Curve Digital Signature Algorithm (ECDSA) is an algorithm that many might find complex. However, it’s essential for various cryptographic operations. Today, let’s break down ECDSA, its advantages, and a straightforward example using Go.

1. Use Cases of ECDSA:

  1. Web Security: ECDSA provides robust security measures for web traffic, ensuring that the data being transferred between users and servers is safe.
  2. Cryptocurrencies: Many modern digital currencies, like Bitcoin, use ECDSA for their transaction signature algorithms. It allows for secure and verified transactions.
  3. Secure Email: ECDSA can be utilized in digital signatures for emails, making sure the recipient can verify the sender’s identity.
  4. Software Authenticity: Software developers can use ECDSA to sign their applications, ensuring users that the software has not been tampered with since its creation.

2. ECDSA: Pros and Cons

Pros:

  • Security: For the same level of security, ECDSA requires shorter key lengths compared to other algorithms, which means faster processing and less storage.
  • Efficiency: Due to its shorter key size, it is faster and consumes less power, making it ideal for devices with limited resources.
  • Compatibility: ECDSA is now widely supported and has become a standard in modern cryptography practices.

Cons:

  • Complexity: The underlying mathematics of elliptic curves can be quite complex, making the initial understanding and implementation a bit challenging.
  • Vulnerabilities: Like all cryptographic algorithms, vulnerabilities can be found over time. Proper and secure implementation is crucial.

Example

Here’s a gist, which shows a simple implementation of ECDSA in Go:

package ec

import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/md5"
"crypto/rand"
"crypto/x509"
"encoding/pem"
"errors"
"io"
"reflect"
)

// Elliptic Curve Cryptography (ECC) is a key-based technique for encrypting data.
// ECC focuses on pairs of public and private keys for decryption and encryption of web traffic.
// ECC is frequently discussed in the context of the Rivest–Shamir–Adleman (RSA) cryptographic algorithm.
// EllipticCurve data struct
type EllipticCurve struct {
pubKeyCurve elliptic.Curve // http://golang.org/pkg/crypto/elliptic/#P256
privateKey *ecdsa.PrivateKey
publicKey *ecdsa.PublicKey
}

// New EllipticCurve instance
func New(curve elliptic.Curve) *EllipticCurve {
return &EllipticCurve{
pubKeyCurve: curve,
privateKey: new(ecdsa.PrivateKey),
}
}

// GenerateKeys EllipticCurve public and private keys
func (ec *EllipticCurve) GenerateKeys() (privKey *ecdsa.PrivateKey, pubKey *ecdsa.PublicKey, err error) {

privKey, err = ecdsa.GenerateKey(ec.pubKeyCurve, rand.Reader)

if err == nil {
ec.privateKey = privKey
ec.publicKey = &privKey.PublicKey
}

return
}

// EncodePrivate private key
func (ec *EllipticCurve) EncodePrivate(privKey *ecdsa.PrivateKey) (key string, err error) {

encoded, err := x509.MarshalECPrivateKey(privKey)

if err != nil {
return
}
pemEncoded := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: encoded})

key = string(pemEncoded)

return
}

// EncodePublic public key
func (ec *EllipticCurve) EncodePublic(pubKey *ecdsa.PublicKey) (key string, err error) {

encoded, err := x509.MarshalPKIXPublicKey(pubKey)

if err != nil {
return
}
pemEncodedPub := pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: encoded})

key = string(pemEncodedPub)
return
}

// DecodePrivate private key
func (ec *EllipticCurve) DecodePrivate(pemEncodedPriv string) (privateKey *ecdsa.PrivateKey, err error) {
blockPriv, _ := pem.Decode([]byte(pemEncodedPriv))

x509EncodedPriv := blockPriv.Bytes

privateKey, err = x509.ParseECPrivateKey(x509EncodedPriv)

return
}

// DecodePublic public key
func (ec *EllipticCurve) DecodePublic(pemEncodedPub string) (publicKey *ecdsa.PublicKey, err error) {
blockPub, _ := pem.Decode([]byte(pemEncodedPub))

x509EncodedPub := blockPub.Bytes

genericPublicKey, err := x509.ParsePKIXPublicKey(x509EncodedPub)
publicKey = genericPublicKey.(*ecdsa.PublicKey)

return
}

// VerifySignature sign ecdsa style and verify signature
func (ec *EllipticCurve) VerifySignature(privKey *ecdsa.PrivateKey, pubKey *ecdsa.PublicKey) (signature []byte, ok bool, err error) {

h := md5.New()

_, err = io.WriteString(h, "This is a message to be signed and verified by ECDSA!")
if err != nil {
return
}
signhash := h.Sum(nil)

r, s, serr := ecdsa.Sign(rand.Reader, privKey, signhash)
if serr != nil {
return []byte(""), false, serr
}

signature = r.Bytes()
signature = append(signature, s.Bytes()...)

ok = ecdsa.Verify(pubKey, signhash, r, s)

return
}

// Can be used in _test.go
// Test encode, decode and test it with deep equal
func (ec *EllipticCurve) Test(privKey *ecdsa.PrivateKey, pubKey *ecdsa.PublicKey) (err error) {

encPriv, err := ec.EncodePrivate(privKey)
if err != nil {
return
}
encPub, err := ec.EncodePublic(pubKey)
if err != nil {
return
}
priv2, err := ec.DecodePrivate(encPriv)
if err != nil {
return
}
pub2, err := ec.DecodePublic(encPub)
if err != nil {
return
}

if !reflect.DeepEqual(privKey, priv2) {
err = errors.New("private keys do not match")
return
}
if !reflect.DeepEqual(pubKey, pub2) {
err = errors.New("public keys do not match")
return
}

return
}

https://gist.github.com/LukaGiorgadze/85b9e09d2008a03adfdfd5eea5964f93

--

--