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,52 @@
|
|
1
|
+
module JOSE::JWA::Curve25519_Unsupported
|
2
|
+
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def __ruby__?; true; end
|
6
|
+
def __supported__?; false; end
|
7
|
+
|
8
|
+
def ed25519_keypair(secret = nil)
|
9
|
+
raise NotImplementedError
|
10
|
+
end
|
11
|
+
|
12
|
+
def ed25519_secret_to_public(sk)
|
13
|
+
raise NotImplementedError
|
14
|
+
end
|
15
|
+
|
16
|
+
def ed25519_sign(m, sk)
|
17
|
+
raise NotImplementedError
|
18
|
+
end
|
19
|
+
|
20
|
+
def ed25519_verify(sig, m, pk)
|
21
|
+
raise NotImplementedError
|
22
|
+
end
|
23
|
+
|
24
|
+
def ed25519ph_keypair(secret = nil)
|
25
|
+
raise NotImplementedError
|
26
|
+
end
|
27
|
+
|
28
|
+
def ed25519ph_secret_to_public(sk)
|
29
|
+
raise NotImplementedError
|
30
|
+
end
|
31
|
+
|
32
|
+
def ed25519ph_sign(m, sk)
|
33
|
+
raise NotImplementedError
|
34
|
+
end
|
35
|
+
|
36
|
+
def ed25519ph_verify(sig, m, pk)
|
37
|
+
raise NotImplementedError
|
38
|
+
end
|
39
|
+
|
40
|
+
def x25519_keypair(secret = nil)
|
41
|
+
raise NotImplementedError
|
42
|
+
end
|
43
|
+
|
44
|
+
def x25519_secret_to_public(sk)
|
45
|
+
raise NotImplementedError
|
46
|
+
end
|
47
|
+
|
48
|
+
def x25519_shared_secret(pk, sk)
|
49
|
+
raise NotImplementedError
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module JOSE::JWA::Curve448
|
2
|
+
|
3
|
+
extend self
|
4
|
+
|
5
|
+
MUTEX = Mutex.new
|
6
|
+
|
7
|
+
@__implementations__ = []
|
8
|
+
@__ruby_implementations__ = []
|
9
|
+
|
10
|
+
def __implementation__
|
11
|
+
return MUTEX.synchronize { @__implementation__ ||= __pick_best_implementation__ }
|
12
|
+
end
|
13
|
+
|
14
|
+
def __implementation__=(implementation)
|
15
|
+
return MUTEX.synchronize { @__implementation__ = implementation }
|
16
|
+
end
|
17
|
+
|
18
|
+
def __register__(implementation, ruby = false)
|
19
|
+
MUTEX.synchronize {
|
20
|
+
if ruby
|
21
|
+
@__ruby_implementations__.unshift(implementation)
|
22
|
+
else
|
23
|
+
@__implementations__.unshift(implementation)
|
24
|
+
end
|
25
|
+
__config_change__(false)
|
26
|
+
implementation
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
def __config_change__(lock = true)
|
31
|
+
MUTEX.lock if lock
|
32
|
+
@__implementation__ = __pick_best_implementation__ if @__implementation__.nil? or @__implementation__.__ruby__? or not @__implementation__.__supported__?
|
33
|
+
MUTEX.unlock if lock
|
34
|
+
end
|
35
|
+
|
36
|
+
def ed448_keypair(secret = nil)
|
37
|
+
return (@__implementation__ || __implementation__).ed448_keypair(secret)
|
38
|
+
end
|
39
|
+
|
40
|
+
def ed448_secret_to_public(sk)
|
41
|
+
return (@__implementation__ || __implementation__).ed448_secret_to_public(sk)
|
42
|
+
end
|
43
|
+
|
44
|
+
def ed448_sign(m, sk)
|
45
|
+
return (@__implementation__ || __implementation__).ed448_sign(m, sk)
|
46
|
+
end
|
47
|
+
|
48
|
+
def ed448_verify(sig, m, pk)
|
49
|
+
return (@__implementation__ || __implementation__).ed448_verify(sig, m, pk)
|
50
|
+
end
|
51
|
+
|
52
|
+
def ed448ph_keypair(secret = nil)
|
53
|
+
return (@__implementation__ || __implementation__).ed448ph_keypair(secret)
|
54
|
+
end
|
55
|
+
|
56
|
+
def ed448ph_secret_to_public(sk)
|
57
|
+
return (@__implementation__ || __implementation__).ed448ph_secret_to_public(sk)
|
58
|
+
end
|
59
|
+
|
60
|
+
def ed448ph_sign(m, sk)
|
61
|
+
return (@__implementation__ || __implementation__).ed448ph_sign(m, sk)
|
62
|
+
end
|
63
|
+
|
64
|
+
def ed448ph_verify(sig, m, pk)
|
65
|
+
return (@__implementation__ || __implementation__).ed448ph_verify(sig, m, pk)
|
66
|
+
end
|
67
|
+
|
68
|
+
def x448_keypair(secret = nil)
|
69
|
+
return (@__implementation__ || __implementation__).x448_keypair(secret)
|
70
|
+
end
|
71
|
+
|
72
|
+
def x448_secret_to_public(sk)
|
73
|
+
return (@__implementation__ || __implementation__).x448_secret_to_public(sk)
|
74
|
+
end
|
75
|
+
|
76
|
+
def x448_shared_secret(pk, sk)
|
77
|
+
return (@__implementation__ || __implementation__).x448_shared_secret(pk, sk)
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
def __pick_best_implementation__
|
82
|
+
implementation = nil
|
83
|
+
implementation = @__implementations__.detect do |implementation|
|
84
|
+
next implementation.__supported__?
|
85
|
+
end
|
86
|
+
implementation ||= @__ruby_implementations__.detect do |implementation|
|
87
|
+
next implementation.__supported__?
|
88
|
+
end
|
89
|
+
implementation ||= JOSE::JWA::Curve448_Unsupported
|
90
|
+
return implementation
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
require 'jose/jwa/ed448'
|
96
|
+
require 'jose/jwa/x448'
|
97
|
+
|
98
|
+
require 'jose/jwa/curve448_unsupported'
|
99
|
+
require 'jose/jwa/curve448_ruby'
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module JOSE::JWA::Curve448_Ruby
|
2
|
+
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def __ruby__?; true; end
|
6
|
+
def __supported__?; JOSE.__crypto_fallback__; end
|
7
|
+
|
8
|
+
def ed448_keypair(secret = nil)
|
9
|
+
return JOSE::JWA::Ed448.keypair(secret)
|
10
|
+
end
|
11
|
+
|
12
|
+
def ed448_secret_to_public(sk)
|
13
|
+
return JOSE::JWA::Ed448.sk_to_pk(sk)
|
14
|
+
end
|
15
|
+
|
16
|
+
def ed448_sign(m, sk)
|
17
|
+
return JOSE::JWA::Ed448.sign(m, sk)
|
18
|
+
end
|
19
|
+
|
20
|
+
def ed448_verify(sig, m, pk)
|
21
|
+
return JOSE::JWA::Ed448.verify(sig, m, pk)
|
22
|
+
end
|
23
|
+
|
24
|
+
def ed448ph_keypair(secret = nil)
|
25
|
+
return JOSE::JWA::Ed448.keypair(secret)
|
26
|
+
end
|
27
|
+
|
28
|
+
def ed448ph_secret_to_public(sk)
|
29
|
+
return JOSE::JWA::Ed448.sk_to_pk(sk)
|
30
|
+
end
|
31
|
+
|
32
|
+
def ed448ph_sign(m, sk)
|
33
|
+
return JOSE::JWA::Ed448.sign_ph(m, sk)
|
34
|
+
end
|
35
|
+
|
36
|
+
def ed448ph_verify(sig, m, pk)
|
37
|
+
return JOSE::JWA::Ed448.verify_ph(sig, m, pk)
|
38
|
+
end
|
39
|
+
|
40
|
+
def x448_keypair(secret = nil)
|
41
|
+
return JOSE::JWA::X448.keypair(secret)
|
42
|
+
end
|
43
|
+
|
44
|
+
def x448_secret_to_public(sk)
|
45
|
+
return JOSE::JWA::X448.sk_to_pk(sk)
|
46
|
+
end
|
47
|
+
|
48
|
+
def x448_shared_secret(pk, sk)
|
49
|
+
return JOSE::JWA::X448.shared_secret(pk, sk)
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
JOSE::JWA::Curve448.__register__(JOSE::JWA::Curve448_Ruby, true)
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module JOSE::JWA::Curve448_Unsupported
|
2
|
+
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def __ruby__?; true; end
|
6
|
+
def __supported__?; false; end
|
7
|
+
|
8
|
+
def ed448_keypair(secret = nil)
|
9
|
+
raise NotImplementedError
|
10
|
+
end
|
11
|
+
|
12
|
+
def ed448_secret_to_public(sk)
|
13
|
+
raise NotImplementedError
|
14
|
+
end
|
15
|
+
|
16
|
+
def ed448_sign(m, sk)
|
17
|
+
raise NotImplementedError
|
18
|
+
end
|
19
|
+
|
20
|
+
def ed448_verify(sig, m, pk)
|
21
|
+
raise NotImplementedError
|
22
|
+
end
|
23
|
+
|
24
|
+
def ed448ph_keypair(secret = nil)
|
25
|
+
raise NotImplementedError
|
26
|
+
end
|
27
|
+
|
28
|
+
def ed448ph_secret_to_public(sk)
|
29
|
+
raise NotImplementedError
|
30
|
+
end
|
31
|
+
|
32
|
+
def ed448ph_sign(m, sk)
|
33
|
+
raise NotImplementedError
|
34
|
+
end
|
35
|
+
|
36
|
+
def ed448ph_verify(sig, m, pk)
|
37
|
+
raise NotImplementedError
|
38
|
+
end
|
39
|
+
|
40
|
+
def x448_keypair(secret = nil)
|
41
|
+
raise NotImplementedError
|
42
|
+
end
|
43
|
+
|
44
|
+
def x448_secret_to_public(sk)
|
45
|
+
raise NotImplementedError
|
46
|
+
end
|
47
|
+
|
48
|
+
def x448_shared_secret(pk, sk)
|
49
|
+
raise NotImplementedError
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
module JOSE::JWA::Ed25519
|
2
|
+
|
3
|
+
extend self
|
4
|
+
|
5
|
+
C_bits = 256
|
6
|
+
C_bytes = (C_bits + 7) / 8
|
7
|
+
C_secretbytes = C_bytes
|
8
|
+
C_publickeybytes = C_bytes
|
9
|
+
C_secretkeybytes = C_secretbytes + C_publickeybytes
|
10
|
+
C_signaturebytes = C_bytes + C_bytes
|
11
|
+
C_B = JOSE::JWA::Edwards25519Point.stdbase.freeze
|
12
|
+
|
13
|
+
def secret_to_curve25519(secret)
|
14
|
+
raise ArgumentError, "secret must be #{C_secretbytes} bytes" if secret.bytesize != C_secretbytes
|
15
|
+
curve25519_scalar = Digest::SHA512.digest(secret)[0, 32]
|
16
|
+
curve25519_scalar.setbyte(0, curve25519_scalar.getbyte(0) & 248)
|
17
|
+
curve25519_scalar.setbyte(31, (curve25519_scalar.getbyte(31) & 127) | 64)
|
18
|
+
return curve25519_scalar
|
19
|
+
end
|
20
|
+
|
21
|
+
def secret_to_pk(secret)
|
22
|
+
raise ArgumentError, "secret must be #{C_secretbytes} bytes" if secret.bytesize != C_secretbytes
|
23
|
+
return (C_B * OpenSSL::BN.new(secret_to_curve25519(secret).reverse, 2).to_i).encode()
|
24
|
+
end
|
25
|
+
|
26
|
+
def keypair(secret = nil)
|
27
|
+
secret ||= SecureRandom.random_bytes(C_secretbytes)
|
28
|
+
pk = secret_to_pk(secret)
|
29
|
+
sk = secret + pk
|
30
|
+
return pk, sk
|
31
|
+
end
|
32
|
+
|
33
|
+
def sk_to_secret(sk)
|
34
|
+
raise ArgumentError, "sk must be #{C_secretkeybytes} bytes" if sk.bytesize != C_secretkeybytes
|
35
|
+
return sk[0, C_secretbytes]
|
36
|
+
end
|
37
|
+
|
38
|
+
def sk_to_pk(sk)
|
39
|
+
raise ArgumentError, "sk must be #{C_secretkeybytes} bytes" if sk.bytesize != C_secretkeybytes
|
40
|
+
return sk[C_secretbytes, C_secretkeybytes]
|
41
|
+
end
|
42
|
+
|
43
|
+
def sk_to_curve25519(sk)
|
44
|
+
return secret_to_curve25519(sk_to_secret(sk))
|
45
|
+
end
|
46
|
+
|
47
|
+
def pk_to_curve25519(pk)
|
48
|
+
raise ArgumentError, "pk must be #{C_publickeybytes} bytes" if pk.bytesize != C_publickeybytes
|
49
|
+
a = C_B.decode(pk)
|
50
|
+
u = (JOSE::JWA::X25519::C_F_one + a.y) / (JOSE::JWA::X25519::C_F_one - a.y)
|
51
|
+
return u.to_bytes(C_bits)
|
52
|
+
end
|
53
|
+
|
54
|
+
def sign(m, sk)
|
55
|
+
raise ArgumentError, "sk must be #{C_secretkeybytes} bytes" if sk.bytesize != C_secretkeybytes
|
56
|
+
secret, pk = sk[0, 32], sk[32, 64]
|
57
|
+
khash = Digest::SHA512.digest(secret)
|
58
|
+
curve25519_scalar, seed = khash[0, 32], khash[32, 64]
|
59
|
+
curve25519_scalar.setbyte(0, curve25519_scalar.getbyte(0) & 248)
|
60
|
+
curve25519_scalar.setbyte(31, (curve25519_scalar.getbyte(31) & 127) | 64)
|
61
|
+
a_s = OpenSSL::BN.new(curve25519_scalar.reverse, 2).to_i
|
62
|
+
# Calculate r_s and r (r only used in encoded form)
|
63
|
+
r_s = (OpenSSL::BN.new(Digest::SHA512.digest(seed+m).reverse, 2) % JOSE::JWA::Edwards25519Point::L).to_i
|
64
|
+
r = (C_B * r_s).encode()
|
65
|
+
# Calculate h.
|
66
|
+
h = (OpenSSL::BN.new(Digest::SHA512.digest(r+pk+m).reverse, 2) % JOSE::JWA::Edwards25519Point::L).to_i
|
67
|
+
# Calculate s.
|
68
|
+
s = OpenSSL::BN.new((r_s+h*a_s) % JOSE::JWA::Edwards25519Point::L).to_s(2).rjust(C_bytes, JOSE::JWA::ZERO_PAD).reverse
|
69
|
+
# The final signature is concatenation of r and s.
|
70
|
+
return r+s
|
71
|
+
end
|
72
|
+
|
73
|
+
def sign_ph(m, sk)
|
74
|
+
return sign(Digest::SHA512.digest(m), sk)
|
75
|
+
end
|
76
|
+
|
77
|
+
def verify(sig, m, pk)
|
78
|
+
# Sanity-check sizes.
|
79
|
+
return false if sig.bytesize != C_signaturebytes
|
80
|
+
return false if pk.bytesize != C_publickeybytes
|
81
|
+
# Split signature into R and S, and parse.
|
82
|
+
r, s = sig[0, 32], sig[32, 64]
|
83
|
+
r_p, s_s = C_B.decode(r), OpenSSL::BN.new(s.reverse, 2).to_i
|
84
|
+
# Parse public key.
|
85
|
+
a_p = C_B.decode(pk)
|
86
|
+
# Check parse results.
|
87
|
+
return false if r_p.nil? or a_p.nil? or s_s > JOSE::JWA::Edwards25519Point::L
|
88
|
+
# Calculate h.
|
89
|
+
h = (OpenSSL::BN.new(Digest::SHA512.digest(r+pk+m).reverse, 2) % JOSE::JWA::Edwards25519Point::L).to_i
|
90
|
+
# Calculate left and right sides of check eq.
|
91
|
+
rhs = r_p + (a_p * h)
|
92
|
+
lhs = C_B * s_s
|
93
|
+
JOSE::JWA::Edwards25519Point::C.times do
|
94
|
+
lhs = lhs.double()
|
95
|
+
rhs = rhs.double()
|
96
|
+
end
|
97
|
+
# Check eq. holds?
|
98
|
+
return lhs == rhs
|
99
|
+
end
|
100
|
+
|
101
|
+
# def verify(sig, m, pk)
|
102
|
+
# return false if sig.bytesize != C_signaturebytes
|
103
|
+
# return false if pk.bytesize != C_publickeybytes
|
104
|
+
# r, s = sig[0, 32], sig[32, 64]
|
105
|
+
# a = C_B.decode(pk)
|
106
|
+
# k = (OpenSSL::BN.new(Digest::SHA512.digest(r+pk+m).reverse, 2) % JOSE::JWA::Edwards25519Point::L).to_i
|
107
|
+
# s_s = (OpenSSL::BN.new(s.reverse, 2)).to_i
|
108
|
+
# lhs = C_B * s_s
|
109
|
+
# rhs = C_B.decode(r) + (a * k)
|
110
|
+
# return lhs == rhs
|
111
|
+
# end
|
112
|
+
|
113
|
+
def verify_ph(sig, m, pk)
|
114
|
+
return verify(sig, Digest::SHA512.digest(m), pk)
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module JOSE::JWA::Ed25519_RbNaCl
|
2
|
+
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def keypair(secret = nil)
|
6
|
+
secret ||= RbNaCl::Random.random_bytes(RbNaCl::Signatures::Ed25519::SEEDBYTES)
|
7
|
+
RbNaCl::Util.check_length(secret, RbNaCl::Signatures::Ed25519::SEEDBYTES, "secret")
|
8
|
+
pk = RbNaCl::Util.zeros(RbNaCl::Signatures::Ed25519::VERIFYKEYBYTES)
|
9
|
+
sk = RbNaCl::Util.zeros(RbNaCl::Signatures::Ed25519::SIGNINGKEYBYTES)
|
10
|
+
RbNaCl::Signatures::Ed25519::SigningKey.sign_ed25519_seed_keypair(pk, sk, secret) || fail(RbNaCl::CryptoError, "Failed to generate a key pair")
|
11
|
+
return pk, sk
|
12
|
+
end
|
13
|
+
|
14
|
+
def sk_to_pk(sk)
|
15
|
+
return sk[RbNaCl::Signatures::Ed25519::VERIFYKEYBYTES..-1]
|
16
|
+
end
|
17
|
+
|
18
|
+
def sign(m, sk)
|
19
|
+
signing_key = RbNaCl::Signatures::Ed25519::SigningKey.allocate
|
20
|
+
signing_key.instance_variable_set(:@signing_key, sk)
|
21
|
+
return signing_key.sign(m)
|
22
|
+
end
|
23
|
+
|
24
|
+
def sign_ph(m, sk)
|
25
|
+
return sign(RbNaCl::Hash.sha512(m), sk)
|
26
|
+
end
|
27
|
+
|
28
|
+
def verify(sig, m, pk)
|
29
|
+
return RbNaCl::Signatures::Ed25519::VerifyKey.new(pk).verify(sig, m)
|
30
|
+
end
|
31
|
+
|
32
|
+
def verify_ph(sig, m, pk)
|
33
|
+
return verify(sig, RbNaCl::Hash.sha512(m), pk)
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,166 @@
|
|
1
|
+
module JOSE::JWA::Ed448
|
2
|
+
|
3
|
+
extend self
|
4
|
+
|
5
|
+
C_bits = 456
|
6
|
+
C_bytes = (C_bits + 7) / 8
|
7
|
+
C_secretbytes = C_bytes
|
8
|
+
C_legacysecretbytes = 32
|
9
|
+
C_publickeybytes = C_bytes
|
10
|
+
C_secretkeybytes = C_secretbytes + C_publickeybytes
|
11
|
+
C_legacysecretkeybytes = C_legacysecretbytes + C_publickeybytes
|
12
|
+
C_signaturebytes = C_bytes + C_bytes
|
13
|
+
C_B = JOSE::JWA::Edwards448Point.stdbase.freeze
|
14
|
+
|
15
|
+
def secret_to_curve448(secret)
|
16
|
+
raise ArgumentError, "secret must be #{C_secretbytes} bytes" if secret.bytesize != C_secretbytes and secret.bytesize != C_legacysecretbytes
|
17
|
+
curve448_scalar = JOSE::JWA::SHA3.shake256(secret, 114)[0, 56]
|
18
|
+
curve448_scalar.setbyte(0, curve448_scalar.getbyte(0) & 252)
|
19
|
+
curve448_scalar.setbyte(55, curve448_scalar.getbyte(55) | 128)
|
20
|
+
return curve448_scalar
|
21
|
+
end
|
22
|
+
|
23
|
+
def secret_to_pk(secret)
|
24
|
+
raise ArgumentError, "secret must be #{C_secretbytes} bytes" if secret.bytesize != C_secretbytes and secret.bytesize != C_legacysecretbytes
|
25
|
+
return (C_B * OpenSSL::BN.new(secret_to_curve448(secret).reverse, 2).to_i).encode()
|
26
|
+
end
|
27
|
+
|
28
|
+
def keypair(secret = nil)
|
29
|
+
secret ||= SecureRandom.random_bytes(C_secretbytes)
|
30
|
+
pk = secret_to_pk(secret)
|
31
|
+
sk = secret + pk
|
32
|
+
return pk, sk
|
33
|
+
end
|
34
|
+
|
35
|
+
def sk_to_secret(sk)
|
36
|
+
return sk[0, C_secretbytes] if sk.bytesize == C_secretkeybytes
|
37
|
+
return sk[0, C_legacysecretbytes] if sk.bytesize == C_legacysecretkeybytes
|
38
|
+
raise ArgumentError, "sk must be #{C_secretkeybytes} bytes"
|
39
|
+
end
|
40
|
+
|
41
|
+
def sk_to_pk(sk)
|
42
|
+
return sk[C_secretbytes, C_secretkeybytes] if sk.bytesize == C_secretkeybytes
|
43
|
+
return sk[C_legacysecretbytes, C_legacysecretkeybytes] if sk.bytesize == C_legacysecretkeybytes
|
44
|
+
raise ArgumentError, "sk must be #{C_secretkeybytes} bytes"
|
45
|
+
end
|
46
|
+
|
47
|
+
def sk_to_curve448(sk)
|
48
|
+
return secret_to_curve448(sk_to_secret(sk))
|
49
|
+
end
|
50
|
+
|
51
|
+
def pk_to_curve448(pk)
|
52
|
+
raise ArgumentError, "pk must be #{C_publickeybytes} bytes" if pk.bytesize != C_publickeybytes
|
53
|
+
a = C_B.decode(pk)
|
54
|
+
u = a.y.sqr / a.x.sqr
|
55
|
+
return u.to_bytes(448)
|
56
|
+
end
|
57
|
+
|
58
|
+
def sign(m, sk, ctx = nil)
|
59
|
+
raise ArgumentError, "sk must be #{C_secretkeybytes} bytes" if sk.bytesize != C_secretkeybytes and sk.bytesize != C_legacysecretkeybytes
|
60
|
+
ctx ||= ''
|
61
|
+
raise ArgumentError, "ctx must be 255 bytes or smaller" if ctx.bytesize > 255
|
62
|
+
secret, pk = nil, nil
|
63
|
+
if sk.bytesize == C_secretkeybytes
|
64
|
+
secret, pk = sk[0, 57], sk[57, 114]
|
65
|
+
elsif sk.bytesize == C_legacysecretkeybytes
|
66
|
+
secret, pk = sk[0, 32], sk[32, 89]
|
67
|
+
end
|
68
|
+
khash = JOSE::JWA::SHA3.shake256(secret, 114)
|
69
|
+
curve448_scalar, seed = khash[0, 57], khash[57, 114]
|
70
|
+
curve448_scalar.setbyte(0, curve448_scalar.getbyte(0) & 252)
|
71
|
+
curve448_scalar.setbyte(55, curve448_scalar.getbyte(55) | 128)
|
72
|
+
curve448_scalar.setbyte(56, 0)
|
73
|
+
a_s = OpenSSL::BN.new(curve448_scalar.reverse, 2).to_i
|
74
|
+
# Calculate r_s and r (r only used in encoded form)
|
75
|
+
r_s = (OpenSSL::BN.new(JOSE::JWA::SHA3.shake256(['SigEd448', 0, ctx.bytesize, ctx, seed, m].pack('A*CCA*A*A*'), 114).reverse, 2) % JOSE::JWA::Edwards448Point::L).to_i
|
76
|
+
r = (C_B * r_s).encode()
|
77
|
+
# Calculate h.
|
78
|
+
h = (OpenSSL::BN.new(JOSE::JWA::SHA3.shake256(['SigEd448', 0, ctx.bytesize, ctx, r, pk, m].pack('A*CCA*A*A*A*'), 114).reverse, 2) % JOSE::JWA::Edwards448Point::L).to_i
|
79
|
+
# Calculate s.
|
80
|
+
s = OpenSSL::BN.new((r_s+h*a_s) % JOSE::JWA::Edwards448Point::L).to_s(2).rjust(C_bytes, JOSE::JWA::ZERO_PAD).reverse
|
81
|
+
# The final signature is concatenation of r and s.
|
82
|
+
return r+s
|
83
|
+
end
|
84
|
+
|
85
|
+
def sign_ph(m, sk, ctx = nil)
|
86
|
+
raise ArgumentError, "sk must be #{C_secretkeybytes} bytes" if sk.bytesize != C_secretkeybytes and sk.bytesize != C_legacysecretkeybytes
|
87
|
+
ctx ||= ''
|
88
|
+
raise ArgumentError, "ctx must be 255 bytes or smaller" if ctx.bytesize > 255
|
89
|
+
m = JOSE::JWA::SHA3.shake256(['SigEd448', 2, ctx.bytesize, ctx, m].pack('A*CCA*A*'), 64)
|
90
|
+
secret, pk = nil, nil
|
91
|
+
if sk.bytesize == C_secretkeybytes
|
92
|
+
secret, pk = sk[0, 57], sk[57, 114]
|
93
|
+
elsif sk.bytesize == C_legacysecretkeybytes
|
94
|
+
secret, pk = sk[0, 32], sk[32, 89]
|
95
|
+
end
|
96
|
+
khash = JOSE::JWA::SHA3.shake256(secret, 114)
|
97
|
+
curve448_scalar, seed = khash[0, 57], khash[57, 114]
|
98
|
+
curve448_scalar.setbyte(0, curve448_scalar.getbyte(0) & 252)
|
99
|
+
curve448_scalar.setbyte(55, curve448_scalar.getbyte(55) | 128)
|
100
|
+
curve448_scalar.setbyte(56, 0)
|
101
|
+
a_s = OpenSSL::BN.new(curve448_scalar.reverse, 2).to_i
|
102
|
+
# Calculate r_s and r (r only used in encoded form)
|
103
|
+
r_s = (OpenSSL::BN.new(JOSE::JWA::SHA3.shake256(['SigEd448', 1, ctx.bytesize, ctx, seed, m].pack('A*CCA*A*A*'), 114).reverse, 2) % JOSE::JWA::Edwards448Point::L).to_i
|
104
|
+
r = (C_B * r_s).encode()
|
105
|
+
# Calculate h.
|
106
|
+
h = (OpenSSL::BN.new(JOSE::JWA::SHA3.shake256(['SigEd448', 1, ctx.bytesize, ctx, r, pk, m].pack('A*CCA*A*A*A*'), 114).reverse, 2) % JOSE::JWA::Edwards448Point::L).to_i
|
107
|
+
# Calculate s.
|
108
|
+
s = OpenSSL::BN.new((r_s+h*a_s) % JOSE::JWA::Edwards448Point::L).to_s(2).rjust(C_bytes, JOSE::JWA::ZERO_PAD).reverse
|
109
|
+
# The final signature is concatenation of r and s.
|
110
|
+
return r+s
|
111
|
+
end
|
112
|
+
|
113
|
+
def verify(sig, m, pk, ctx = nil)
|
114
|
+
ctx ||= ''
|
115
|
+
raise ArgumentError, "ctx must be 255 bytes or smaller" if ctx.bytesize > 255
|
116
|
+
# Sanity-check sizes.
|
117
|
+
return false if sig.bytesize != C_signaturebytes
|
118
|
+
return false if pk.bytesize != C_publickeybytes
|
119
|
+
# Split signature into R and S, and parse.
|
120
|
+
r, s = sig[0, 57], sig[57, 114]
|
121
|
+
r_p, s_s = C_B.decode(r), OpenSSL::BN.new(s.reverse, 2).to_i
|
122
|
+
# Parse public key.
|
123
|
+
a_p = C_B.decode(pk)
|
124
|
+
# Check parse results.
|
125
|
+
return false if r_p.nil? or a_p.nil? or s_s > JOSE::JWA::Edwards448Point::L
|
126
|
+
# Calculate h.
|
127
|
+
h = (OpenSSL::BN.new(JOSE::JWA::SHA3.shake256(['SigEd448', 0, ctx.bytesize, ctx, r, pk, m].pack('A*CCA*A*A*A*'), 114).reverse, 2) % JOSE::JWA::Edwards448Point::L).to_i
|
128
|
+
# Calculate left and right sides of check eq.
|
129
|
+
rhs = r_p + (a_p * h)
|
130
|
+
lhs = C_B * s_s
|
131
|
+
JOSE::JWA::Edwards448Point::C.times do
|
132
|
+
lhs = lhs.double()
|
133
|
+
rhs = rhs.double()
|
134
|
+
end
|
135
|
+
# Check eq. holds?
|
136
|
+
return lhs == rhs
|
137
|
+
end
|
138
|
+
|
139
|
+
def verify_ph(sig, m, pk, ctx = nil)
|
140
|
+
ctx ||= ''
|
141
|
+
raise ArgumentError, "ctx must be 255 bytes or smaller" if ctx.bytesize > 255
|
142
|
+
m = JOSE::JWA::SHA3.shake256(['SigEd448', 2, ctx.bytesize, ctx, m].pack('A*CCA*A*'), 64)
|
143
|
+
# Sanity-check sizes.
|
144
|
+
return false if sig.bytesize != C_signaturebytes
|
145
|
+
return false if pk.bytesize != C_publickeybytes
|
146
|
+
# Split signature into R and S, and parse.
|
147
|
+
r, s = sig[0, 57], sig[57, 114]
|
148
|
+
r_p, s_s = C_B.decode(r), OpenSSL::BN.new(s.reverse, 2).to_i
|
149
|
+
# Parse public key.
|
150
|
+
a_p = C_B.decode(pk)
|
151
|
+
# Check parse results.
|
152
|
+
return false if r_p.nil? or a_p.nil? or s_s > JOSE::JWA::Edwards448Point::L
|
153
|
+
# Calculate h.
|
154
|
+
h = (OpenSSL::BN.new(JOSE::JWA::SHA3.shake256(['SigEd448', 1, ctx.bytesize, ctx, r, pk, m].pack('A*CCA*A*A*A*'), 114).reverse, 2) % JOSE::JWA::Edwards448Point::L).to_i
|
155
|
+
# Calculate left and right sides of check eq.
|
156
|
+
rhs = r_p + (a_p * h)
|
157
|
+
lhs = C_B * s_s
|
158
|
+
JOSE::JWA::Edwards448Point::C.times do
|
159
|
+
lhs = lhs.double()
|
160
|
+
rhs = rhs.double()
|
161
|
+
end
|
162
|
+
# Check eq. holds?
|
163
|
+
return lhs == rhs
|
164
|
+
end
|
165
|
+
|
166
|
+
end
|