elliptic - elliptic curve digital signature algorithm (ECDSA) cryptography with OpenSSL made easy (incl. secp256k1 curve)
Usage
Intro
Did you know? All you need to open up a new account on a blockchain
is an (unsigned) 256-bit / 32-byte integer number.
Yes, that's it. No questions asked.
The private key is the secret "magic" that unlocks your own bank.
Q: What's the maximum value for a 256-bit / 32-byte integer number
(hint 2^256-1)?
Maximum value of 2^256-1 =
2**256-1
(2**256-1).digits.size
Yes, that's 78 (!) decimal digits.
Let's (re)try the maximum value for a 256-bit (32-byte) integer number
in hexadecimal (base 16) and binary (base 2) format?
(2**256-1).to_s(16)
(2**256-1).digits(16).size
(2**256-1).to_s(2)
(2**256-1).digits(2).size
Surprise - a 256-bit number has 256 binary digits (0 and 1s).
BEWARE - Blockchain Bandits!
If you use a low integer number e.g. 1, 2, etc.
your account is guaranteed to get robbed by blockchain bandits in
seconds.
(See A "Blockchain Bandit" Is Guessing Private Keys and Scoring Millions
by Andy Greenberg, Wired Magazine, April 2019)
Private Key
An ECDSA (Elliptic Curve Digital Signature Algorithm) private key is a random number between 1 and the order of the elliptic curve group.
require 'elliptic'
private_key = EC::PrivateKey.generate
private_key.to_i
private_key.to_s
Derive / (Auto-)Calculate the Public Key - Enter Elliptic Curve (EC) Cryptography
The public key (K
) are two numbers (that is, a point with the coordinates x and y) computed by multiplying
the generator point (G
) of the curve with the private key (k
) e.g. K=k*G
.
This is equivalent to adding the generator to itself k
times.
Magic?
Let's try:
private_key = EC::PrivateKey.new( 1234 )
public_key = private_key.public_key
point = public_key.point
point.x
point.y
point.x.to_s(16)
point.y.to_s(16)
Sign & Verify Transactions
Sign a transaction with an (elliptic curve) private key:
tx = 'from: Alice to: Bob cryptos: 43_000_000_000'
txhash = Digest::SHA256.digest( tx )
private_key = EC::PrivateKey.new( 1234 )
signature = private_key.sign( txhash )
signature = EC.sign( txhash, private_key )
signature.r
signature.s
signature.r.to_s(16)
signature.s.to_s(16)
Verify a signed transaction with an (elliptic curve) public key:
tx = 'from: Alice to: Bob cryptos: 43_000_000_000'
txhash = Digest::SHA256.digest( tx )
public_key = EC::PublicKey.new(
102884003323827292915668239759940053105992008087520207150474896054185180420338,
49384988101491619794462775601349526588349137780292274540231125201115197157452
)
signature = EC::Signature.new(
80563021554295584320113598933963644829902821722081604563031030942154621916407,
58316177618967642068351252425530175807242657664855230973164972803783751708604
)
public_key.verify?( txhash, signature )
EC.verify?( txhash, signature, public_key )
public_key = EC::PublicKey.new(
0xe37648435c60dcd181b3d41d50857ba5b5abebe279429aa76558f6653f1658f2,
0x6d2ee9a82d4158f164ae653e9c6fa7f982ed8c94347fc05c2d068ff1d38b304c
)
signature = EC::Signature.new(
0x3306a2f81ad2b2f62ebe0faec129545bc772babe1ca5e70f6e56556b406464c0,
0x4fe202bb0835758f514cd4a0787986f8f6bf303df629dc98c5b1a438a426f49a
)
public_key.verify?( txhash, signature )
EC.verify?( txhash, signature, public_key )
To sum up:
- The (raw) private key is a 256-bit unsigned integer number
- The (raw) public key is a point (x,y), that is, two 256-bit unsigned integer numbers - derived (calculated) from the private key
- A (raw) signature is composed of (r,s), that is, two 256-bit unsigned integer numbers
That's all the magic.
Private / Public Key Formats
Intro
To get the all-in-one-string
public key from a point with the coordinates x and y
use the
Standards for Efficient Cryptography (SEC) 1) uncompressed format
or the 2) compressed format:
prefix = '04'
pubkey = prefix + "%064x" % point.x + "%064x" % point.y
prefix = point.y % 2 == 0 ? '02' : '03'
pubkey = prefix + "%064x" % point.x
or use the builtin helpers:
point.to_s
point.to_s( :compressed )
PEM, DER, BASE64(DER)
Export
To export a private or public key to
the Privacy Enhanced Mail (PEM) format use to_pem
:
private_key = EC::PrivateKey.generate
private_key.to_pem
public_key = private_key.public_key
public_key.to_pem
To export a private or public key to
the (binary) Distinguished Encoding Rules (DER)
in Abstract Syntax Notation One (ASN.1) format use to_der
:
private_key.to_der
public_key = private_key.public_key
public_key.to_der
To export a private or public key to
the Base64-encoded Distinguished Encoding Rules (DER)
in Abstract Syntax Notation One (ASN.1) format use to_base64
:
private_key.to_base64
public_key = private_key.public_key
public_key.to_base64
Import
To import a private or public key
in the PEM or DER format use the all-in-one convenience constructor:
private_key = EC::PrivateKey.new( "-----BEGIN EC PRIVATE KEY-----
MHQCAQEEIDIWkCIC58Yo1E5noSiXbHdR/8zUqB+vvTK4nSk8tZ1RoAcGBSuBBAAK
oUQDQgAEoll8rYerfDH4q6nT1miTZZ315a8BFsKA13Z8Zif5Mh+qavIr/6HpI/Kq
Q0bnuOZiCD9gpEIWo7VGN8wJgcu6ZA==
-----END EC PRIVATE KEY-----" )
public_key = EC::PublicKey.new( "-----BEGIN PUBLIC KEY-----
MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEoll8rYerfDH4q6nT1miTZZ315a8BFsKA
13Z8Zif5Mh+qavIr/6HpI/KqQ0bnuOZiCD9gpEIWo7VGN8wJgcu6ZA==
-----END PUBLIC KEY-----" )
private_key = EC::PrivateKey.new( "\xA1D\x03B\x00\x04\xA2Y|\xAD\x87\xAB|1
\xF8\xAB\xA9\xD3\xD6h\x93e\x9D\xF5\xE5\xAF\x01\x16\xC2\x80
\xD7v|f'\xF92\x1F\xAAj\xF2+\xFF\xA1\xE9#\xF2\xAACF\xE7\xB8
\xE6b\b?`\xA4B\x16\xA3\xB5F7\xCC\t\x81\xCB\xBAd".b )
public_key = EC::PublicKey.new( "0V0\x10\x06\a*\x86H\xCE=\x02\x01\x06\x05+\x81\x04\x00
\x03B\x00\x04\xA2Y|\xAD\x87\xAB|1\xF8\xAB\xA9
\xD3\xD6h\x93e\x9D\xF5\xE5\xAF\x01\x16\xC2\x80\xD7v|f'\xF92
\x1F\xAAj\xF2+\xFF\xA1\xE9#\xF2\xAACF\xE7\xB8\xE6b
\b?`\xA4B\x16\xA3\xB5F7\xCC\t\x81\xCB\xBAd".b )
or use the decode/from helper:
private_key = EC::PrivateKey.decode_pem( ... )
EC::PrivateKey.decode_der( ... )
EC::PrivateKey.decode_base64( ... )
public_key = EC::PublicKey.decode_pem( ... )
EC::PublicKey.decode_der( ... )
EC::PublicKey.decode_base64( ... )
That's it.
Aside - Elliptic What?
Elliptic-curve cryptography (ECC) is
an approach to public-key cryptography based
on the algebraic structure of elliptic curves over finite fields.
(Source: Elliptic-curve cryptography @ Wikipedia)
What's an Elliptic Curve?
This is a graph of secp256k1's elliptic curve y² = x³ + 7
over the real numbers.
Note that because secp256k1 is actually defined over the field Zₚ,
its graph will in reality look like random scattered points,
not anything like this.
(Source: Secp256k1 @ Bitcoin Wiki)
Bitcon Public Service Announcement:
If we all buy Bitcoin from one another at ever higher
prices we'll all be rich beyond our wildest dreams.
-- Trolly McTrollface
BEWARE: Yes, Bitcoin Is a Ponzi - Learn How the Investment Fraud Works »
Install
Just install the gem:
$ gem install elliptic
License
The scripts are dedicated to the public domain.
Use it as you please with no restrictions whatsoever.
Send them along to the wwwmake forum.
Thanks!