starkbank-ecdsa 0.0.5 → 2.0.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/lib/curve.rb +94 -0
- data/lib/ecdsa.rb +37 -19
- data/lib/math.rb +167 -0
- data/lib/point.rb +19 -0
- data/lib/privatekey.rb +45 -30
- data/lib/publickey.rb +85 -14
- data/lib/signature.rb +37 -21
- data/lib/starkbank-ecdsa.rb +9 -1
- data/lib/utils/binary.rb +44 -0
- data/lib/utils/der.rb +184 -0
- data/lib/utils/file.rb +0 -6
- data/lib/utils/integer.rb +23 -0
- data/lib/utils/oid.rb +43 -0
- data/lib/utils/pem.rb +18 -0
- metadata +11 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4d4a76bc728ec00f073424e79a9e72d75d39aa863074fff03fd5c35694d221f2
|
4
|
+
data.tar.gz: d8612f17a0eb7b9d7e5d735f7fc164ee288e770ba5d2b4bc62c35fcbda97088e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 88d83c16ca1e416998cf5411dd45ca4cd9e62af3841dcea5981a2fa8dedbc86ea74a8ef91ceb44db6e5192e36af65c5877f32d373ccffd154c4472951d379fcf
|
7
|
+
data.tar.gz: 4332d9b28484fe4c09708147494ea33c3caa48f263824a72ef846888a32cd27bad585a98560a8f7883c5be88394da72318e44a144e0b1b4ecfbde148812de058
|
data/lib/curve.rb
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
module EllipticCurve
|
2
|
+
#
|
3
|
+
# Elliptic Curve Equation
|
4
|
+
#
|
5
|
+
# y^2 = x^3 + A*x + B (mod P)
|
6
|
+
#
|
7
|
+
module Curve
|
8
|
+
class CurveFp
|
9
|
+
attr_accessor :a, :b, :p, :n, :g, :name, :oid, :nistName
|
10
|
+
|
11
|
+
def initialize(a, b, p, n, gx, gy, name, oid, nistName=nil)
|
12
|
+
@a = a
|
13
|
+
@b = b
|
14
|
+
@p = p
|
15
|
+
@n = n
|
16
|
+
@g = Point.new(gx, gy)
|
17
|
+
@name = name
|
18
|
+
@oid = oid
|
19
|
+
@nistName = nistName
|
20
|
+
end
|
21
|
+
|
22
|
+
def contains(p)
|
23
|
+
# Verify if the point `p` is on the curve
|
24
|
+
# :param p: point p = Point(x, y)
|
25
|
+
# :return: boolean
|
26
|
+
if not (0 <= p.x and p.x <= @p - 1)
|
27
|
+
return false
|
28
|
+
end
|
29
|
+
if not (0 <= p.y and p.y <= @p - 1)
|
30
|
+
return false
|
31
|
+
end
|
32
|
+
if (p.y ** 2 - (p.x ** 3 + @a * p.x + @b)) % @p != 0
|
33
|
+
return false
|
34
|
+
end
|
35
|
+
return true
|
36
|
+
end
|
37
|
+
|
38
|
+
def length
|
39
|
+
return (1 + ("%x" % @n).length).div(2)
|
40
|
+
end
|
41
|
+
|
42
|
+
def y(x, isEven)
|
43
|
+
ySquared = (x.pow(3, @p) + @a * x + @b) % @p
|
44
|
+
y = Math::modularSquareRoot(ySquared, @p)
|
45
|
+
if isEven != (y % 2 == 0)
|
46
|
+
y = @p - y
|
47
|
+
end
|
48
|
+
return y
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
@_curvesByOid = { }
|
54
|
+
|
55
|
+
def self.add(curve)
|
56
|
+
@_curvesByOid[curve.oid] = curve
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.getbyOid(oid)
|
60
|
+
if not @_curvesByOid.include?(oid)
|
61
|
+
raise Exception.new("Unknown curve oid: #{oid}; The following are registered: #{@_curvesByOid.map{|k,v| v.name}}")
|
62
|
+
end
|
63
|
+
return @_curvesByOid[oid]
|
64
|
+
end
|
65
|
+
|
66
|
+
SECP256K1 = CurveFp.new(
|
67
|
+
0x0000000000000000000000000000000000000000000000000000000000000000,
|
68
|
+
0x0000000000000000000000000000000000000000000000000000000000000007,
|
69
|
+
0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f,
|
70
|
+
0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141,
|
71
|
+
0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,
|
72
|
+
0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8,
|
73
|
+
"secp256k1",
|
74
|
+
[1, 3, 132, 0, 10]
|
75
|
+
)
|
76
|
+
|
77
|
+
PRIME256V1 = CurveFp.new(
|
78
|
+
0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc,
|
79
|
+
0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b,
|
80
|
+
0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff,
|
81
|
+
0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551,
|
82
|
+
0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296,
|
83
|
+
0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5,
|
84
|
+
"prime256v1",
|
85
|
+
[1, 2, 840, 10045, 3, 1, 7],
|
86
|
+
"p-256",
|
87
|
+
)
|
88
|
+
|
89
|
+
P256 = PRIME256V1
|
90
|
+
|
91
|
+
self.add(PRIME256V1)
|
92
|
+
self.add(SECP256K1)
|
93
|
+
end
|
94
|
+
end
|
data/lib/ecdsa.rb
CHANGED
@@ -1,32 +1,50 @@
|
|
1
1
|
require 'digest'
|
2
|
-
require 'openssl'
|
3
|
-
require_relative './signature'
|
4
2
|
|
5
3
|
|
6
4
|
module EllipticCurve
|
7
|
-
|
8
5
|
module Ecdsa
|
9
|
-
|
10
6
|
def self.sign(message, privateKey, hashfunc=nil)
|
11
|
-
if hashfunc.nil?
|
12
|
-
|
13
|
-
|
14
|
-
|
7
|
+
if hashfunc.nil? then hashfunc = lambda{ |x| Digest::SHA256.digest(x) } end
|
8
|
+
byteMessage = hashfunc.call(message)
|
9
|
+
numberMessage = Utils::Binary.numberFromByteString(byteMessage)
|
10
|
+
curve = privateKey.curve
|
11
|
+
|
12
|
+
r, s, randSignPoint = 0, 0, nil
|
13
|
+
while r == 0 or s == 0
|
14
|
+
randNum = Utils::RandomInteger.between(1, curve.n - 1)
|
15
|
+
randSignPoint = Math.multiply(curve.g, randNum, curve.n, curve.a, curve.p)
|
16
|
+
r = randSignPoint.x % curve.n
|
17
|
+
s = ((numberMessage + r * privateKey.secret) * (Math.inv(randNum, curve.n))) % curve.n
|
15
18
|
end
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
+
recoveryId = randSignPoint.y & 1
|
20
|
+
if randSignPoint.y > curve.n
|
21
|
+
recoveryId += 2
|
22
|
+
end
|
23
|
+
return Signature.new(r, s, recoveryId)
|
19
24
|
end
|
20
25
|
|
21
26
|
def self.verify(message, signature, publicKey, hashfunc=nil)
|
22
|
-
if hashfunc.nil?
|
23
|
-
|
24
|
-
|
25
|
-
|
27
|
+
if hashfunc.nil? then hashfunc = lambda{ |x| Digest::SHA256.digest(x) } end
|
28
|
+
byteMessage = hashfunc.call(message)
|
29
|
+
numberMessage = Utils::Binary.numberFromByteString(byteMessage)
|
30
|
+
|
31
|
+
curve = publicKey.curve
|
32
|
+
r = signature.r
|
33
|
+
s = signature.s
|
34
|
+
if not (1 <= r and r <= curve.n - 1)
|
35
|
+
return false
|
26
36
|
end
|
27
|
-
|
37
|
+
if not (1 <= s and s <= curve.n - 1)
|
38
|
+
return false
|
39
|
+
end
|
40
|
+
inv = Math.inv(s, curve.n)
|
41
|
+
u1 = Math.multiply(curve.g, (numberMessage * inv) % curve.n, curve.n, curve.a, curve.p)
|
42
|
+
u2 = Math.multiply(publicKey.point, (r * inv) % curve.n, curve.n, curve.a, curve.p)
|
43
|
+
v = Math.add(u1, u2, curve.a, curve.p)
|
44
|
+
if v.isAtInfinity
|
45
|
+
return false
|
46
|
+
end
|
47
|
+
return v.x % curve.n == r
|
28
48
|
end
|
29
|
-
|
30
49
|
end
|
31
|
-
|
32
|
-
end
|
50
|
+
end
|
data/lib/math.rb
ADDED
@@ -0,0 +1,167 @@
|
|
1
|
+
module EllipticCurve
|
2
|
+
class Math
|
3
|
+
def self.modularSquareRoot(value, prime)
|
4
|
+
# :param value: Value to calculate the square root
|
5
|
+
# :param prime: Prime number in the module of the equation Y^2 = X^3 + A*X + B (mod p)
|
6
|
+
# :return: Square root of the value
|
7
|
+
return value.pow((prime + 1).div(4), prime)
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.multiply(p, n, order, coeff, prime)
|
11
|
+
# Fast way to multiply point and scalar in elliptic curves
|
12
|
+
|
13
|
+
# :param p: First Point to mutiply
|
14
|
+
# :param n: Scalar to mutiply
|
15
|
+
# :param N: Order of the elliptic curve
|
16
|
+
# :param A: Coefficient of the first-order term of the equation Y^2 = X^3 + A*X + B (mod p)
|
17
|
+
# :param P: Prime number in the module of the equation Y^2 = X^3 + A*X + B (mod p)
|
18
|
+
# :return: Point that represents the sum of First and Second Point
|
19
|
+
return self._fromJacobian(
|
20
|
+
self._jacobianMultiply(self._toJacobian(p), n, order, coeff, prime), prime
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.add(p, q, coeff, prime)
|
25
|
+
# Fast way to add two points in elliptic curves
|
26
|
+
|
27
|
+
# :param p: First Point you want to add
|
28
|
+
# :param q: Second Point you want to add
|
29
|
+
# :param P: Prime number in the module of the equation Y^2 = X^3 + A*X + B (mod p)
|
30
|
+
# :param A: Coefficient of the first-order term of the equation Y^2 = X^3 + A*X + B (mod p)
|
31
|
+
# :return: Point that represents the sum of First and Second Point
|
32
|
+
return self._fromJacobian(
|
33
|
+
self._jacobianAdd(self._toJacobian(p), self._toJacobian(q), coeff, prime), prime
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.inv(x, n)
|
38
|
+
# Extended Euclidean Algorithm. It's the 'division' in elliptic curves
|
39
|
+
|
40
|
+
# :param x: Divisor
|
41
|
+
# :param n: Mod for division
|
42
|
+
# :return: Value representing the division
|
43
|
+
if x == 0 then return 0 end
|
44
|
+
|
45
|
+
lm = 1
|
46
|
+
hm = 0
|
47
|
+
low = x % n
|
48
|
+
high = n
|
49
|
+
|
50
|
+
while low > 1
|
51
|
+
r = high.div(low)
|
52
|
+
nm = hm - lm * r
|
53
|
+
nw = high - low * r
|
54
|
+
high = low
|
55
|
+
hm = lm
|
56
|
+
low = nw
|
57
|
+
lm = nm
|
58
|
+
end
|
59
|
+
return lm % n
|
60
|
+
end
|
61
|
+
|
62
|
+
def self._toJacobian(p)
|
63
|
+
# Convert point to Jacobian coordinates
|
64
|
+
|
65
|
+
# :param p: First Point you want to add
|
66
|
+
# :return: Point in Jacobian coordinates
|
67
|
+
return Point.new(p.x, p.y, 1)
|
68
|
+
end
|
69
|
+
|
70
|
+
def self._fromJacobian(p, prime)
|
71
|
+
# Convert point back from Jacobian coordinates
|
72
|
+
|
73
|
+
# :param p: First Point you want to add
|
74
|
+
# :param P: Prime number in the module of the equation Y^2 = X^3 + A*X + B (mod p)
|
75
|
+
# :return: Point in default coordinates
|
76
|
+
z = self.inv(p.z, prime)
|
77
|
+
x = (p.x * z ** 2) % prime
|
78
|
+
y = (p.y * z ** 3) % prime
|
79
|
+
|
80
|
+
return Point.new(x, y, 0)
|
81
|
+
end
|
82
|
+
|
83
|
+
def self._jacobianDouble(p, coeff, prime)
|
84
|
+
# Double a point in elliptic curves
|
85
|
+
|
86
|
+
# :param p: Point you want to double
|
87
|
+
# :param A: Coefficient of the first-order term of the equation Y^2 = X^3 + A*X + B (mod p)
|
88
|
+
# :param P: Prime number in the module of the equation Y^2 = X^3 + A*X + B (mod p)
|
89
|
+
# :return: Point that represents the sum of First and Second Point
|
90
|
+
if p.y == 0 then return Point.new(0, 0, 0) end
|
91
|
+
|
92
|
+
ysq = (p.y ** 2) % prime
|
93
|
+
s = (4 * p.x * ysq) % prime
|
94
|
+
m = (3 * p.x ** 2 + coeff * p.z ** 4) % prime
|
95
|
+
nx = (m ** 2 - 2 * s) % prime
|
96
|
+
ny = (m * (s - nx) - 8 * ysq ** 2) % prime
|
97
|
+
nz = (2 * p.y * p.z) % prime
|
98
|
+
|
99
|
+
return Point.new(nx, ny, nz)
|
100
|
+
end
|
101
|
+
|
102
|
+
def self._jacobianAdd(p, q, coeff, prime)
|
103
|
+
# Add two points in elliptic curves
|
104
|
+
|
105
|
+
# :param p: First Point you want to add
|
106
|
+
# :param q: Second Point you want to add
|
107
|
+
# :param A: Coefficient of the first-order term of the equation Y^2 = X^3 + A*X + B (mod p)
|
108
|
+
# :param P: Prime number in the module of the equation Y^2 = X^3 + A*X + B (mod p)
|
109
|
+
# :return: Point that represents the sum of First and Second Point
|
110
|
+
if p.y == 0 then return q end
|
111
|
+
if q.y == 0 then return p end
|
112
|
+
|
113
|
+
u1 = (p.x * q.z ** 2) % prime
|
114
|
+
u2 = (q.x * p.z ** 2) % prime
|
115
|
+
s1 = (p.y * q.z ** 3) % prime
|
116
|
+
s2 = (q.y * p.z ** 3) % prime
|
117
|
+
|
118
|
+
if u1 == u2
|
119
|
+
if s1 != s2 then return Point.new(0, 0, 1) end
|
120
|
+
return self._jacobianDouble(p, coeff, prime)
|
121
|
+
end
|
122
|
+
|
123
|
+
h = u2 - u1
|
124
|
+
r = s2 - s1
|
125
|
+
h2 = (h * h) % prime
|
126
|
+
h3 = (h * h2) % prime
|
127
|
+
u1h2 = (u1 * h2) % prime
|
128
|
+
nx = (r ** 2 - h3 - 2 * u1h2) % prime
|
129
|
+
ny = (r * (u1h2 - nx) - s1 * h3) % prime
|
130
|
+
nz = (h * p.z * q.z) % prime
|
131
|
+
|
132
|
+
return Point.new(nx, ny, nz)
|
133
|
+
end
|
134
|
+
|
135
|
+
def self._jacobianMultiply(p, n, order, coeff, prime)
|
136
|
+
# Multiply point and scalar in elliptic curves
|
137
|
+
|
138
|
+
# :param p: First Point to mutiply
|
139
|
+
# :param n: Scalar to mutiply
|
140
|
+
# :param N: Order of the elliptic curve
|
141
|
+
# :param P: Prime number in the module of the equation Y^2 = X^3 + A*X + B (mod p)
|
142
|
+
# :param A: Coefficient of the first-order term of the equation Y^2 = X^3 + A*X + B (mod p)
|
143
|
+
# :return: Point that represents the sum of First and Second Point
|
144
|
+
if p.y == 0 or n == 0
|
145
|
+
return Point.new(0, 0, 1)
|
146
|
+
end
|
147
|
+
|
148
|
+
if n == 1
|
149
|
+
return p
|
150
|
+
end
|
151
|
+
|
152
|
+
if n < 0 or n >= order
|
153
|
+
return self._jacobianMultiply(p, n % order, order, coeff, prime)
|
154
|
+
end
|
155
|
+
|
156
|
+
if (n % 2) == 0
|
157
|
+
return self._jacobianDouble(
|
158
|
+
self._jacobianMultiply(p, n.div(2), order, coeff, prime), coeff, prime
|
159
|
+
)
|
160
|
+
end
|
161
|
+
|
162
|
+
return self._jacobianAdd(
|
163
|
+
self._jacobianDouble(self._jacobianMultiply(p, n.div(2), order, coeff, prime), coeff, prime), p, coeff, prime
|
164
|
+
)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
data/lib/point.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
module EllipticCurve
|
2
|
+
class Point
|
3
|
+
def initialize(x=0, y=0, z=0)
|
4
|
+
@x = x
|
5
|
+
@y = y
|
6
|
+
@z = z
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_accessor :x, :y, :z
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
return "(#{@x}, #{@y}, #{@z})"
|
13
|
+
end
|
14
|
+
|
15
|
+
def isAtInfinity()
|
16
|
+
return @y == 0
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/privatekey.rb
CHANGED
@@ -1,53 +1,68 @@
|
|
1
|
-
require "openssl"
|
2
|
-
require "base64"
|
3
|
-
require_relative "publickey"
|
4
|
-
|
5
|
-
|
6
1
|
module EllipticCurve
|
7
|
-
|
8
2
|
class PrivateKey
|
3
|
+
PemTemplate = "-----BEGIN EC PRIVATE KEY-----\n{content}\n-----END EC PRIVATE KEY-----"
|
4
|
+
private_constant :PemTemplate
|
9
5
|
|
10
|
-
|
11
|
-
if openSslKey.nil?
|
12
|
-
@openSslPrivateKey = OpenSSL::PKey::EC.new(curve)
|
13
|
-
@openSslPrivateKey.generate_key
|
14
|
-
else
|
15
|
-
@openSslPrivateKey = openSslKey
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
attr_reader :openSslPrivateKey
|
6
|
+
attr_accessor :curve, :secret
|
20
7
|
|
8
|
+
def initialize(curve=Curve::SECP256K1, secret=nil)
|
9
|
+
@curve = curve
|
10
|
+
@secret = secret ? secret : Utils::RandomInteger.between(1, @curve.n - 1)
|
11
|
+
end
|
12
|
+
|
21
13
|
def publicKey
|
22
|
-
|
23
|
-
|
24
|
-
|
14
|
+
curve = @curve
|
15
|
+
publicPoint = Math.multiply(
|
16
|
+
curve.g,
|
17
|
+
@secret,
|
18
|
+
curve.n,
|
19
|
+
curve.a,
|
20
|
+
curve.p
|
21
|
+
)
|
22
|
+
return PublicKey.new(publicPoint, curve)
|
25
23
|
end
|
26
24
|
|
27
25
|
def toString
|
28
|
-
return
|
26
|
+
return Utils::Binary.hexFromInt(@secret)
|
29
27
|
end
|
30
28
|
|
31
29
|
def toDer
|
32
|
-
|
30
|
+
publicKeyString = self.publicKey.toString(true)
|
31
|
+
hexadecimal = Utils::Der.encodeConstructed(
|
32
|
+
Utils::Der.encodePrimitive(Utils::Der::DerFieldType.integer, 1),
|
33
|
+
Utils::Der.encodePrimitive(Utils::Der::DerFieldType.octetString, Utils::Binary.hexFromInt(@secret)),
|
34
|
+
Utils::Der.encodePrimitive(Utils::Der::DerFieldType.oidContainer, Utils::Der.encodePrimitive(Utils::Der::DerFieldType.object, @curve.oid)),
|
35
|
+
Utils::Der.encodePrimitive(Utils::Der::DerFieldType.publicKeyPointContainer, Utils::Der.encodePrimitive(Utils::Der::DerFieldType.bitString, publicKeyString))
|
36
|
+
)
|
37
|
+
return Utils::Binary.byteStringFromHex(hexadecimal)
|
33
38
|
end
|
34
39
|
|
35
40
|
def toPem
|
36
|
-
|
41
|
+
der = self.toDer()
|
42
|
+
return Utils::Pem.create(Utils::Binary.base64FromByteString(der), PemTemplate)
|
37
43
|
end
|
38
44
|
|
39
45
|
def self.fromPem(string)
|
40
|
-
|
46
|
+
privateKeyPem = Utils::Pem.getContent(string, PemTemplate)
|
47
|
+
return self.fromDer(Utils::Binary.byteStringFromBase64(privateKeyPem))
|
41
48
|
end
|
42
49
|
|
43
50
|
def self.fromDer(string)
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
51
|
+
hexadecimal = Utils::Binary.hexFromByteString(string)
|
52
|
+
privateKeyFlag, secretHex, curveData, publicKeyString = Utils::Der.parse(hexadecimal)[0]
|
53
|
+
if privateKeyFlag != 1
|
54
|
+
raise Exception.new("Private keys should start with a '1' flag, but a '#{privateKeyFlag}' was found instead")
|
55
|
+
end
|
56
|
+
curve = Curve.getbyOid(curveData[0])
|
57
|
+
privateKey = self.fromString(secretHex, curve)
|
58
|
+
if privateKey.publicKey.toString(true) != publicKeyString[0]
|
59
|
+
raise Exception.new("The public key described inside the private key file doesn't match the actual public key of the pair")
|
60
|
+
end
|
61
|
+
return privateKey
|
49
62
|
end
|
50
63
|
|
64
|
+
def self.fromString(string, curve=Curve::SECP256K1)
|
65
|
+
return PrivateKey.new(curve, Utils::Binary.intFromHex(string))
|
66
|
+
end
|
51
67
|
end
|
52
|
-
|
53
|
-
end
|
68
|
+
end
|
data/lib/publickey.rb
CHANGED
@@ -1,37 +1,108 @@
|
|
1
1
|
module EllipticCurve
|
2
|
-
|
3
2
|
class PublicKey
|
3
|
+
PemTemplate = "-----BEGIN PUBLIC KEY-----\n{content}\n-----END PUBLIC KEY-----"
|
4
|
+
EcdsaPublicKeyOid = [1, 2, 840, 10045, 2, 1]
|
5
|
+
EvenTag = "02"
|
6
|
+
OddTag = "03"
|
7
|
+
private_constant :PemTemplate, :EcdsaPublicKeyOid, :EvenTag, :OddTag
|
8
|
+
|
9
|
+
attr_accessor :point, :curve
|
4
10
|
|
5
|
-
def initialize(
|
6
|
-
@
|
11
|
+
def initialize(point, curve)
|
12
|
+
@point = point
|
13
|
+
@curve = curve
|
7
14
|
end
|
15
|
+
|
16
|
+
def toString encoded=false
|
17
|
+
baseLength = 2 * @curve.length
|
8
18
|
|
9
|
-
|
19
|
+
xHex = Utils::Binary.hexFromInt(@point.x).rjust(baseLength, "0")
|
20
|
+
yHex = Utils::Binary.hexFromInt(@point.y).rjust(baseLength, "0")
|
21
|
+
string = xHex + yHex
|
22
|
+
if encoded
|
23
|
+
return "0004" + string
|
24
|
+
end
|
25
|
+
return string
|
26
|
+
end
|
10
27
|
|
11
|
-
def
|
12
|
-
|
28
|
+
def toCompressed
|
29
|
+
baseLength = 2 * @curve.length
|
30
|
+
parityTag = @point.y % 2 == 0 ? EvenTag : OddTag
|
31
|
+
xHex = Utils::Binary.hexFromInt(@point.x).rjust(baseLength, "0")
|
32
|
+
return parityTag + xHex
|
13
33
|
end
|
14
34
|
|
15
35
|
def toDer
|
16
|
-
@
|
36
|
+
@hexadecimal = Utils::Der.encodeConstructed(
|
37
|
+
Utils::Der.encodeConstructed(
|
38
|
+
Utils::Der.encodePrimitive(Utils::Der::DerFieldType.object, EcdsaPublicKeyOid),
|
39
|
+
Utils::Der.encodePrimitive(Utils::Der::DerFieldType.object, @curve.oid)
|
40
|
+
),
|
41
|
+
Utils::Der.encodePrimitive(Utils::Der::DerFieldType.bitString, self.toString(true))
|
42
|
+
)
|
43
|
+
return Utils::Binary.byteStringFromHex(@hexadecimal)
|
17
44
|
end
|
18
45
|
|
19
46
|
def toPem
|
20
|
-
|
47
|
+
der = self.toDer()
|
48
|
+
return Utils::Pem.create(Utils::Binary.base64FromByteString(der), PemTemplate)
|
21
49
|
end
|
22
50
|
|
23
51
|
def self.fromPem(string)
|
24
|
-
|
52
|
+
publicKeyPem = Utils::Pem.getContent(string, PemTemplate)
|
53
|
+
return self.fromDer(Utils::Binary.byteStringFromBase64(publicKeyPem))
|
25
54
|
end
|
26
55
|
|
27
56
|
def self.fromDer(string)
|
28
|
-
|
57
|
+
hexadecimal = Utils::Binary.hexFromByteString(string)
|
58
|
+
curveData, pointString = Utils::Der.parse(hexadecimal)[0]
|
59
|
+
publicKeyOid, curveOid = curveData
|
60
|
+
if publicKeyOid != EcdsaPublicKeyOid
|
61
|
+
raise Exception.new("The Public Key Object Identifier (OID) should be #{EcdsaPublicKeyOid}, but #{publicKeyOid} was found instead")
|
62
|
+
end
|
63
|
+
curve = Curve.getbyOid(curveOid)
|
64
|
+
return self.fromString(pointString, curve)
|
29
65
|
end
|
30
66
|
|
31
|
-
def self.fromString(string)
|
32
|
-
|
67
|
+
def self.fromString(string, curve=Curve::SECP256K1, validatePoint=true)
|
68
|
+
baseLength = 2 * curve.length
|
69
|
+
if string.length > 2 * baseLength and string[0..3] == "0004"
|
70
|
+
string = string[4..-1]
|
71
|
+
end
|
72
|
+
|
73
|
+
xs = string[0..baseLength - 1]
|
74
|
+
ys = string[baseLength..-1]
|
75
|
+
|
76
|
+
p = Point.new(
|
77
|
+
Utils::Binary.intFromHex(xs),
|
78
|
+
Utils::Binary.intFromHex(ys)
|
79
|
+
)
|
80
|
+
|
81
|
+
publicKey = PublicKey.new(p, curve)
|
82
|
+
if not validatePoint
|
83
|
+
return publicKey
|
84
|
+
end
|
85
|
+
if p.isAtInfinity()
|
86
|
+
raise Exception.new("Public key point at infinity")
|
87
|
+
end
|
88
|
+
if not curve.contains(p)
|
89
|
+
raise Exception.new("Point (#{p.x}, #{p.y}) is not valid for curve #{curve.name}")
|
90
|
+
end
|
91
|
+
if not Math.multiply(p, curve.n, curve.n, curve.a, curve.p).isAtInfinity()
|
92
|
+
raise Exception.new("Point (#{p.x}, #{p.y}) * #{curve.name}.n is not at infinity")
|
93
|
+
end
|
94
|
+
|
95
|
+
return publicKey
|
33
96
|
end
|
34
97
|
|
98
|
+
def self.fromCompressed(string, curve=Curve::SECP256K1)
|
99
|
+
parityTag, xHex = string[0..1], string[2..-1]
|
100
|
+
if not [EvenTag, OddTag].include? parityTag
|
101
|
+
raise Exception.new("Compressed string should start with 02 or 03")
|
102
|
+
end
|
103
|
+
x = Utils::Binary.intFromHex(xHex)
|
104
|
+
y = curve.y(x=x, isEven=parityTag == EvenTag)
|
105
|
+
return PublicKey.new(point=Point.new(x, y), curve=curve)
|
106
|
+
end
|
35
107
|
end
|
36
|
-
|
37
|
-
end
|
108
|
+
end
|
data/lib/signature.rb
CHANGED
@@ -1,35 +1,51 @@
|
|
1
|
-
require "base64"
|
2
|
-
require "openssl"
|
3
|
-
|
4
|
-
|
5
1
|
module EllipticCurve
|
6
|
-
|
7
2
|
class Signature
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
@
|
13
|
-
@
|
3
|
+
attr_reader :r, :s, :recoveryId
|
4
|
+
|
5
|
+
def initialize(r, s, recoveryId=nil)
|
6
|
+
@r = r
|
7
|
+
@s = s
|
8
|
+
@recoveryId = recoveryId
|
14
9
|
end
|
15
10
|
|
16
|
-
|
11
|
+
def toDer(withRecoveryId=false)
|
12
|
+
hexadecimal = self._toString
|
13
|
+
encodedSequence = Utils::Binary.byteStringFromHex(hexadecimal)
|
14
|
+
if not withRecoveryId
|
15
|
+
return encodedSequence
|
16
|
+
end
|
17
|
+
return (27 + @recoveryId).chr + encodedSequence
|
18
|
+
end
|
17
19
|
|
18
|
-
def
|
19
|
-
return
|
20
|
+
def toBase64(withRecoveryId=false)
|
21
|
+
return Utils::Binary.base64FromByteString(self.toDer(withRecoveryId))
|
20
22
|
end
|
21
23
|
|
22
|
-
def
|
23
|
-
|
24
|
+
def self.fromDer(string, recoveryByte=false)
|
25
|
+
@recoveryId = nil
|
26
|
+
if recoveryByte
|
27
|
+
@recoveryId = string[0].ord - 27
|
28
|
+
string = string[1..-1]
|
29
|
+
end
|
30
|
+
hexadecimal = Utils::Binary.hexFromByteString(string)
|
31
|
+
return self._fromString(hexadecimal, @recoveryId)
|
24
32
|
end
|
25
33
|
|
26
|
-
def self.
|
27
|
-
|
34
|
+
def self.fromBase64(string, recoveryByte=false)
|
35
|
+
der = Utils::Binary.byteStringFromBase64(string)
|
36
|
+
return self.fromDer(der, recoveryByte)
|
28
37
|
end
|
29
38
|
|
30
|
-
def
|
31
|
-
|
39
|
+
def _toString
|
40
|
+
return Utils::Der.encodeConstructed(
|
41
|
+
Utils::Der.encodePrimitive(Utils::Der::DerFieldType.integer, @r),
|
42
|
+
Utils::Der.encodePrimitive(Utils::Der::DerFieldType.integer, @s)
|
43
|
+
)
|
32
44
|
end
|
33
45
|
|
46
|
+
def self._fromString(string, recoveryId=nil)
|
47
|
+
@r, @s = Utils::Der.parse(string)[0]
|
48
|
+
return Signature.new(@r, @s, recoveryId)
|
49
|
+
end
|
34
50
|
end
|
35
|
-
end
|
51
|
+
end
|
data/lib/starkbank-ecdsa.rb
CHANGED
@@ -1,5 +1,13 @@
|
|
1
|
+
require_relative "utils/pem"
|
2
|
+
require_relative "utils/binary"
|
3
|
+
require_relative "utils/oid"
|
4
|
+
require_relative "utils/integer"
|
5
|
+
require_relative "utils/der"
|
6
|
+
require_relative "utils/file"
|
1
7
|
require_relative "signature"
|
2
8
|
require_relative "publickey"
|
9
|
+
require_relative "math"
|
10
|
+
require_relative "point"
|
11
|
+
require_relative "curve"
|
3
12
|
require_relative "privatekey"
|
4
13
|
require_relative "ecdsa"
|
5
|
-
require_relative "utils/file"
|
data/lib/utils/binary.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require "base64"
|
2
|
+
|
3
|
+
|
4
|
+
module EllipticCurve
|
5
|
+
module Utils
|
6
|
+
class Binary
|
7
|
+
def self.hexFromInt(number)
|
8
|
+
hexadecimal = number.to_s(16)
|
9
|
+
if hexadecimal.length % 2 == 1
|
10
|
+
hexadecimal = "0" + hexadecimal
|
11
|
+
end
|
12
|
+
return hexadecimal
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.intFromHex(hexadecimal)
|
16
|
+
return hexadecimal.to_i(16)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.hexFromByteString(bytes)
|
20
|
+
return bytes.unpack("H*")[0]
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.byteStringFromHex(hexadecimal)
|
24
|
+
return [hexadecimal].pack("H*")
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.numberFromByteString(bytes)
|
28
|
+
return bytes.unpack("C*").reduce(0) { |number, byte| number * 256 + byte }
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.base64FromByteString(byteString)
|
32
|
+
return Base64.encode64(byteString).gsub("\n", "")
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.byteStringFromBase64(base64)
|
36
|
+
return Base64.decode64(base64)
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.bitsFromHex(hexadecimal)
|
40
|
+
return intFromHex(hexadecimal).to_s(2).rjust(hexadecimal.length * 4, "0")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/utils/der.rb
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
module EllipticCurve
|
2
|
+
module Utils
|
3
|
+
class Der
|
4
|
+
module DerFieldType
|
5
|
+
@integer = "integer"
|
6
|
+
@bitString = "bitString"
|
7
|
+
@octetString = "octetString"
|
8
|
+
@null = "null"
|
9
|
+
@object = "object"
|
10
|
+
@printableString = "printableString"
|
11
|
+
@utcTime = "utcTime"
|
12
|
+
@sequence = "sequence"
|
13
|
+
@set = "set"
|
14
|
+
@oidContainer = "oidContainer"
|
15
|
+
@publicKeyPointContainer = "publicKeyPointContainer"
|
16
|
+
|
17
|
+
class << self; attr_accessor :integer, :bitString, :octetString, :null, :object, :printableString, :utcTime, :sequence, :set, :oidContainer, :publicKeyPointContainer; end
|
18
|
+
end
|
19
|
+
|
20
|
+
@_hexTagToType = {
|
21
|
+
"02" => DerFieldType.integer,
|
22
|
+
"03" => DerFieldType.bitString,
|
23
|
+
"04" => DerFieldType.octetString,
|
24
|
+
"05" => DerFieldType.null,
|
25
|
+
"06" => DerFieldType.object,
|
26
|
+
"13" => DerFieldType.printableString,
|
27
|
+
"17" => DerFieldType.utcTime,
|
28
|
+
"30" => DerFieldType.sequence,
|
29
|
+
"31" => DerFieldType.set,
|
30
|
+
"a0" => DerFieldType.oidContainer,
|
31
|
+
"a1" => DerFieldType.publicKeyPointContainer
|
32
|
+
}
|
33
|
+
|
34
|
+
@_typeToHexTag = {}
|
35
|
+
|
36
|
+
@_hexTagToType.each { |k, v| @_typeToHexTag[v] = k }
|
37
|
+
|
38
|
+
def self.encodeConstructed(*encodedValues)
|
39
|
+
return self.encodePrimitive(DerFieldType.sequence, encodedValues.join(""))
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.encodePrimitive(tagType, value)
|
43
|
+
if tagType == DerFieldType.integer
|
44
|
+
value = self._encodeInteger(value)
|
45
|
+
end
|
46
|
+
if tagType == DerFieldType.object
|
47
|
+
value = Utils::Oid.oidToHex(value)
|
48
|
+
end
|
49
|
+
return "#{@_typeToHexTag[tagType]}#{self._generateLengthBytes(value)}#{value}"
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.parse(hexadecimal)
|
53
|
+
if hexadecimal.class == String && hexadecimal.empty?
|
54
|
+
return []
|
55
|
+
elsif not hexadecimal then
|
56
|
+
return []
|
57
|
+
end
|
58
|
+
|
59
|
+
typeByte, hexadecimal = hexadecimal[0..1], hexadecimal[2..-1]
|
60
|
+
length, lengthBytes = self._readLengthBytes(hexadecimal)
|
61
|
+
content = hexadecimal[lengthBytes..lengthBytes + length - 1]
|
62
|
+
hexadecimal = hexadecimal[lengthBytes + length..-1]
|
63
|
+
|
64
|
+
if content.length < length
|
65
|
+
raise Exception.new("missing bytes in DER parsing")
|
66
|
+
end
|
67
|
+
|
68
|
+
tagData = self._getTagData(typeByte)
|
69
|
+
if tagData[:isConstructed]
|
70
|
+
content = self.parse(content)
|
71
|
+
end
|
72
|
+
|
73
|
+
valueParser = {
|
74
|
+
DerFieldType.null => lambda { |content| self._parseNull(content) },
|
75
|
+
DerFieldType.object => lambda { |content| self._parseOid(content) },
|
76
|
+
DerFieldType.utcTime => lambda { |content| self._parseTime(content) },
|
77
|
+
DerFieldType.integer => lambda { |content| self._parseInteger(content) },
|
78
|
+
DerFieldType.printableString => lambda { |content| self._parseString(content) },
|
79
|
+
}.fetch(tagData[:type], lambda { |content| self._parseAny(content) })
|
80
|
+
|
81
|
+
return [valueParser.call(content)] + self.parse(hexadecimal)
|
82
|
+
end
|
83
|
+
|
84
|
+
def self._parseAny(hexadecimal)
|
85
|
+
return hexadecimal
|
86
|
+
end
|
87
|
+
|
88
|
+
def self._parseOid(hexadecimal)
|
89
|
+
return Utils::Oid.oidFromHex(hexadecimal)
|
90
|
+
end
|
91
|
+
|
92
|
+
def self._parseTime(hexadecimal)
|
93
|
+
string = self._parseString(hexadecimal)
|
94
|
+
return DateTime.strptime(string, "%y%m%d%H%M%SZ")
|
95
|
+
end
|
96
|
+
|
97
|
+
def self._parseString(hexadecimal)
|
98
|
+
return Utils::Binary.byteStringFromHex(hexadecimal)
|
99
|
+
end
|
100
|
+
|
101
|
+
def self._parseNull(_content)
|
102
|
+
return nil
|
103
|
+
end
|
104
|
+
|
105
|
+
def self._parseInteger(hexadecimal)
|
106
|
+
integer = Utils::Binary.intFromHex(hexadecimal)
|
107
|
+
bits = Utils::Binary.bitsFromHex(hexadecimal[0])
|
108
|
+
if bits[0] == "0" # negative numbers are encoded using two's complement
|
109
|
+
return integer
|
110
|
+
end
|
111
|
+
bitCount = 4 * hexadecimal.length
|
112
|
+
return integer - (2 ** bitCount)
|
113
|
+
end
|
114
|
+
|
115
|
+
def self._encodeInteger(number)
|
116
|
+
hexadecimal = Utils::Binary.hexFromInt(number.abs)
|
117
|
+
if number < 0
|
118
|
+
bitCount = hexadecimal.length * 4
|
119
|
+
twosComplement = (2 ** bitCount) + number
|
120
|
+
return Utils::Binary.hexFromInt(twosComplement)
|
121
|
+
end
|
122
|
+
bits = Utils::Binary.bitsFromHex(hexadecimal[0])
|
123
|
+
if bits[0] == "1"
|
124
|
+
hexadecimal = "00" + hexadecimal
|
125
|
+
end
|
126
|
+
return hexadecimal
|
127
|
+
end
|
128
|
+
|
129
|
+
def self._readLengthBytes(hexadecimal)
|
130
|
+
lengthBytes = 2
|
131
|
+
lengthIndicator = Utils::Binary.intFromHex(hexadecimal[0, lengthBytes])
|
132
|
+
isShortForm = lengthIndicator < 128 # checks if first bit of byte is 1 (a.k.a. short-form)
|
133
|
+
if isShortForm
|
134
|
+
length = lengthIndicator * 2
|
135
|
+
return length, lengthBytes
|
136
|
+
end
|
137
|
+
|
138
|
+
lengthLength = lengthIndicator - 128 # nullifies first bit of byte (only used as long-form flag)
|
139
|
+
if lengthLength == 0
|
140
|
+
raise Exception.new("indefinite length encoding located in DER")
|
141
|
+
end
|
142
|
+
lengthBytes += 2 * lengthLength
|
143
|
+
length = Utils::Binary.intFromHex(hexadecimal[2, lengthBytes]) * 2
|
144
|
+
return length, lengthBytes
|
145
|
+
end
|
146
|
+
|
147
|
+
def self._generateLengthBytes(hexadecimal)
|
148
|
+
size = hexadecimal.length.div(2)
|
149
|
+
length = Utils::Binary.hexFromInt(size)
|
150
|
+
if size < 128
|
151
|
+
return length.rjust(2, "0")
|
152
|
+
end
|
153
|
+
lengthLength = 128 + length.length.div(2)
|
154
|
+
return Utils::Binary.hexFromInt(lengthLength) + length
|
155
|
+
end
|
156
|
+
|
157
|
+
def self._getTagData(tag)
|
158
|
+
bits = Utils::Binary.bitsFromHex(tag)
|
159
|
+
bit8 = bits[0]
|
160
|
+
bit7 = bits[1]
|
161
|
+
bit6 = bits[2]
|
162
|
+
|
163
|
+
tagClass = {
|
164
|
+
"0" => {
|
165
|
+
"0" => "universal",
|
166
|
+
"1" => "application",
|
167
|
+
},
|
168
|
+
"1" => {
|
169
|
+
"0" => "context-specific",
|
170
|
+
"1" => "private",
|
171
|
+
},
|
172
|
+
}[bit8][bit7]
|
173
|
+
|
174
|
+
isConstructed = bit6 == "1"
|
175
|
+
|
176
|
+
return {
|
177
|
+
"class": tagClass,
|
178
|
+
"isConstructed": isConstructed,
|
179
|
+
"type": @_hexTagToType[tag],
|
180
|
+
}
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
data/lib/utils/file.rb
CHANGED
@@ -0,0 +1,23 @@
|
|
1
|
+
require('securerandom')
|
2
|
+
|
3
|
+
module EllipticCurve
|
4
|
+
module Utils
|
5
|
+
class RandomInteger
|
6
|
+
# Return integer x in the range: min <= x <= max
|
7
|
+
|
8
|
+
# Parameters (required):
|
9
|
+
# :param min: minimum value of the integer
|
10
|
+
# :param max: maximum value of the integer
|
11
|
+
# :return: A random number between min and max
|
12
|
+
def self.between(min, max)
|
13
|
+
if (max - min) < 0 then
|
14
|
+
raise Exception.new("max must be greater than min")
|
15
|
+
end
|
16
|
+
if (max - min) > 0 then
|
17
|
+
return SecureRandom.random_number((max + 1) - min) + min
|
18
|
+
end
|
19
|
+
return min
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/utils/oid.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
module EllipticCurve
|
2
|
+
module Utils
|
3
|
+
class Oid
|
4
|
+
def self.oidFromHex(hexadecimal)
|
5
|
+
firstByte, remainingBytes = hexadecimal[0..1], hexadecimal[2..-1]
|
6
|
+
firstByteInt = Utils::Binary.intFromHex(firstByte)
|
7
|
+
oid = [firstByteInt.div(40), firstByteInt % 40]
|
8
|
+
oidInt = 0
|
9
|
+
while remainingBytes.to_s.length > 0
|
10
|
+
byte, remainingBytes = remainingBytes[0..1], remainingBytes[2..-1]
|
11
|
+
byteInt = Utils::Binary.intFromHex(byte)
|
12
|
+
if byteInt >= 128
|
13
|
+
oidInt = (128 * oidInt) + (byteInt - 128)
|
14
|
+
next
|
15
|
+
end
|
16
|
+
oidInt = (128 * oidInt) + byteInt
|
17
|
+
oid.append(oidInt)
|
18
|
+
oidInt = 0
|
19
|
+
end
|
20
|
+
return oid
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.oidToHex(oid)
|
24
|
+
hexadecimal = Utils::Binary.hexFromInt(40 * oid[0] + oid[1])
|
25
|
+
for number in oid[2..-1]
|
26
|
+
hexadecimal += self._oidNumberToHex(number)
|
27
|
+
end
|
28
|
+
return hexadecimal
|
29
|
+
end
|
30
|
+
|
31
|
+
def self._oidNumberToHex(number)
|
32
|
+
hexadecimal = ""
|
33
|
+
endDelta = 0
|
34
|
+
while number > 0
|
35
|
+
hexadecimal = Utils::Binary.hexFromInt((number % 128) + endDelta) + hexadecimal
|
36
|
+
number = number.div(128)
|
37
|
+
endDelta = 128
|
38
|
+
end
|
39
|
+
return hexadecimal == "" ? "00" : hexadecimal
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/utils/pem.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
module EllipticCurve
|
2
|
+
module Utils
|
3
|
+
class Pem
|
4
|
+
def self.getContent(pem, template)
|
5
|
+
pattern = template.sub "{content}", ("(.*)")
|
6
|
+
return pem.split("\n").join("").match(pattern.split("\n").join("")).captures[0]
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.create(content, template)
|
10
|
+
lines = []
|
11
|
+
(0..content.length).step(64) do |start|
|
12
|
+
lines.append(content[start..start+63])
|
13
|
+
end
|
14
|
+
return template.sub "{content}", lines.join("\n")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: starkbank-ecdsa
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- starkbank
|
@@ -44,12 +44,20 @@ executables: []
|
|
44
44
|
extensions: []
|
45
45
|
extra_rdoc_files: []
|
46
46
|
files:
|
47
|
+
- lib/curve.rb
|
47
48
|
- lib/ecdsa.rb
|
49
|
+
- lib/math.rb
|
50
|
+
- lib/point.rb
|
48
51
|
- lib/privatekey.rb
|
49
52
|
- lib/publickey.rb
|
50
53
|
- lib/signature.rb
|
51
54
|
- lib/starkbank-ecdsa.rb
|
55
|
+
- lib/utils/binary.rb
|
56
|
+
- lib/utils/der.rb
|
52
57
|
- lib/utils/file.rb
|
58
|
+
- lib/utils/integer.rb
|
59
|
+
- lib/utils/oid.rb
|
60
|
+
- lib/utils/pem.rb
|
53
61
|
homepage: https://github.com/starkbank/ecdsa-ruby
|
54
62
|
licenses:
|
55
63
|
- MIT
|
@@ -62,14 +70,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
62
70
|
requirements:
|
63
71
|
- - ">="
|
64
72
|
- !ruby/object:Gem::Version
|
65
|
-
version: '2.
|
73
|
+
version: '2.4'
|
66
74
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
67
75
|
requirements:
|
68
76
|
- - ">="
|
69
77
|
- !ruby/object:Gem::Version
|
70
78
|
version: '0'
|
71
79
|
requirements: []
|
72
|
-
rubygems_version: 3.0.
|
80
|
+
rubygems_version: 3.0.3.1
|
73
81
|
signing_key:
|
74
82
|
specification_version: 4
|
75
83
|
summary: fast openSSL-compatible implementation of the Elliptic Curve Digital Signature
|