jose 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,257 @@
1
+ # A point on (twisted) Edwards curve.
2
+ class JOSE::JWA::EdwardsPoint
3
+ include Comparable
4
+
5
+ attr_accessor :x, :y, :z
6
+
7
+ def initpoint(x, y)
8
+ @x = x
9
+ @y = y
10
+ @z = self.class::BASE_FIELD.make(1)
11
+ end
12
+
13
+ def decode_base(s, b)
14
+ # Check that point encoding is of correct length.
15
+ raise ArgumentError, "s must be #{(b/8)} bytes" if s.bytesize != (b / 8)
16
+ # Extract signbit.
17
+ s = s.dup
18
+ xs = s.getbyte((b-1)/8) >> ((b-1) & 7)
19
+ s.setbyte((b-1)/8, s.getbyte((b-1)/8) & ~(1 << 7))
20
+ # Decode y. If this fails, fail.
21
+ y = self.class::BASE_FIELD.from_bytes(s, b)
22
+ # Try to recover x. If it does not exist, or is zero and xs is
23
+ # wrong, fail.
24
+ x = solve_x2(y).sqrt
25
+ raise ArgumentError, "decode error" if x.nil? or (x.zero? and xs != x.sign)
26
+ # If sign of x isn't correct, flip it.
27
+ x = -x if x.sign != xs
28
+ # Return the constructed point.
29
+ return x, y
30
+ end
31
+
32
+ def encode_base(b)
33
+ xp, yp = @x / @z, @y / @z
34
+ # Encode y.
35
+ s = yp.to_bytes(b)
36
+ # Add sign bit of x to encoding.
37
+ if xp.sign != 0
38
+ s.setbyte((b-1)/8, s.getbyte((b-1)/8) | (1 << ((b-1) % 8)))
39
+ end
40
+ return s
41
+ end
42
+
43
+ def *(x)
44
+ r = zero_elem
45
+ s = self
46
+ while x > 0
47
+ if (x % 2) > 0
48
+ r = r + s
49
+ end
50
+ s = s.double
51
+ x = x / 2
52
+ end
53
+ return r
54
+ end
55
+
56
+ # Check two points are equal.
57
+ def <=>(y)
58
+ # Need to check x1/z1 == x2/z2 and similarly for y, so cross-
59
+ # multiply to eliminate divisions.
60
+ xn1 = @x * y.z
61
+ xn2 = y.x * @z
62
+ yn1 = @y * y.z
63
+ yn2 = y.y * @z
64
+ return yn1 <=> yn2 if xn1 == xn2
65
+ return xn1 <=> xn2
66
+ end
67
+ end
68
+
69
+ # A point on Edwards25519.
70
+ class JOSE::JWA::Edwards25519Point < JOSE::JWA::EdwardsPoint
71
+ # Create a new point on curve.
72
+ BASE_FIELD = JOSE::JWA::FieldElement.new(1, (2**255)-19).freeze
73
+ D = (-BASE_FIELD.make(121665)/BASE_FIELD.make(121666)).freeze
74
+ F0 = BASE_FIELD.make(0).freeze
75
+ F1 = BASE_FIELD.make(1).freeze
76
+ XB = BASE_FIELD.make(15112221349535400772501151409588531511454012693041857206046113283949847762202).freeze
77
+ YB = BASE_FIELD.make(46316835694926478169428394003475163141307993866256225615783033603165251855960).freeze
78
+ # Order of basepoint.
79
+ L = 7237005577332262213973186563042994240857116359379907606001950938285454250989
80
+ # The logarithm of cofactor.
81
+ C = 3
82
+ # The highest set bit
83
+ N = 254
84
+ # The coding length
85
+ B = 256
86
+
87
+ attr_accessor :t
88
+
89
+ # The standard base point.
90
+ def self.stdbase
91
+ return new(XB, YB)
92
+ end
93
+
94
+ def initialize(x, y)
95
+ # Check the point is actually on the curve.
96
+ raise ArgumentError, "Invalid point" if y*y-x*x != F1+D*x*x*y*y
97
+ initpoint(x, y)
98
+ @t = x*y
99
+ end
100
+
101
+ # Decode a point representation.
102
+ def decode(s)
103
+ x, y = decode_base(s, B)
104
+ return nil if x.nil?
105
+ return JOSE::JWA::Edwards25519Point.new(x, y)
106
+ end
107
+
108
+ # Encode a point representation.
109
+ def encode
110
+ return encode_base(B)
111
+ end
112
+
113
+ def normalize
114
+ xp, yp, zp = @x / @z, @y / @z, @z / @z
115
+ tmp = zero_elem
116
+ tmp.x, tmp.y, tmp.z, tmp.t = xp, yp, zp, xp * yp
117
+ return tmp
118
+ end
119
+
120
+ # Construct neutral point on this curve.
121
+ def zero_elem
122
+ return JOSE::JWA::Edwards25519Point.new(F0, F1)
123
+ end
124
+
125
+ # Solve for x^2.
126
+ def solve_x2(y)
127
+ return ((y*y-F1)/(D*y*y+F1))
128
+ end
129
+
130
+ # Point addition.
131
+ def +(y)
132
+ # The formulas are from EFD.
133
+ tmp = zero_elem
134
+ zcp = @z * y.z
135
+ a = (@y - @x) * (y.y - y.x)
136
+ b = (@y + @x) * (y.y + y.x)
137
+ c = (D + D) * @t * y.t
138
+ d = zcp + zcp
139
+ e, h = b - a, b + a
140
+ f, g = d - c, d + c
141
+ tmp.x, tmp.y, tmp.z, tmp.t = e * f, g * h, f * g, e * h
142
+ return tmp
143
+ end
144
+
145
+ # Point doubling.
146
+ def double
147
+ # The formulas are from EFD.
148
+ tmp = zero_elem
149
+ x1s, y1s, z1s = @x * @x, @y * @y, @z * @z
150
+ xys = @x + @y
151
+ h = -(x1s + y1s)
152
+ e = xys * xys + h
153
+ g = y1s - x1s
154
+ f = g - (z1s + z1s)
155
+ tmp.x, tmp.y, tmp.z, tmp.t = e * f, g * h, f * g, e * h
156
+ return tmp
157
+ end
158
+
159
+ def inspect
160
+ "\n{#{@x.x},\n"\
161
+ " #{@y.x},\n"\
162
+ " #{@z.x},\n"\
163
+ " #{@t.x}}"
164
+ end
165
+
166
+ end
167
+
168
+ # A point on Edward448
169
+ class JOSE::JWA::Edwards448Point < JOSE::JWA::EdwardsPoint
170
+ # Create a new point on curve.
171
+ BASE_FIELD = JOSE::JWA::FieldElement.new(1, (2**448)-(2**224)-1).freeze
172
+ D = BASE_FIELD.make(-39081).freeze
173
+ F0 = BASE_FIELD.make(0).freeze
174
+ F1 = BASE_FIELD.make(1).freeze
175
+ XB = BASE_FIELD.make(224580040295924300187604334099896036246789641632564134246125461686950415467406032909029192869357953282578032075146446173674602635247710).freeze
176
+ YB = BASE_FIELD.make(298819210078481492676017930443930673437544040154080242095928241372331506189835876003536878655418784733982303233503462500531545062832660).freeze
177
+ # Order of basepoint.
178
+ L = 181709681073901722637330951972001133588410340171829515070372549795146003961539585716195755291692375963310293709091662304773755859649779
179
+ # The logarithm of cofactor.
180
+ C = 2
181
+ # The highest set bit
182
+ N = 447
183
+ # The coding length
184
+ B = 456
185
+
186
+ # The standard base point.
187
+ def self.stdbase
188
+ return new(XB, YB)
189
+ end
190
+
191
+ def initialize(x, y)
192
+ # Check the point is actually on the curve.
193
+ raise ArgumentError, "Invalid point" if y*y+x*x != F1+D*x*x*y*y
194
+ initpoint(x, y)
195
+ end
196
+
197
+ # Decode a point representation.
198
+ def decode(s)
199
+ x, y = decode_base(s, B)
200
+ return nil if x.nil?
201
+ return JOSE::JWA::Edwards448Point.new(x, y)
202
+ end
203
+
204
+ # Encode a point representation.
205
+ def encode
206
+ return encode_base(B)
207
+ end
208
+
209
+ def normalize
210
+ xp, yp, zp = @x / @z, @y / @z, @z / @z
211
+ tmp = zero_elem
212
+ tmp.x, tmp.y, tmp.z = xp, yp, zp
213
+ return tmp
214
+ end
215
+
216
+ # Construct neutral point on this curve.
217
+ def zero_elem
218
+ return JOSE::JWA::Edwards448Point.new(F0, F1)
219
+ end
220
+
221
+ # Solve for x^2.
222
+ def solve_x2(y)
223
+ return ((y*y-F1)/(D*y*y-F1))
224
+ end
225
+
226
+ # Point addition.
227
+ def +(y)
228
+ # The formulas are from EFD.
229
+ tmp = zero_elem
230
+ xcp, ycp, zcp = @x * y.x, @y * y.y, @z * y.z
231
+ b = zcp * zcp
232
+ e = D * xcp * ycp
233
+ f, g = b - e, b + e
234
+ tmp.x = zcp * f * ((@x + @y) * (y.x + y.y) - xcp - ycp)
235
+ tmp.y, tmp.z = zcp * g * (ycp - xcp), f * g
236
+ return tmp
237
+ end
238
+
239
+ # Point doubling.
240
+ def double
241
+ # The formulas are from EFD.
242
+ tmp = zero_elem
243
+ x1s, y1s, z1s = @x * @x, @y * @y, @z * @z
244
+ xys = @x + @y
245
+ f = x1s + y1s
246
+ j = f - (z1s + z1s)
247
+ tmp.x, tmp.y, tmp.z = (xys * xys - x1s - y1s) * j, f * (x1s - y1s), f * j
248
+ return tmp
249
+ end
250
+
251
+ def inspect
252
+ "\n{#{@x.x},\n"\
253
+ " #{@y.x},\n"\
254
+ " #{@z.x}}"
255
+ end
256
+
257
+ end
@@ -0,0 +1,161 @@
1
+ class JOSE::JWA::FieldElement
2
+ include Comparable
3
+
4
+ attr_reader :x, :p
5
+
6
+ def initialize(x, p)
7
+ @p = p.to_bn
8
+ @x = x.to_bn % @p
9
+ @x = (@x.to_i % @p).to_bn if @x < 0
10
+ end
11
+
12
+ def <=>(y)
13
+ return nil if not y.is_a?(JOSE::JWA::FieldElement)
14
+ return @p <=> y.p if @p != y.p
15
+ return value <=> y.value
16
+ end
17
+
18
+ def +(y)
19
+ check_field_element(y)
20
+ return make(@x+y.x)
21
+ end
22
+
23
+ def **(y)
24
+ check_field_element(y)
25
+ return make(@x**y.x)
26
+ end
27
+
28
+ def -(y)
29
+ check_field_element(y)
30
+ return make(@p+@x-y.x)
31
+ end
32
+
33
+ def -@
34
+ return make(@p-@x)
35
+ end
36
+
37
+ def *(y)
38
+ check_field_element(y)
39
+ return make(@x*y.x)
40
+ end
41
+
42
+ def /(y)
43
+ check_field_element(y)
44
+ return self*y.inv()
45
+ end
46
+
47
+ def &(y)
48
+ ival = y.x.to_i if y.is_a?(JOSE::JWA::FieldElement) and check_field_element(y)
49
+ ival ||= y
50
+ return make(@x.to_i & ival)
51
+ end
52
+
53
+ def |(y)
54
+ ival = y.x.to_i if y.is_a?(JOSE::JWA::FieldElement) and check_field_element(y)
55
+ ival ||= y
56
+ return make(@x.to_i | ival)
57
+ end
58
+
59
+ def ^(y)
60
+ ival = y.x.to_i if y.is_a?(JOSE::JWA::FieldElement) and check_field_element(y)
61
+ ival ||= y
62
+ return make(@x.to_i ^ ival)
63
+ end
64
+
65
+ def ~@
66
+ return make(~@x.to_i)
67
+ end
68
+
69
+ def <<(y)
70
+ ival = y.x.to_i if y.is_a?(JOSE::JWA::FieldElement) and check_field_element(y)
71
+ ival ||= y
72
+ return make(@x.to_i << ival)
73
+ end
74
+
75
+ def >>(y)
76
+ ival = y.x.to_i if y.is_a?(JOSE::JWA::FieldElement) and check_field_element(y)
77
+ ival ||= y
78
+ return make(@x.to_i >> ival)
79
+ end
80
+
81
+ def inv
82
+ return make(@x.mod_inverse(@p))
83
+ end
84
+
85
+ def sqr
86
+ return self*self
87
+ end
88
+
89
+ def sqrt
90
+ y = nil
91
+ # Compute candidate square root.
92
+ if (@p % 4) == 3
93
+ y = JOSE::JWA::FieldElement.sqrt4k3(@x,@p)
94
+ elsif (@p % 8) == 5
95
+ y = JOSE::JWA::FieldElement.sqrt8k5(@x,@p)
96
+ else
97
+ raise NotImplementedError, 'sqrt(_,8k+1)'
98
+ end
99
+ # Check square root candidate valid.
100
+ return y if y*y == self
101
+ return nil
102
+ end
103
+
104
+ def make(ival)
105
+ return JOSE::JWA::FieldElement.new(ival,@p)
106
+ end
107
+
108
+ def sign
109
+ return @x%2
110
+ end
111
+
112
+ def value
113
+ return (@p-@x)*(-1) if negative?
114
+ return @x
115
+ end
116
+
117
+ def from_bytes(x, b)
118
+ x = x.pack(JOSE::JWA::UCHAR_PACK) if x.is_a?(Array)
119
+ rv = OpenSSL::BN.new(x.reverse, 2)# % (2**(b-1))
120
+
121
+ raise ArgumentError, "x is larger than or equal to p (#{rv})" if rv >= @p
122
+ return make(rv)
123
+ end
124
+
125
+ def to_bytes(b)
126
+ return @x.to_s(2).rjust(b.to_i.div(8), JOSE::JWA::ZERO_PAD).reverse
127
+ end
128
+
129
+ def negative?
130
+ return !!(sign.zero? and not zero?)
131
+ end
132
+
133
+ def positive?
134
+ return !negative?
135
+ end
136
+
137
+ def zero?
138
+ return @x.zero?
139
+ end
140
+
141
+ def self.sqrt4k3(x, p)
142
+ return self.new(x.mod_exp(((p+1)/4)[0], p), p)
143
+ end
144
+
145
+ def self.sqrt8k5(x, p)
146
+ y = x.mod_exp(((p+3)/8)[0], p)
147
+ # If the square root exists, it is either y, or y*2^(p-1)/4.
148
+ if y.mod_sqr(p) == (x % p)
149
+ return self.new(y, p)
150
+ else
151
+ z = 2.to_bn.mod_exp(((p-1)/4)[0], p)
152
+ return self.new(y.mod_mul(z, p), p)
153
+ end
154
+ end
155
+
156
+ private
157
+ def check_field_element(y)
158
+ raise ArgumentError, "fields don't match" if not y.is_a?(JOSE::JWA::FieldElement) or @p != y.p
159
+ return true
160
+ end
161
+ end
@@ -0,0 +1,150 @@
1
+ module JOSE::JWA::SHA3
2
+
3
+ extend self
4
+
5
+ ROUNDS5 = (0...5).to_a.freeze
6
+ ROUNDS23 = (0...23).to_a.freeze
7
+ ROUNDS24 = (0...24).to_a.freeze
8
+ ROUNDS25 = (0...25).to_a.freeze
9
+ ROUNDSBY5 = [0, 5, 10, 15, 20].freeze
10
+
11
+ ROTATIONS = [
12
+ 0,1,62,28,27,36,44,6,55,20,3,10,43,25,39,41,45,15,
13
+ 21,8,18,2,61,56,14
14
+ ].freeze
15
+
16
+ PERMUTATION = [
17
+ 1,6,9,22,14,20,2,12,13,19,23,15,4,24,21,8,16,5,3,
18
+ 18,17,11,7,10
19
+ ].freeze
20
+
21
+ RC = [
22
+ 0x0000000000000001,0x0000000000008082,0x800000000000808a,
23
+ 0x8000000080008000,0x000000000000808b,0x0000000080000001,
24
+ 0x8000000080008081,0x8000000000008009,0x000000000000008a,
25
+ 0x0000000000000088,0x0000000080008009,0x000000008000000a,
26
+ 0x000000008000808b,0x800000000000008b,0x8000000000008089,
27
+ 0x8000000000008003,0x8000000000008002,0x8000000000000080,
28
+ 0x000000000000800a,0x800000008000000a,0x8000000080008081,
29
+ 0x8000000000008080,0x0000000080000001,0x8000000080008008
30
+ ].freeze
31
+
32
+ # Rotate a word x by b places to the left.
33
+ def rol(x, b)
34
+ return ((x << b) | (x >> (64 - b))) & (2**64-1)
35
+ end
36
+
37
+ # Do the SHA-3 state transform on state s.
38
+ def sha3_transform(s)
39
+ ROUNDS24.each do |rnd|
40
+ # AddColumnParity (Theta)
41
+ c = [0]*5
42
+ d = [0]*5
43
+ ROUNDS25.each do |i|
44
+ c[i % 5] ^= s[i]
45
+ end
46
+ ROUNDS5.each do |i|
47
+ d[i] = c[(i+4) % 5] ^ rol(c[(i+1) % 5], 1)
48
+ end
49
+ ROUNDS25.each do |i|
50
+ s[i] ^= d[i % 5]
51
+ end
52
+ # RotateWords (Rho).
53
+ ROUNDS25.each do |i|
54
+ s[i] = rol(s[i], ROTATIONS[i])
55
+ end
56
+ # PermuteWords (Pi)
57
+ t = s[PERMUTATION[0]]
58
+ ROUNDS23.each do |i|
59
+ s[PERMUTATION[i]] = s[PERMUTATION[i+1]]
60
+ end
61
+ s[PERMUTATION[-1]] = t
62
+ # NonlinearMixRows (Chi)
63
+ ROUNDSBY5.each do |i|
64
+ t = [s[i],s[i+1],s[i+2],s[i+3],s[i+4],s[i],s[i+1]]
65
+ ROUNDS5.each do |j|
66
+ s[i+j] = t[j]^((~t[j+1])&(t[j+2]))
67
+ end
68
+ end
69
+ # AddRoundConstant (Iota)
70
+ s[0] ^= RC[rnd]
71
+ end
72
+ return s
73
+ end
74
+
75
+ # Reinterpret octet array b to word array and XOR it to state s.
76
+ def reinterpret_to_words_and_xor(s, b)
77
+ (0...(b.length/8)).each do |j|
78
+ block = b[(8*j)..-1][0...8]
79
+ block = block.pack(JOSE::JWA::UCHAR_PACK) if block.is_a?(Array)
80
+ s[j] ^= OpenSSL::BN.new(block.reverse, 2).to_i
81
+ end
82
+ return s
83
+ end
84
+
85
+ # Reinterpret word array w to octet array and return it.
86
+ def reinterpret_to_octets(w)
87
+ mp = ''.force_encoding('BINARY')
88
+ (0...(w.length)).each do |j|
89
+ mp << OpenSSL::BN.new(w[j]).to_s(2).rjust(8, JOSE::JWA::ZERO_PAD).reverse
90
+ end
91
+ return mp
92
+ end
93
+
94
+ # (semi-)generic SHA-3 implementation
95
+ def sha3_raw(msg, r_w, o_p, e_b)
96
+ r_b = 8 * r_w
97
+ s = [0]*25
98
+ # Handle whole blocks.
99
+ idx = 0
100
+ blocks = msg.bytesize / r_b
101
+ (0...blocks).each do |i|
102
+ reinterpret_to_words_and_xor(s, msg[idx..-1][0...r_b])
103
+ idx += r_b
104
+ sha3_transform(s)
105
+ end
106
+ # Handle last block padding.
107
+ m = msg[idx..-1].unpack(JOSE::JWA::UCHAR_PACK)
108
+ m.push(o_p)
109
+ while m.length < r_b
110
+ m.push(0)
111
+ end
112
+ m[-1] |= 128
113
+ # Handle padded last block.
114
+ reinterpret_to_words_and_xor(s, m)
115
+ sha3_transform(s)
116
+ # Output.
117
+ out = ''.force_encoding('BINARY')
118
+ while out.length < e_b
119
+ out << reinterpret_to_octets(s[0...r_w])
120
+ sha3_transform(s)
121
+ end
122
+ return out[0...e_b]
123
+ end
124
+
125
+ # Implementations of actual SHA-3 functions.
126
+ def sha3_224(msg)
127
+ return sha3_raw(msg,18,6,28)
128
+ end
129
+
130
+ def sha3_256(msg)
131
+ return sha3_raw(msg,17,6,32)
132
+ end
133
+
134
+ def sha3_384(msg)
135
+ return sha3_raw(msg,13,6,48)
136
+ end
137
+
138
+ def sha3_512(msg)
139
+ return sha3_raw(msg,9,6,64)
140
+ end
141
+
142
+ def shake128(msg,olen)
143
+ return sha3_raw(msg,21,31,olen)
144
+ end
145
+
146
+ def shake256(msg,olen)
147
+ return sha3_raw(msg,17,31,olen)
148
+ end
149
+
150
+ end