elliptic 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +18 -13
- data/lib/elliptic.rb +102 -50
- data/lib/elliptic/version.rb +1 -1
- data/test/test_sign.rb +25 -25
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 50c1f0afa4d55fd9b936ddc1ef0c38ede63054432aae99aa2b717ba21bc819c7
|
4
|
+
data.tar.gz: e9d57e5bfece39941288e66fd9b087d99660dfa25a42e723b69f96f85c690fd0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 56cc6e7d72f921b61701d6ca2d1d25383b91377607752db4f96a7d12c56881fdfda1bcccfe2177ff8695114de81822ea9fbc8b0ccecfa1546f4c678393b2b933
|
7
|
+
data.tar.gz: c8ec4acab2b3495b8937bee4e7488d6d3922b31a6ec13473a877ea30249319e0c8eacc4c89062cd76d3e1a4c078c95d36579b2b420b2a55e931ace6be9b69692
|
data/README.md
CHANGED
@@ -73,10 +73,10 @@ An ECDSA (Elliptic Curve Digital Signature Algorithm) private key is a random nu
|
|
73
73
|
``` ruby
|
74
74
|
require 'elliptic'
|
75
75
|
|
76
|
-
#
|
77
|
-
|
76
|
+
# Auto-generate (random) private key
|
77
|
+
private_key = EC::PrivateKey.generate # by default uses Secp256k1 curve (used in Bitcoin and Ethereum)
|
78
78
|
|
79
|
-
|
79
|
+
private_key.to_i
|
80
80
|
#=> 72190737707147846840353520312904745954595478835413056312168022784020322830309
|
81
81
|
```
|
82
82
|
|
@@ -92,11 +92,10 @@ Let's try:
|
|
92
92
|
|
93
93
|
``` ruby
|
94
94
|
# This private key is just an example. It should be much more secure!
|
95
|
-
|
95
|
+
private_key = EC::PrivateKey.new( 1234 ) # by default uses Secp256k1 curve (used in Bitcoin and Ethereum)
|
96
96
|
|
97
|
-
|
98
|
-
|
99
|
-
point = algo.public_key ## the "magic" one-way K=k*G curve multiplication (K=public key,k=private key, G=generator point)
|
97
|
+
public_key = private_key.public_key ## the "magic" one-way K=k*G curve multiplication (K=public key,k=private key, G=generator point)
|
98
|
+
point = public_key.point
|
100
99
|
|
101
100
|
point.x
|
102
101
|
#=> 102884003323827292915668239759940053105992008087520207150474896054185180420338
|
@@ -120,10 +119,12 @@ tx = 'from: Alice to: Bob cryptos: 43_000_000_000'
|
|
120
119
|
txhash = Digest::SHA256.digest( tx )
|
121
120
|
|
122
121
|
# Step 2 - Get the Signer's Private key
|
123
|
-
|
122
|
+
private_key = EC::PrivateKey.new( 1234 ) # This private key is just an example. It should be much more secure!
|
124
123
|
|
125
124
|
# Sign!
|
126
|
-
signature =
|
125
|
+
signature = private_key.sign( txhash )
|
126
|
+
# -or-
|
127
|
+
signature = EC.sign( txhash, private_key )
|
127
128
|
|
128
129
|
signature.r
|
129
130
|
#=> 80563021554295584320113598933963644829902821722081604563031030942154621916407
|
@@ -145,7 +146,7 @@ tx = 'from: Alice to: Bob cryptos: 43_000_000_000'
|
|
145
146
|
txhash = Digest::SHA256.digest( tx )
|
146
147
|
|
147
148
|
# Step 2 - Get the Signer's Public Key
|
148
|
-
|
149
|
+
public_key = EC::PublicKey.new(
|
149
150
|
102884003323827292915668239759940053105992008087520207150474896054185180420338,
|
150
151
|
49384988101491619794462775601349526588349137780292274540231125201115197157452
|
151
152
|
)
|
@@ -157,13 +158,15 @@ signature = EC::Signature.new(
|
|
157
158
|
)
|
158
159
|
|
159
160
|
# Don't Trust - Verify
|
160
|
-
|
161
|
+
public_key.verify?( txhash, signature )
|
162
|
+
# -or-
|
163
|
+
EC.verify?( txhash, signature, public_key )
|
161
164
|
#=> true
|
162
165
|
|
163
166
|
|
164
167
|
# or using hexadecimal numbers
|
165
168
|
|
166
|
-
|
169
|
+
public_key = EC::PublicKey.new(
|
167
170
|
0xe37648435c60dcd181b3d41d50857ba5b5abebe279429aa76558f6653f1658f2,
|
168
171
|
0x6d2ee9a82d4158f164ae653e9c6fa7f982ed8c94347fc05c2d068ff1d38b304c
|
169
172
|
)
|
@@ -173,7 +176,9 @@ signature = EC::Signature.new(
|
|
173
176
|
0x4fe202bb0835758f514cd4a0787986f8f6bf303df629dc98c5b1a438a426f49a
|
174
177
|
)
|
175
178
|
|
176
|
-
|
179
|
+
public_key.verify?( txhash, signature )
|
180
|
+
# -or-
|
181
|
+
EC.verify?( txhash, signature, public_key )
|
177
182
|
#=> true
|
178
183
|
```
|
179
184
|
|
data/lib/elliptic.rb
CHANGED
@@ -66,8 +66,7 @@ module EC
|
|
66
66
|
|
67
67
|
## encoded_point is the octet string representation of the point.
|
68
68
|
## This must be either a String or an OpenSSL::BN
|
69
|
-
|
70
|
-
hex = prefix + ("%064x" % @x) + ("%064x" % @y)
|
69
|
+
hex = '04' + ("%064x" % @x) + ("%064x" % @y)
|
71
70
|
bin = [hex].pack( 'H*' )
|
72
71
|
|
73
72
|
ec_group = GROUP[ group || 'secp256k1' ]
|
@@ -89,62 +88,54 @@ module EC
|
|
89
88
|
end
|
90
89
|
|
91
90
|
## return OpenSSL::PKey::EC::Point - find a better name? e.g. to_raw/native or such - why? why not?
|
92
|
-
def
|
91
|
+
def to_ec_point() @pt; end
|
93
92
|
end
|
94
93
|
|
95
94
|
|
96
95
|
|
97
96
|
|
98
|
-
class
|
99
|
-
def
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
point = input.is_a?( Point ) ? input.to_point : input
|
106
|
-
|
107
|
-
## note: (auto)get group from point
|
108
|
-
@pkey = OpenSSL::PKey::EC.new( point.group )
|
109
|
-
@pkey.public_key = point
|
110
|
-
else ## case 2) build a private/public key pair
|
111
|
-
|
112
|
-
ec_group = GROUP[ group || 'secp256k1' ]
|
113
|
-
@pkey = OpenSSL::PKey::EC.new( ec_group )
|
114
|
-
|
115
|
-
if input.nil? ## auto-generate new key
|
116
|
-
@pkey.generate_key
|
117
|
-
else
|
118
|
-
num = if input.is_a?( String )
|
119
|
-
input.to_i( 16 )
|
120
|
-
else ## assume (big) integer
|
121
|
-
input
|
122
|
-
end
|
123
|
-
@pkey.private_key = OpenSSL::BN.new( num )
|
124
|
-
## auto-calculate public key too
|
125
|
-
@pkey.public_key = @pkey.group.generator.mul( @pkey.private_key )
|
126
|
-
end
|
97
|
+
class PublicKey
|
98
|
+
def self.convert( *args, **kwargs )
|
99
|
+
if args.size==1 && args[0].is_a?( PublicKey )
|
100
|
+
args[0] ## pass through as is (already a public key)
|
101
|
+
else
|
102
|
+
new( *args, group: kwargs[:group] )
|
127
103
|
end
|
128
104
|
end
|
129
105
|
|
130
106
|
|
131
|
-
def group() @pkey.group; end
|
132
107
|
|
133
|
-
def
|
134
|
-
|
108
|
+
def initialize( *args, group: nil )
|
109
|
+
point = if args.size == 2 ## assume (x,y) raw integer points
|
110
|
+
@pt = Point.new( *args, group: group )
|
111
|
+
@pt.to_ec_point ## convert point to openssl (native) class
|
112
|
+
else
|
113
|
+
## "restore" public key (only) from point for verify
|
114
|
+
## - OpenSSL::PKey::EC::Point ## assume public key only (restore pkey object for verify?)
|
115
|
+
## - Point
|
116
|
+
if args[0].is_a?( Point )
|
117
|
+
@pt = args[0]
|
118
|
+
@pt.to_ec_point
|
119
|
+
else
|
120
|
+
args[0] ## assume it is already OpenSSL::PKey::EC::Point
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
## note: (auto)get group from point
|
125
|
+
@pkey = OpenSSL::PKey::EC.new( point.group )
|
126
|
+
@pkey.public_key = point
|
127
|
+
end
|
135
128
|
|
129
|
+
def group() @pkey.group; end
|
136
130
|
|
137
|
-
## todo/check/fix: fix case with no private_key if passed in point/public key for verify!!!
|
138
|
-
def private_key() @pkey.private_key.to_i; end
|
139
|
-
alias_method :priv_key, :private_key ## add signing_key alias too?
|
140
131
|
|
141
|
-
def
|
132
|
+
def point
|
133
|
+
## cache returned point - why? why not?
|
134
|
+
@pt ||= Point.new( @pkey.public_key )
|
135
|
+
@pt
|
136
|
+
end
|
142
137
|
|
143
138
|
|
144
|
-
def sign( message )
|
145
|
-
signature_der = @pkey.dsa_sign_asn1( message )
|
146
|
-
Signature.decode_der( signature_der )
|
147
|
-
end
|
148
139
|
|
149
140
|
def verify?( message, signature )
|
150
141
|
signature_der = signature.to_der
|
@@ -152,20 +143,81 @@ module EC
|
|
152
143
|
end
|
153
144
|
alias_method :valid_signature?, :verify?
|
154
145
|
|
155
|
-
|
146
|
+
## helpers for debugging
|
156
147
|
def to_text() @pkey.to_text; end
|
157
|
-
|
148
|
+
def public?() @pkey.public?; end ## todo/check: keep - needed? - why? why not?
|
149
|
+
end # class PublicKey
|
150
|
+
|
151
|
+
|
152
|
+
|
153
|
+
|
154
|
+
class PrivateKey
|
155
|
+
def self.convert( *args, **kwargs )
|
156
|
+
if args.size==1 && args[0].is_a?( PrivateKey )
|
157
|
+
args[0] ## pass through as is (alread a private key)
|
158
|
+
else
|
159
|
+
new( args[0], group: kwargs[:group] )
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
|
164
|
+
def self.generate( group: nil ) new( group: group ); end
|
165
|
+
|
166
|
+
def initialize( input=nil, group: nil )
|
167
|
+
ec_group = GROUP[ group || 'secp256k1' ]
|
168
|
+
@pkey = OpenSSL::PKey::EC.new( ec_group )
|
169
|
+
|
170
|
+
if input.nil? ## auto-generate new key
|
171
|
+
@pkey.generate_key
|
172
|
+
else
|
173
|
+
num = if input.is_a?( String ) ## assume hex string
|
174
|
+
input.to_i( 16 )
|
175
|
+
else ## assume integer
|
176
|
+
input
|
177
|
+
end
|
178
|
+
@pkey.private_key = OpenSSL::BN.new( num )
|
179
|
+
## auto-calculate public key too
|
180
|
+
@pkey.public_key = @pkey.group.generator.mul( @pkey.private_key )
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def to_i() @pkey.private_key.to_i; end
|
185
|
+
## todo/check/fix: make it always a 32 byte (64 hex chars) string
|
186
|
+
## even with leading zeros !!! - why? why not?
|
187
|
+
def to_s() @pkey.private_key.to_i.to_s(16); end
|
188
|
+
|
189
|
+
def group() @pkey.group; end
|
190
|
+
|
191
|
+
|
192
|
+
def public_key
|
193
|
+
## cache returned public key - why? why not?
|
194
|
+
@pub ||= PublicKey.new( @pkey.public_key )
|
195
|
+
@pub
|
196
|
+
end
|
197
|
+
|
198
|
+
|
199
|
+
def sign( message )
|
200
|
+
signature_der = @pkey.dsa_sign_asn1( message )
|
201
|
+
Signature.decode_der( signature_der )
|
202
|
+
end
|
203
|
+
|
204
|
+
## helpers for debugging
|
205
|
+
def to_text() @pkey.to_text; end
|
206
|
+
def private?() @pkey.private?; end ## todo/check: keep - needed? - why? why not?
|
207
|
+
end # class PrivateKey
|
208
|
+
|
209
|
+
|
158
210
|
|
159
211
|
|
160
212
|
|
161
213
|
def self.sign( message, priv_key )
|
162
|
-
|
163
|
-
|
214
|
+
signer = PrivateKey.convert( priv_key )
|
215
|
+
signer.sign( message )
|
164
216
|
end
|
165
217
|
|
166
|
-
def self.verify?( message,
|
167
|
-
|
168
|
-
|
218
|
+
def self.verify?( message, signature, pub_key )
|
219
|
+
verifier = PublicKey.convert( pub_key )
|
220
|
+
verifier.verify?( message, signature )
|
169
221
|
end
|
170
222
|
|
171
223
|
class << self
|
data/lib/elliptic/version.rb
CHANGED
data/test/test_sign.rb
CHANGED
@@ -10,76 +10,76 @@ class TestSign < MiniTest::Test
|
|
10
10
|
|
11
11
|
|
12
12
|
def test_keys
|
13
|
-
|
14
|
-
assert_equal 1234,
|
15
|
-
assert_equal "secp256k1",
|
13
|
+
private_key = EC::PrivateKey.new( 1234 )
|
14
|
+
assert_equal 1234, private_key.to_i
|
15
|
+
assert_equal "secp256k1", private_key.group.curve_name
|
16
16
|
|
17
|
-
public_key =
|
17
|
+
public_key = private_key.public_key
|
18
18
|
assert_equal 102884003323827292915668239759940053105992008087520207150474896054185180420338,
|
19
|
-
public_key.x
|
19
|
+
public_key.point.x
|
20
20
|
|
21
21
|
assert_equal 49384988101491619794462775601349526588349137780292274540231125201115197157452,
|
22
|
-
public_key.y
|
22
|
+
public_key.point.y
|
23
23
|
|
24
24
|
assert_equal "e37648435c60dcd181b3d41d50857ba5b5abebe279429aa76558f6653f1658f2",
|
25
|
-
public_key.x.to_s(16)
|
25
|
+
public_key.point.x.to_s(16)
|
26
26
|
assert_equal "6d2ee9a82d4158f164ae653e9c6fa7f982ed8c94347fc05c2d068ff1d38b304c",
|
27
|
-
public_key.y.to_s(16)
|
27
|
+
public_key.point.y.to_s(16)
|
28
28
|
|
29
29
|
assert_equal "02e37648435c60dcd181b3d41d50857ba5b5abebe279429aa76558f6653f1658f2",
|
30
|
-
public_key.to_s( :compressed )
|
30
|
+
public_key.point.to_s( :compressed )
|
31
31
|
|
32
32
|
assert_equal "04e37648435c60dcd181b3d41d50857ba5b5abebe279429aa76558f6653f1658f26d2ee9a82d4158f164ae653e9c6fa7f982ed8c94347fc05c2d068ff1d38b304c",
|
33
|
-
public_key.to_s
|
33
|
+
public_key.point.to_s
|
34
34
|
end
|
35
35
|
|
36
36
|
def test_sign
|
37
|
-
|
38
|
-
priv_key = algo.private_key
|
37
|
+
private_key = EC::PrivateKey.new( 1234 )
|
39
38
|
|
40
39
|
message = Digest::SHA256.digest( "hello" )
|
41
40
|
|
42
41
|
## note: sign uses a secure random generated temp key
|
43
42
|
## every signature is different!!!
|
44
|
-
signature = EC.sign( message,
|
43
|
+
signature = EC.sign( message, private_key )
|
45
44
|
pp signature.to_der
|
46
45
|
|
47
|
-
signature = EC.sign( message,
|
46
|
+
signature = EC.sign( message, private_key )
|
48
47
|
pp signature.to_der
|
49
48
|
|
50
|
-
signature =
|
49
|
+
signature = private_key.sign( message )
|
51
50
|
pp signature.to_der
|
52
51
|
|
53
|
-
|
52
|
+
signature = private_key.sign( message )
|
53
|
+
pp signature.to_der
|
54
54
|
|
55
55
|
|
56
56
|
### verify
|
57
|
-
public_key =
|
57
|
+
public_key = private_key.public_key
|
58
58
|
|
59
|
-
assert EC.verify?( message,
|
60
|
-
assert EC.valid_signature?( message,
|
59
|
+
assert EC.verify?( message, signature, public_key )
|
60
|
+
assert EC.valid_signature?( message, signature, public_key )
|
61
61
|
|
62
|
-
assert
|
63
|
-
assert
|
62
|
+
assert public_key.verify?( message, signature )
|
63
|
+
assert public_key.valid_signature?( message, signature )
|
64
64
|
|
65
65
|
## public key from point (x,y)
|
66
66
|
point = EC::Point.new(
|
67
67
|
102884003323827292915668239759940053105992008087520207150474896054185180420338,
|
68
68
|
49384988101491619794462775601349526588349137780292274540231125201115197157452)
|
69
|
-
assert EC.verify?( message,
|
69
|
+
assert EC.verify?( message, signature, point )
|
70
70
|
|
71
71
|
## signature from r,s values
|
72
72
|
signature = EC::Signature.new(
|
73
73
|
17435452009115387225781053203681496505351828576563920844344513414624978140095,
|
74
74
|
57357162317582879484266879619863223941116872722080991745355058707843528515381)
|
75
|
-
assert EC.verify?( message,
|
75
|
+
assert EC.verify?( message, signature, point )
|
76
76
|
|
77
77
|
|
78
78
|
|
79
79
|
## tamper with message
|
80
80
|
message = Digest::SHA256.digest( "hellox" )
|
81
|
-
assert_equal false, EC.verify?( message,
|
82
|
-
assert_equal false,
|
81
|
+
assert_equal false, EC.verify?( message, signature, public_key )
|
82
|
+
assert_equal false, public_key.verify?( message, signature )
|
83
83
|
end
|
84
84
|
|
85
85
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: elliptic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gerald Bauer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-01-
|
11
|
+
date: 2021-01-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rdoc
|