jose 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/.travis.yml +11 -2
- data/CHANGELOG.md +69 -0
- data/README.md +5 -3
- data/jose.gemspec +3 -2
- data/lib/jose.rb +44 -6
- data/lib/jose/jwa.rb +23 -0
- data/lib/jose/jwa/curve25519.rb +102 -0
- data/lib/jose/jwa/curve25519_rbnacl.rb +67 -0
- data/lib/jose/jwa/curve25519_ruby.rb +54 -0
- data/lib/jose/jwa/curve25519_unsupported.rb +52 -0
- data/lib/jose/jwa/curve448.rb +99 -0
- data/lib/jose/jwa/curve448_ruby.rb +54 -0
- data/lib/jose/jwa/curve448_unsupported.rb +52 -0
- data/lib/jose/jwa/ed25519.rb +117 -0
- data/lib/jose/jwa/ed25519_rbnacl.rb +36 -0
- data/lib/jose/jwa/ed448.rb +166 -0
- data/lib/jose/jwa/edwards_point.rb +257 -0
- data/lib/jose/jwa/field_element.rb +161 -0
- data/lib/jose/jwa/sha3.rb +150 -0
- data/lib/jose/jwa/x25519.rb +188 -0
- data/lib/jose/jwa/x25519_rbnacl.rb +35 -0
- data/lib/jose/jwa/x448.rb +188 -0
- data/lib/jose/jwk.rb +74 -0
- data/lib/jose/jwk/kty.rb +6 -0
- data/lib/jose/jwk/kty_okp_ed25519.rb +112 -0
- data/lib/jose/jwk/kty_okp_ed25519ph.rb +112 -0
- data/lib/jose/jwk/kty_okp_ed448.rb +121 -0
- data/lib/jose/jwk/kty_okp_ed448ph.rb +121 -0
- data/lib/jose/jwk/kty_okp_x25519.rb +120 -0
- data/lib/jose/jwk/kty_okp_x448.rb +120 -0
- data/lib/jose/jws.rb +4 -0
- data/lib/jose/jws/alg.rb +1 -0
- data/lib/jose/jws/alg_eddsa.rb +35 -0
- data/lib/jose/version.rb +1 -1
- metadata +45 -7
@@ -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
|