ecdsa 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d725d0e513f16f2ec4b24170b5d2f85c26472d79
4
+ data.tar.gz: 866b36f01ad0f3e0cf3f32b2ec01881252e564b7
5
+ SHA512:
6
+ metadata.gz: d00fa2b23d9fc9225b6bbe7ae9e1b84254b3e31a5163974179f3ce8bbd3cde4c7d48012de2ba54a238d76cc068f9d0f0310eff985ebdb296191036fbba9f6c07
7
+ data.tar.gz: 84610f1b18e5e8e3b633e932fc58eb950d37a4b193a87ea49e9a16bfd454a1c961278276f0bad1c49d26c7d19cf8c4f3ee4b3690e29cc13ca1c9a79b3ba5c070
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org/'
2
+
3
+ gem 'rspec'
4
+ gem 'rubocop', '0.19.1'
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 David Grayson.
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,29 @@
1
+ # ECDSA gem for Ruby
2
+
3
+ This gem implements the Elliptic Curve Digital Signature Algorithm (ECDSA) almost entirely in pure Ruby.
4
+ It aims to be easier to use and easier to understand than Ruby's [OpenSSL EC support](http://www.ruby-doc.org/stdlib/libdoc/openssl/rdoc/OpenSSL/PKey/EC.html).
5
+ This gem does use OpenSSL but it only uses it to decode and encode ASN1 strings for ECDSA signatures.
6
+ All cryptographic calculations are done in pure Ruby.
7
+
8
+ The main classes of this gem are `ECDSA::Group`, `ECDSA::Point`, and `ECDSA::Signature`.
9
+ These classes operate on Ruby integers and do not deal at all with binary formatting.
10
+ Encoding and decoding of binary formats is solely handled by classes under the `ECDSA::Format` module.
11
+
12
+ You can enter your own curve parameters by instantiating a new `ECDSA::Group` object or you can
13
+ use a pre-existing group object such as `ECDSA::Group::Secp256k1`.
14
+ The pre-existing groups can be seen in the `lib/ecdsa/group` folder, and include all the curves
15
+ defined in [SEC2](http://www.secg.org/collateral/sec2_final.pdf) and [NIST's Recommended Elliptic Curves for Federal Government Use](http://csrc.nist.gov/groups/ST/toolkit/documents/dss/NISTReCur.pdf).
16
+
17
+ This gem is hosted at the [DavidEGrayson/ruby_ecdsa github repository](https://github.com/DavidEGrayson/ruby_ecdsa).
18
+
19
+ ## Current limitations
20
+
21
+ - This gem only supports fields of integers modulo a prime number (_F<sub>p</sub>_).
22
+ ECDSA's characteristic 2 fields are not supported.
23
+ - This gem can only compute square roots in prime fields over a prime _p_
24
+ that is one less than a multiple of 4.
25
+ Computing a square root is required for parsing public keys stored in compressed form.
26
+ - There is no documentation. If you know ECDSA and know how to read Ruby source code,
27
+ you can probably figure it out though.
28
+ - The algorithms have not been optimized for speed, and will probably never be, because that
29
+ would hinder the goal of helping people understand ECDSA.
@@ -0,0 +1,59 @@
1
+ require_relative 'ecdsa/group'
2
+ require_relative 'ecdsa/signature'
3
+ require_relative 'ecdsa/verify'
4
+ require_relative 'ecdsa/sign'
5
+ require_relative 'ecdsa/format'
6
+ require_relative 'ecdsa/version'
7
+
8
+ module ECDSA
9
+ def self.byte_length(integer)
10
+ length = 0
11
+ while integer > 0
12
+ length += 1
13
+ integer >>= 8
14
+ end
15
+ length
16
+ end
17
+
18
+ def self.bit_length(integer)
19
+ length = 0
20
+ while integer > 0
21
+ length += 1
22
+ integer >>= 1
23
+ end
24
+ length
25
+ end
26
+
27
+ def self.convert_digest_to_integer(digest)
28
+ case digest
29
+ when Integer then digest
30
+ when String then convert_octet_string_to_bit_string(digest)
31
+ else raise "Invalid digest: #{digest.inspect}"
32
+ end
33
+ end
34
+
35
+ def self.leftmost_bits(n, bit_length)
36
+ if n >= (1 << (8 * bit_length))
37
+ raise 'Have not yet written code to handle this case'
38
+ else
39
+ n
40
+ end
41
+ end
42
+
43
+ def self.normalize_digest(digest, bit_length)
44
+ digest_num = convert_digest_to_integer(digest)
45
+ leftmost_bits(digest_num, bit_length)
46
+ end
47
+
48
+ # SEC1, Section 2.3.2.
49
+ # My interpretation of that section is that we treat the octet string as BIG endian.
50
+ def self.convert_octet_string_to_bit_string(string)
51
+ string.bytes.reduce { |n, b| (n << 8) + b }
52
+ end
53
+ end
54
+
55
+ class String
56
+ def hex_inspect
57
+ '"' + each_byte.map { |b| '\x%02x' % b }.join + '"'
58
+ end
59
+ end
@@ -0,0 +1,5 @@
1
+ require 'ecdsa/format/decode_error'
2
+ require 'ecdsa/format/point_octet_string'
3
+ require 'ecdsa/format/integer_octet_string'
4
+ require 'ecdsa/format/field_element_octet_string'
5
+ require 'ecdsa/format/signature_der_string'
@@ -0,0 +1,11 @@
1
+ module ECDSA
2
+ module Format
3
+ # Raising instance of this class as an exception indicates that
4
+ # the data being decoded was invalid, but does not necessarily
5
+ # indicate a bug in the program, unlike most other exceptions
6
+ # because it is possible the data being decoded is coming from
7
+ # an untrusted source.
8
+ class DecodeError < StandardError
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,27 @@
1
+ # Defined in http://www.secg.org/collateral/sec1_final.pdf
2
+ # section 2.3.5: FieldElement-to-OctetString Conversion
3
+
4
+ module ECDSA
5
+ module Format
6
+ module FieldElementOctetString
7
+ # @param integer (Integer) The integer to encode
8
+ # @param length (Integer) The number of bytes desired in the output string.
9
+ def self.encode(element, field)
10
+ raise ArgumentError, 'Given element is not an element of the field.' if !field.include?(element)
11
+ length = ECDSA.byte_length(field.prime)
12
+ IntegerOctetString.encode(element, length)
13
+ end
14
+
15
+ def self.decode(string, field)
16
+ int = IntegerOctetString.decode(string)
17
+
18
+ if !field.include?(int)
19
+ # The integer has to be non-negative, so it must be too big.
20
+ raise DecodeError, 'Decoded integer is too large for field: 0x%x >= 0x%x.' % [int, field.prime]
21
+ end
22
+
23
+ int
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,28 @@
1
+ # Defined in http://www.secg.org/collateral/sec1_final.pdf :
2
+ # Section 2.3.7: Integer-to-OctetString Conversion
3
+ # Section 2.3.8: OctetString-to-Integer Conversion
4
+
5
+ module ECDSA
6
+ module Format
7
+ module IntegerOctetString
8
+ # @param integer (Integer) The integer to encode
9
+ # @param length (Integer) The number of bytes desired in the output string.
10
+ def self.encode(integer, length)
11
+ raise ArgumentError, 'Integer to encode is negative.' if integer < 0
12
+ raise ArgumentError, 'Integer to encode is too large.' if integer >= (1 << (8 * length))
13
+
14
+ length.pred.downto(0).map do |i|
15
+ integer >> (8 * i)
16
+ end.pack('C*')
17
+ end
18
+
19
+ def self.decode(string)
20
+ integer = 0
21
+ string.each_byte do |b|
22
+ integer = (integer << 8) + b.ord
23
+ end
24
+ integer
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,87 @@
1
+ # encoding: US-ASCII
2
+
3
+ # The point octet string format is defined in http://www.secg.org/collateral/sec1_final.pdf .
4
+ # Section 2.3.3: EllipticCurvePoint-to-OctetString Conversion
5
+ # Section 2.3.4: OctetString-to-EllipticCurvePoint Conversion
6
+
7
+ require_relative '../point'
8
+
9
+ module ECDSA
10
+ module Format
11
+ module PointOctetString
12
+ def self.encode(point, opts = {})
13
+ return "\x00" if point.infinity?
14
+
15
+ if opts[:compression]
16
+ start_byte = point.y.even? ? "\x02" : "\x03"
17
+ start_byte + FieldElementOctetString.encode(point.x, point.group.field)
18
+ else
19
+ "\x04" +
20
+ FieldElementOctetString.encode(point.x, point.group.field) +
21
+ FieldElementOctetString.encode(point.y, point.group.field)
22
+ end
23
+ end
24
+
25
+ # This is equivalent to ec_GFp_simple_oct2point in OpenSSL:
26
+ # https://github.com/openssl/openssl/blob/a898936218bc279b5d7cdf76d58a25e7a2d419cb/crypto/ec/ecp_oct.c
27
+ def self.decode(string, group)
28
+ raise DecodeError, 'Point octet string is empty.' if string.empty?
29
+
30
+ case string[0].ord
31
+ when 0
32
+ check_length string, 1
33
+ return group.infinity_point
34
+ when 2
35
+ decode_compressed string, group, 0
36
+ when 3
37
+ decode_compressed string, group, 1
38
+ when 4
39
+ decode_uncompressed string, group
40
+ else
41
+ raise DecodeError, 'Unrecognized start byte for point octet string: 0x%x' % string[0].ord
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def self.decode_compressed(string, group, y_lsb)
48
+ expected_length = 1 + group.byte_length
49
+ check_length string, expected_length
50
+
51
+ x_string = string[1, group.byte_length]
52
+ x = ECDSA::Format::FieldElementOctetString.decode x_string, group.field
53
+
54
+ possible_ys = group.solve_for_y(x)
55
+ y = possible_ys.find { |py| (py % 2) == y_lsb }
56
+
57
+ finish_decode x, y, group
58
+ end
59
+
60
+ def self.decode_uncompressed(string, group)
61
+ expected_length = 1 + 2 * group.byte_length
62
+ check_length string, expected_length
63
+
64
+ x_string = string[1, group.byte_length]
65
+ y_string = string[1 + group.byte_length, group.byte_length]
66
+ x = ECDSA::Format::FieldElementOctetString.decode x_string, group.field
67
+ y = ECDSA::Format::FieldElementOctetString.decode y_string, group.field
68
+
69
+ finish_decode x, y, group
70
+ end
71
+
72
+ def self.finish_decode(x, y, group)
73
+ point = group.new_point [x, y]
74
+ if !group.include? point
75
+ raise DecodeError, "Decoded point does not satisfy curve equation: #{point.inspect}."
76
+ end
77
+ point
78
+ end
79
+
80
+ def self.check_length(string, expected_length)
81
+ if string.length != expected_length
82
+ raise DecodeError, "Expected point octet string to be length #{expected_length} but it was #{string.length}."
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,22 @@
1
+ require 'openssl'
2
+ require_relative '../signature'
3
+
4
+ module ECDSA
5
+ module Format
6
+ module SignatureDerString
7
+ def self.decode(der_string)
8
+ asn1 = OpenSSL::ASN1.decode(der_string)
9
+ r = asn1.value[0].value.to_i
10
+ s = asn1.value[1].value.to_i
11
+ Signature.new(r, s)
12
+ end
13
+
14
+ def self.encode(signature)
15
+ ra = OpenSSL::ASN1::Integer.new signature.r
16
+ sa = OpenSSL::ASN1::Integer.new signature.s
17
+ asn1 = OpenSSL::ASN1::Sequence.new [ra, sa]
18
+ asn1.to_der
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,126 @@
1
+ require_relative 'prime_field'
2
+ require_relative 'point'
3
+
4
+ module ECDSA
5
+ class Group
6
+ attr_reader :name
7
+
8
+ attr_reader :generator
9
+
10
+ attr_reader :order
11
+
12
+ attr_reader :param_a
13
+
14
+ attr_reader :param_b
15
+
16
+ attr_reader :field
17
+
18
+ # These parameters are defined in http://www.secg.org/collateral/sec2_final.pdf
19
+ #
20
+ # - +p+: A prime number that defines the field used. The field will be F_p.
21
+ # - +a+: The a parameter in the curve equation (y^2 = x^3 + ax + b).
22
+ # - +b+: The b parameter in the curve equation.
23
+ # - +g+: The base point as an octet string.
24
+ # - +n+: The order of g.
25
+ # - +h+: The cofactor.
26
+ def initialize(opts)
27
+ @opts = opts
28
+
29
+ @name = opts.fetch(:name) { '%#x' % object_id }
30
+ @field = PrimeField.new(opts[:p])
31
+ @param_a = opts[:a]
32
+ @param_b = opts[:b]
33
+ @generator = new_point(@opts[:g])
34
+ @order = opts[:n]
35
+ @cofactor = opts[:h]
36
+
37
+ @param_a.is_a?(Integer) or raise ArgumentError, 'Invalid a.'
38
+ @param_b.is_a?(Integer) or raise ArgumentError, 'Invalid b.'
39
+
40
+ @param_a = field.mod @param_a
41
+ @param_b = field.mod @param_b
42
+ end
43
+
44
+ def new_point(p)
45
+ case p
46
+ when :infinity
47
+ infinity_point
48
+ when Array
49
+ x, y = p
50
+ Point.new(self, x, y)
51
+ when Integer
52
+ generator.multiply_by_scalar(p)
53
+ else
54
+ raise ArgumentError, "Invalid point specifier #{p.inspect}."
55
+ end
56
+ end
57
+
58
+ def infinity_point
59
+ @infinity_point ||= Point.new(self, :infinity)
60
+ end
61
+
62
+ # The number of bits that it takes to represent a member of the field.
63
+ # Log base 2 of the prime p, rounded up.
64
+ def bit_length
65
+ @bit_length ||= ECDSA.bit_length(field.prime)
66
+ end
67
+
68
+ def byte_length
69
+ @byte_length ||= ECDSA.byte_length(field.prime)
70
+ end
71
+
72
+ # Verify that the point is a solution to the curve's defining equation.
73
+ def include?(point)
74
+ raise 'Group mismatch.' if point.group != self
75
+ point.infinity? or point_satisfies_equation?(point)
76
+ end
77
+
78
+ # You should probably use include? instead of this.
79
+ def point_satisfies_equation?(point)
80
+ field.mod(point.y * point.y) == equation_right_hand_side(point.x)
81
+ end
82
+
83
+ def equation_right_hand_side(x)
84
+ field.mod(x * x * x + param_a * x + param_b)
85
+ end
86
+
87
+ def solve_for_y(x)
88
+ field.square_roots equation_right_hand_side x
89
+ end
90
+
91
+ def inspect
92
+ "#<#{self.class}:#{name}>"
93
+ end
94
+
95
+ def to_s
96
+ inspect
97
+ end
98
+
99
+ NAMES = %w(
100
+ Secp112r1
101
+ Secp112r2
102
+ Secp128r1
103
+ Secp128r2
104
+ Secp160k1
105
+ Secp160r1
106
+ Secp160r2
107
+ Secp192k1
108
+ Secp192r1
109
+ Secp224k1
110
+ Secp224r1
111
+ Secp256k1
112
+ Secp256r1
113
+ Secp384r1
114
+ Secp521r1
115
+ Nistp192
116
+ Nistp224
117
+ Nistp256
118
+ Nistp384
119
+ Nistp521
120
+ )
121
+
122
+ NAMES.each do |name|
123
+ autoload name, 'ecdsa/group/' + name.downcase
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,16 @@
1
+ # Source: http://csrc.nist.gov/groups/ST/toolkit/documents/dss/NISTReCur.pdf
2
+
3
+ module ECDSA
4
+ class Group
5
+ Nistp192 = new(
6
+ name: 'nistp192',
7
+ p: 62771017353866807638357894232076664160839087_00390324961279,
8
+ a: -3,
9
+ b: 0x64210519_e59c80e7_0fa7e9ab_72243049_feb8deec_c146b9b1,
10
+ g: [0x188da80e_b03090f6_7cbf20eb_43a18800_f4ff0afd_82ff1012,
11
+ 0x07192b95_ffc8da78_631011ed_6b24cdd5_73f977a1_1e794811],
12
+ n: 62771017353866807638357894231760590137671947_73182842284081,
13
+ h: nil, # cofactor not given in NIST document
14
+ )
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ # Source: http://csrc.nist.gov/groups/ST/toolkit/documents/dss/NISTReCur.pdf
2
+
3
+ module ECDSA
4
+ class Group
5
+ Nistp224 = new(
6
+ name: 'nistp224',
7
+ p: 26959946667150639794667015087019630673557916_260026308143510066298881,
8
+ a: -3,
9
+ b: 0xb4050a85_0c04b3ab_f5413256_5044b0b7_d7bfd8ba_270b3943_2355ffb4,
10
+ g: [0xb70e0cbd_6bb4bf7f_321390b9_4a03c1d3_56c21122_343280d6_115c1d21,
11
+ 0xbd376388_b5f723fb_4c22dfe6_cd4375a0_5a074764_44d58199_85007e34],
12
+ n: 26959946667150639794667015087019625940457807_714424391721682722368061,
13
+ h: nil, # cofactor not given in NIST document
14
+ )
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ # Source: http://csrc.nist.gov/groups/ST/toolkit/documents/dss/NISTReCur.pdf
2
+
3
+ module ECDSA
4
+ class Group
5
+ Nistp256 = new(
6
+ name: 'nistp256',
7
+ p: 11579208921035624876269744694940757353008614_3415290314195533631308867097853951,
8
+ a: -3,
9
+ b: 0x5ac635d8_aa3a93e7_b3ebbd55_769886bc_651d06b0_cc53b0f6_3bce3c3e_27d2604b,
10
+ g: [0x6b17d1f2_e12c4247_f8bce6e5_63a440f2_77037d81_2deb33a0_f4a13945_d898c296,
11
+ 0x4fe342e2_fe1a7f9b_8ee7eb4a_7c0f9e16_2bce3357_6b315ece_cbb64068_37bf51f5],
12
+ n: 11579208921035624876269744694940757352999695_5224135760342422259061068512044369,
13
+ h: nil, # cofactor not given in NIST document
14
+ )
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ # Source: http://csrc.nist.gov/groups/ST/toolkit/documents/dss/NISTReCur.pdf
2
+
3
+ module ECDSA
4
+ class Group
5
+ Nistp384 = new(
6
+ name: 'nistp384',
7
+ p: 39402006196394479212279040100143613805079739_27046544666794829340424572177149687032904726_6088258938001861606973112319,
8
+ a: -3,
9
+ b: 0xb3312fa7_e23ee7e4_988e056b_e3f82d19_181d9c6e_fe814112_0314088f_5013875a_c656398d_8a2ed19d_2a85c8ed_d3ec2aef,
10
+ g: [0xaa87ca22_be8b0537_8eb1c71e_f320ad74_6e1d3b62_8ba79b98_59f741e0_82542a38_5502f25d_bf55296c_3a545e38_72760ab7,
11
+ 0x3617de4a_96262c6f_5d9e98bf_9292dc29_f8f41dbd_289a147c_e9da3113_b5f0b8c0_0a60b1ce_1d7e819d_7a431d7c_90ea0e5f,
12
+ ],
13
+ n: 39402006196394479212279040100143613805079739_27046544666794690527962765939911326356939895_6308152294913554433653942643,
14
+ h: nil, # cofactor not given in NIST document
15
+ )
16
+ end
17
+ end
@@ -0,0 +1,16 @@
1
+ # Source: "Curve P-256" from http://csrc.nist.gov/groups/ST/toolkit/documents/dss/NISTReCur.doc
2
+
3
+ module ECDSA
4
+ class Group
5
+ Nistp521 = new(
6
+ name: 'nistp521',
7
+ p: 68647976601306097149819007990813932172694353_00143305409394463459185543183397656052122559_64066145455497729631139148085803712198799971_6643812574028291115057151,
8
+ a: -3,
9
+ b: 0x051_953eb961_8e1c9a1f_929a21a0_b68540ee_a2da725b_99b315f3_b8b48991_8ef109e1_56193951_ec7e937b_1652c0bd_3bb1bf07_3573df88_3d2c34f1_ef451fd4_6b503f00,
10
+ g: [0x00c6_858e06b7_0404e9cd_9e3ecb66_2395b442_9c648139_053fb521_f828af60_6b4d3dba_a14b5e77_efe75928_fe1dc127_a2ffa8de_3348b3c1_856a429b_f97e7e31_c2e5bd66,
11
+ 0x0118_39296a78_9a3bc004_5c8a5fb4_2c7d1bd9_98f54449_579b4468_17afbd17_273e662c_97ee7299_5ef42640_c550b901_3fad0761_353c7086_a272c240_88be9476_9fd16650],
12
+ n: 68647976601306097149819007990813932172694353_00143305409394463459185543183397655394245057_74633321719753296399637136332111386476861244_0380340372808892707005449,
13
+ h: nil, # cofactor not given in NIST document
14
+ )
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ # Source: http://www.secg.org/collateral/sec2_final.pdf
2
+
3
+ module ECDSA
4
+ class Group
5
+ Secp112r1 = new(
6
+ name: 'secp112r1',
7
+ p: 0xDB7C_2ABF62E3_5E668076_BEAD208B,
8
+ a: 0xDB7C_2ABF62E3_5E668076_BEAD2088,
9
+ b: 0x659E_F8BA0439_16EEDE89_11702B22,
10
+ g: [0x0948_7239995A_5EE76B55_F9C2F098,
11
+ 0xA89C_E5AF8724_C0A23E0E_0FF77500],
12
+ n: 0xDB7C_2ABF62E3_5E7628DF_AC6561C5,
13
+ h: 1,
14
+ )
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ # Source: http://www.secg.org/collateral/sec2_final.pdf
2
+
3
+ module ECDSA
4
+ class Group
5
+ Secp112r2 = new(
6
+ name: 'secp112r2',
7
+ p: 0xDB7C_2ABF62E3_5E668076_BEAD208B,
8
+ a: 0x6127_C24C05F3_8A0AAAF6_5C0EF02C,
9
+ b: 0x51DE_F1815DB5_ED74FCC3_4C85D709,
10
+ g: [0x4BA3_0AB5E892_B4E1649D_D0928643,
11
+ 0xADCD_46F5882E_3747DEF3_6E956E97],
12
+ n: 0x36DF_0AAFD8B8_D7597CA1_0520D04B,
13
+ h: 4,
14
+ )
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ # Source: http://www.secg.org/collateral/sec2_final.pdf
2
+
3
+ module ECDSA
4
+ class Group
5
+ Secp128r1 = new(
6
+ name: 'secp128r1',
7
+ p: 0xFFFFFFFD_FFFFFFFF_FFFFFFFF_FFFFFFFF,
8
+ a: 0xFFFFFFFD_FFFFFFFF_FFFFFFFF_FFFFFFFC,
9
+ b: 0xE87579C1_1079F43D_D824993C_2CEE5ED3,
10
+ g: [0x161FF752_8B899B2D_0C28607C_A52C5B86,
11
+ 0xCF5AC839_5BAFEB13_C02DA292_DDED7A83],
12
+ n: 0xFFFFFFFE_00000000_75A30D1B_9038A115,
13
+ h: 1,
14
+ )
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ # Source: http://www.secg.org/collateral/sec2_final.pdf
2
+
3
+ module ECDSA
4
+ class Group
5
+ Secp128r2 = new(
6
+ name: 'secp128r2',
7
+ p: 0xFFFFFFFD_FFFFFFFF_FFFFFFFF_FFFFFFFF,
8
+ a: 0xD6031998_D1B3BBFE_BF59CC9B_BFF9AEE1,
9
+ b: 0x5EEEFCA3_80D02919_DC2C6558_BB6D8A5D,
10
+ g: [0x7B6AA5D8_5E572983_E6FB32A7_CDEBC140,
11
+ 0x27B6916A_894D3AEE_7106FE80_5FC34B44],
12
+ n: 0x3FFFFFFF_7FFFFFFF_BE002472_0613B5A3,
13
+ h: 4,
14
+ )
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ # Source: http://www.secg.org/collateral/sec2_final.pdf
2
+
3
+ module ECDSA
4
+ class Group
5
+ Secp160k1 = new(
6
+ name: 'secp160k1',
7
+ p: 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFAC73,
8
+ a: 0,
9
+ b: 7,
10
+ g: [0x3B4C382C_E37AA192_A4019E76_3036F4F5_DD4D7EBB,
11
+ 0x938CF935_318FDCED_6BC28286_531733C3_F03C4FEE],
12
+ n: 0x01_00000000_00000000_0001B8FA_16DFAB9A_CA16B6B3,
13
+ h: 1,
14
+ )
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ # Source: http://www.secg.org/collateral/sec2_final.pdf
2
+
3
+ module ECDSA
4
+ class Group
5
+ Secp160r1 = new(
6
+ name: 'secp160r1',
7
+ p: 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_7FFFFFFF,
8
+ a: 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_7FFFFFFC,
9
+ b: 0x1C97BEFC_54BD7A8B_65ACF89F_81D4D4AD_C565FA45,
10
+ g: [0x4A96B568_8EF57328_46646989_68C38BB9_13CBFC82,
11
+ 0x23A62855_3168947D_59DCC912_04235137_7AC5FB32],
12
+ n: 0x01_00000000_00000000_0001F4C8_F927AED3_CA752257,
13
+ h: 1,
14
+ )
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ # Source: http://www.secg.org/collateral/sec2_final.pdf
2
+
3
+ module ECDSA
4
+ class Group
5
+ Secp160r2 = new(
6
+ name: 'secp160r2',
7
+ p: 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFAC73,
8
+ a: 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFAC70,
9
+ b: 0xB4E134D3_FB59EB8B_AB572749_04664D5A_F50388BA,
10
+ g: [0x52DCB034_293A117E_1F4FF11B_30F7199D_3144CE6D,
11
+ 0xFEAFFEF2_E331F296_E071FA0D_F9982CFE_A7D43F2E],
12
+ n: 0x01_00000000_00000000_0000351E_E786A818_F3A1A16B,
13
+ h: 1,
14
+ )
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ # Source: http://www.secg.org/collateral/sec2_final.pdf
2
+
3
+ module ECDSA
4
+ class Group
5
+ Secp192k1 = new(
6
+ name: 'secp192k1',
7
+ p: 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFEE37,
8
+ a: 0,
9
+ b: 3,
10
+ g: [0xDB4FF10E_C057E9AE_26B07D02_80B7F434_1DA5D1B1_EAE06C7D,
11
+ 0x9B2F2F6D_9C5628A7_844163D0_15BE8634_4082AA88_D95E2F9D],
12
+ n: 0xFFFFFFFF_FFFFFFFF_FFFFFFFE_26F2FC17_0F69466A_74DEFD8D,
13
+ h: 1,
14
+ )
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ # Source: http://www.secg.org/collateral/sec2_final.pdf
2
+
3
+ module ECDSA
4
+ class Group
5
+ Secp192r1 = new(
6
+ name: 'secp192r1',
7
+ p: 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFFFF_FFFFFFFF,
8
+ a: 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFFFF_FFFFFFFC,
9
+ b: 0x64210519_E59C80E7_0FA7E9AB_72243049_FEB8DEEC_C146B9B1,
10
+ g: [0x188DA80E_B03090F6_7CBF20EB_43A18800_F4FF0AFD_82FF1012,
11
+ 0x07192B95_FFC8DA78_631011ED_6B24CDD5_73F977A1_1E794811],
12
+ n: 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_99DEF836_146BC9B1_B4D22831,
13
+ h: 1,
14
+ )
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ # Source: http://www.secg.org/collateral/sec2_final.pdf
2
+
3
+ module ECDSA
4
+ class Group
5
+ Secp224k1 = new(
6
+ name: 'secp224k1',
7
+ p: 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFE56D,
8
+ a: 0,
9
+ b: 5,
10
+ g: [0xA1455B33_4DF099DF_30FC28A1_69A467E9_E47075A9_0F7E650E_B6B7A45C,
11
+ 0x7E089FED_7FBA3442_82CAFBD6_F7E319F7_C0B0BD59_E2CA4BDB_556D61A5],
12
+ n: 0x01_00000000_00000000_00000000_0001DCE8_D2EC6184_CAF0A971_769FB1F7,
13
+ h: 1,
14
+ )
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ # Source: http://www.secg.org/collateral/sec2_final.pdf
2
+
3
+ module ECDSA
4
+ class Group
5
+ Secp224r1 = new(
6
+ name: 'secp224r1',
7
+ p: 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_00000000_00000000_00000001,
8
+ a: 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFFFF_FFFFFFFF_FFFFFFFE,
9
+ b: 0xB4050A85_0C04B3AB_F5413256_5044B0B7_D7BFD8BA_270B3943_2355FFB4,
10
+ g: [0xB70E0CBD_6BB4BF7F_321390B9_4A03C1D3_56C21122_343280D6_115C1D21,
11
+ 0xBD376388_B5F723FB_4C22DFE6_CD4375A0_5A074764_44D58199_85007E34],
12
+ n: 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFF16A2_E0B8F03E_13DD2945_5C5C2A3D,
13
+ h: 1,
14
+ )
15
+ end
16
+ end
@@ -0,0 +1,18 @@
1
+ # Source: http://www.secg.org/collateral/sec2_final.pdf
2
+
3
+ module ECDSA
4
+ class Group
5
+ Secp256k1 = new(
6
+ name: 'secp256k1',
7
+ p: 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFC2F,
8
+ a: 0,
9
+ b: 7,
10
+ g: [0x79BE667E_F9DCBBAC_55A06295_CE870B07_029BFCDB_2DCE28D9_59F2815B_16F81798,
11
+ 0x483ADA77_26A3C465_5DA4FBFC_0E1108A8_FD17B448_A6855419_9C47D08F_FB10D4B8],
12
+ n: 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFE_BAAEDCE6_AF48A03B_BFD25E8C_D0364141,
13
+ h: 1,
14
+ )
15
+
16
+ # compressed G: "\x02\x79\xBE\x66\x7E\xF9\xDC\xBB\xAC\x55\xA0\x62\x95\xCE\x87\x0B\x07\x02\x9B\xFC\xDB\x2D\xCE\x28\xD9\x59\xF2\x81\x5B\x16\xF8\x17\x98",
17
+ end
18
+ end
@@ -0,0 +1,16 @@
1
+ # Source: http://www.secg.org/collateral/sec2_final.pdf
2
+
3
+ module ECDSA
4
+ class Group
5
+ Secp256r1 = new(
6
+ name: 'secp256r1',
7
+ p: 0xFFFFFFFF_00000001_00000000_00000000_00000000_FFFFFFFF_FFFFFFFF_FFFFFFFF,
8
+ a: 0xFFFFFFFF_00000001_00000000_00000000_00000000_FFFFFFFF_FFFFFFFF_FFFFFFFC,
9
+ b: 0x5AC635D8_AA3A93E7_B3EBBD55_769886BC_651D06B0_CC53B0F6_3BCE3C3E_27D2604B,
10
+ g: [0x6B17D1F2_E12C4247_F8BCE6E5_63A440F2_77037D81_2DEB33A0_F4A13945_D898C296,
11
+ 0x4FE342E2_FE1A7F9B_8EE7EB4A_7C0F9E16_2BCE3357_6B315ECE_CBB64068_37BF51F5],
12
+ n: 0xFFFFFFFF_00000000_FFFFFFFF_FFFFFFFF_BCE6FAAD_A7179E84_F3B9CAC2_FC632551,
13
+ h: 1,
14
+ )
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ # Source: http://www.secg.org/collateral/sec2_final.pdf
2
+
3
+ module ECDSA
4
+ class Group
5
+ Secp384r1 = new(
6
+ name: 'secp384r1',
7
+ p: 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFFFF_00000000_00000000_FFFFFFFF,
8
+ a: 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFFFF_00000000_00000000_FFFFFFFC,
9
+ b: 0xB3312FA7_E23EE7E4_988E056B_E3F82D19_181D9C6E_FE814112_0314088F_5013875A_C656398D_8A2ED19D_2A85C8ED_D3EC2AEF,
10
+ g: [0xAA87CA22_BE8B0537_8EB1C71E_F320AD74_6E1D3B62_8BA79B98_59F741E0_82542A38_5502F25D_BF55296C_3A545E38_72760AB7,
11
+ 0x3617DE4A_96262C6F_5D9E98BF_9292DC29_F8F41DBD_289A147C_E9DA3113_B5F0B8C0_0A60B1CE_1D7E819D_7A431D7C_90EA0E5F],
12
+ n: 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_C7634D81_F4372DDF_581A0DB2_48B0A77A_ECEC196A_CCC52973,
13
+ h: 1,
14
+ )
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ # Source: http://www.secg.org/collateral/sec2_final.pdf
2
+
3
+ module ECDSA
4
+ class Group
5
+ Secp521r1 = new(
6
+ name: 'secp521r1',
7
+ p: 0x01FF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF,
8
+ a: 0x01FF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFC,
9
+ b: 0x0051_953EB961_8E1C9A1F_929A21A0_B68540EE_A2DA725B_99B315F3_B8B48991_8EF109E1_56193951_EC7E937B_1652C0BD_3BB1BF07_3573DF88_3D2C34F1_EF451FD4_6B503F00,
10
+ g: [0x00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66,
11
+ 0x011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650],
12
+ n: 0x01FF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFA_51868783_BF2F966B_7FCC0148_F709A5D0_3BB5C9B8_899C47AE_BB6FB71E_91386409,
13
+ h: 1,
14
+ )
15
+ end
16
+ end
@@ -0,0 +1,113 @@
1
+ # http://www.secg.org/collateral/sec1_final.pdf
2
+
3
+ module ECDSA
4
+ class Point
5
+ attr_reader :group
6
+
7
+ attr_reader :x
8
+
9
+ attr_reader :y
10
+
11
+ def initialize(group, *args)
12
+ @group = group
13
+
14
+ if args == [:infinity]
15
+ @infinity = true
16
+ # leave @x and @y nil
17
+ else
18
+ x, y = args
19
+ raise ArgumentError, "Invalid x: #{x.inspect}" if !x.is_a? Integer
20
+ raise ArgumentError, "Invalid y: #{y.inspect}" if !y.is_a? Integer
21
+
22
+ @x = x
23
+ @y = y
24
+ end
25
+ end
26
+
27
+ def coords
28
+ [x, y]
29
+ end
30
+
31
+ def add_to_point(point)
32
+ check_group! point
33
+
34
+ # Assertions:
35
+ # raise "point given (#{point.inspect}) does not belong to #{group.name}" if !group.include?(point)
36
+ # raise "point (#{inspect}) does not belong to #{group.name}" if !group.include?(self)
37
+
38
+ # SEC1, section 2.2.1, rules 1 and 2
39
+ return point if infinity?
40
+ return self if point.infinity?
41
+
42
+ # SEC1, section 2.2.1, rule 3
43
+ return group.infinity_point if x == point.x && y == field.mod(-point.y)
44
+
45
+ # SEC1, section 2.2.1, rule 4
46
+ if x != point.x
47
+ gamma = field.mod((point.y - y) * field.inverse(point.x - x))
48
+ sum_x = field.mod(gamma * gamma - x - point.x)
49
+ sum_y = field.mod(gamma * (x - sum_x) - y)
50
+ return self.class.new(group, sum_x, sum_y)
51
+ end
52
+
53
+ # SEC2, section 2.2.1, rule 5
54
+ return double if self == point
55
+
56
+ raise "Failed to add #{inspect} to #{point.inspect}: No addition rules matched."
57
+ end
58
+
59
+ def negate
60
+ return self if infinity?
61
+ self.class.new(group, x, field.mod(-y))
62
+ end
63
+
64
+ def double
65
+ gamma = field.mod((3 * x * x + @group.param_a) * field.inverse(2 * y))
66
+ new_x = field.mod(gamma * gamma - 2 * x)
67
+ new_y = field.mod(gamma * (x - new_x) - y)
68
+ self.class.new(group, new_x, new_y)
69
+ end
70
+
71
+ def multiply_by_scalar(i)
72
+ result = group.infinity_point
73
+ v = self
74
+ while i > 0
75
+ result = result.add_to_point(v) if i.odd?
76
+ v = v.double
77
+ i >>= 1
78
+ end
79
+ result
80
+ end
81
+
82
+ def eql?(other)
83
+ return false if !other.is_a?(Point) || other.group != group
84
+ x == other.x && y == other.y
85
+ end
86
+
87
+ def ==(other)
88
+ eql?(other)
89
+ end
90
+
91
+ def infinity?
92
+ @infinity == true
93
+ end
94
+
95
+ def inspect
96
+ if infinity?
97
+ '#<%s: %s, infinity>' % [self.class, group.name]
98
+ else
99
+ '#<%s: %s, 0x%x, 0x%x>' % [self.class, group.name, x, y]
100
+ end
101
+ end
102
+
103
+ private
104
+
105
+ def check_group!(point)
106
+ raise 'Mismatched groups.' if point.group != group
107
+ end
108
+
109
+ def field
110
+ group.field
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,81 @@
1
+ module ECDSA
2
+ class PrimeField
3
+ attr_reader :prime
4
+
5
+ def initialize(prime)
6
+ raise ArgumentError, "Invalid prime #{prime.inspect}" if !prime.is_a?(Integer)
7
+ @prime = prime
8
+ end
9
+
10
+ def include?(e)
11
+ e.is_a?(Integer) && e >= 0 && e < prime
12
+ end
13
+
14
+ def mod(num)
15
+ num % prime
16
+ end
17
+
18
+ # http://en.wikipedia.org/wiki/Extended_Euclidean_algorithm
19
+ def inverse(num)
20
+ raise ArgumentError, '0 has no multiplicative inverse.' if num.zero?
21
+
22
+ # For every i, we make sure that num * s[i] + prime * t[i] = r[i].
23
+ # Eventually r[i] will equal 1 because gcd(num, prime) is always 1.
24
+ # At that point, s[i] is the multiplicative inverse of num in the field.
25
+
26
+ remainders = [num, prime]
27
+ s = [1, 0]
28
+ t = [0, 1]
29
+ arrays = [remainders, s, t]
30
+ while remainders.last > 0
31
+ quotient = remainders[-2] / remainders[-1]
32
+ arrays.each do |array|
33
+ array << array[-2] - quotient * array[-1]
34
+ end
35
+ end
36
+
37
+ raise 'Inversion bug: remainder is not than 1.' if remainders[-2] != 1
38
+ mod s[-2]
39
+ end
40
+
41
+ # Computes n raised to the power m.
42
+ # This algorithm uses the same idea as Point#multiply_by_scalar.
43
+ def power(n, m)
44
+ result = 1
45
+ v = n
46
+ while m > 0
47
+ result = mod result * v if m.odd?
48
+ v = square v
49
+ m >>= 1
50
+ end
51
+ result
52
+ end
53
+
54
+ # Computes n^2.
55
+ def square(n)
56
+ mod n * n
57
+ end
58
+
59
+ def square_roots(n)
60
+ raise ArgumentError, "Not a member of the field: #{n}." if !include?(n)
61
+ if (prime % 4) == 3
62
+ square_roots_for_p_3_mod_4(n)
63
+ else
64
+ raise NotImplementedError, 'Square root is only implemented in fields where the prime is equal to 3 mod 4.'
65
+ end
66
+ end
67
+
68
+ private
69
+
70
+ # This is Algorithm 1 from http://math.stanford.edu/~jbooher/expos/sqr_qnr.pdf
71
+ # The algorithm assumes that its input actually does have a square root.
72
+ # To get around that, we double check the answer after running the algorithm to make
73
+ # sure it works.
74
+ def square_roots_for_p_3_mod_4(n)
75
+ candidate = power n, (prime + 1) / 4
76
+ return [] if square(candidate) != n
77
+ return [candidate] if candidate.zero?
78
+ [candidate, mod(-candidate)].sort
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,37 @@
1
+ module ECDSA
2
+ class SZeroError < StandardError
3
+ end
4
+
5
+ # From http://www.secg.org/collateral/sec1_final.pdf section 4.1.3
6
+ # Warning: Never use the same k value twice for two different messages
7
+ # or else it will be trivial for someone to calculate your private key.
8
+ # k should be generated with a secure random number generator.
9
+ def self.sign(group, private_key, digest, k)
10
+ # Second part of step 1: Select ephemeral elliptic curve key pair
11
+ # k was already selected for us by the caller
12
+ r_point = group.new_point k
13
+
14
+ # Step 2
15
+ xr = r_point.x
16
+
17
+ # Step 3
18
+ point_field = PrimeField.new(group.order)
19
+ r = point_field.mod(xr)
20
+
21
+ # Step 4, calculating the hash, was already performed by the caller.
22
+
23
+ # Step 5
24
+ e = normalize_digest(digest, group.bit_length)
25
+
26
+ # Step 6
27
+ s = point_field.mod(point_field.inverse(k) * (e + r * private_key))
28
+
29
+ if s.zero?
30
+ # We need to go back to step 1, so the caller should generate another
31
+ # random number k and try again.
32
+ return nil
33
+ end
34
+
35
+ Signature.new r, s
36
+ end
37
+ end
@@ -0,0 +1,16 @@
1
+ module ECDSA
2
+ class Signature
3
+ attr_reader :r
4
+ attr_reader :s
5
+
6
+ def initialize(r, s)
7
+ @r, @s = r, s
8
+ r.is_a?(Integer) or raise ArgumentError, 'r is not an integer.'
9
+ s.is_a?(Integer) or raise ArgumentError, 's is not an integer.'
10
+ end
11
+
12
+ def components
13
+ [r, s]
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,48 @@
1
+ module ECDSA
2
+ class InvalidSignatureError < StandardError
3
+ end
4
+
5
+ # Algorithm taken from http://www.secg.org/collateral/sec1_final.pdf Section 4.1.4.
6
+ def self.valid_signature?(public_key, digest, signature)
7
+ check_signature! public_key, digest, signature
8
+ rescue InvalidSignatureError
9
+ false
10
+ end
11
+
12
+ def self.check_signature!(public_key, digest, signature)
13
+ group = public_key.group
14
+ field = group.field
15
+
16
+ # Step 1: r and s must be in the field and non-zero
17
+ raise InvalidSignatureError, 'r value is not the field.' if !field.include?(signature.r)
18
+ raise InvalidSignatureError, 's value is not the field.' if !field.include?(signature.s)
19
+ raise InvalidSignatureError, 'r is zero.' if signature.r.zero?
20
+ raise InvalidSignatureError, 's is zero.' if signature.s.zero?
21
+
22
+ # Step 2 was already performed when the digest of the message was computed.
23
+
24
+ # Step 3: Convert octet string to number and take leftmost bits.
25
+ e = normalize_digest(digest, group.bit_length)
26
+
27
+ # Step 4
28
+ point_field = PrimeField.new(group.order)
29
+ s_inverted = point_field.inverse(signature.s)
30
+ u1 = point_field.mod(e * s_inverted)
31
+ u2 = point_field.mod(signature.r * s_inverted)
32
+
33
+ # Step 5
34
+ r = group.generator.multiply_by_scalar(u1).add_to_point public_key.multiply_by_scalar(u2)
35
+ raise InvalidSignatureError, 'R is infinity in step 5.' if r.infinity?
36
+
37
+ # Step 6
38
+ xr = r.x
39
+
40
+ # Step 7
41
+ v = point_field.mod xr
42
+
43
+ # Step 8
44
+ raise InvalidSignatureError, 'v does not equal r.' if v != signature.r
45
+
46
+ true
47
+ end
48
+ end
@@ -0,0 +1,3 @@
1
+ module ECDSA
2
+ VERSION = '0.1.0'
3
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ecdsa
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - David Grayson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-03-18 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description:
14
+ email: davidegrayson@gmail.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/ecdsa/format/decode_error.rb
20
+ - lib/ecdsa/format/field_element_octet_string.rb
21
+ - lib/ecdsa/format/integer_octet_string.rb
22
+ - lib/ecdsa/format/point_octet_string.rb
23
+ - lib/ecdsa/format/signature_der_string.rb
24
+ - lib/ecdsa/format.rb
25
+ - lib/ecdsa/group/nistp192.rb
26
+ - lib/ecdsa/group/nistp224.rb
27
+ - lib/ecdsa/group/nistp256.rb
28
+ - lib/ecdsa/group/nistp384.rb
29
+ - lib/ecdsa/group/nistp521.rb
30
+ - lib/ecdsa/group/secp112r1.rb
31
+ - lib/ecdsa/group/secp112r2.rb
32
+ - lib/ecdsa/group/secp128r1.rb
33
+ - lib/ecdsa/group/secp128r2.rb
34
+ - lib/ecdsa/group/secp160k1.rb
35
+ - lib/ecdsa/group/secp160r1.rb
36
+ - lib/ecdsa/group/secp160r2.rb
37
+ - lib/ecdsa/group/secp192k1.rb
38
+ - lib/ecdsa/group/secp192r1.rb
39
+ - lib/ecdsa/group/secp224k1.rb
40
+ - lib/ecdsa/group/secp224r1.rb
41
+ - lib/ecdsa/group/secp256k1.rb
42
+ - lib/ecdsa/group/secp256r1.rb
43
+ - lib/ecdsa/group/secp384r1.rb
44
+ - lib/ecdsa/group/secp521r1.rb
45
+ - lib/ecdsa/group.rb
46
+ - lib/ecdsa/point.rb
47
+ - lib/ecdsa/prime_field.rb
48
+ - lib/ecdsa/sign.rb
49
+ - lib/ecdsa/signature.rb
50
+ - lib/ecdsa/verify.rb
51
+ - lib/ecdsa/version.rb
52
+ - lib/ecdsa.rb
53
+ - Gemfile
54
+ - README.md
55
+ - LICENSE.txt
56
+ homepage: https://github.com/pololu/rpicsim
57
+ licenses:
58
+ - MIT
59
+ metadata: {}
60
+ post_install_message:
61
+ rdoc_options: []
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - '>='
72
+ - !ruby/object:Gem::Version
73
+ version: '2'
74
+ requirements: []
75
+ rubyforge_project:
76
+ rubygems_version: 2.0.0
77
+ signing_key:
78
+ specification_version: 4
79
+ summary: This gem implements the Ellipctic Curve Digital Signature Algorithm (ECDSA)
80
+ almost entirely in pure Ruby.
81
+ test_files: []
82
+ has_rdoc: