jose 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.
@@ -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