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