Skip to content

Commit

Permalink
Add support for CryptoRb ed25519 and x25519 libraries [see #8]
Browse files Browse the repository at this point in the history
  • Loading branch information
potatosalad committed Sep 20, 2018
1 parent 8b8fa70 commit ff432bc
Show file tree
Hide file tree
Showing 11 changed files with 292 additions and 7 deletions.
2 changes: 1 addition & 1 deletion .ruby-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.3.1
2.5.1
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ env:
- RUBYOPT="-W0"

rvm:
- 2.3.1
- 2.5.1

notifications:
email: false

before_install:
- "gem install bundler -v 1.12.2"
- "gem install bundler -v 1.16.4"
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ end
group :test do
gem 'minitest-focus', require: false
gem 'minitest-perf', require: false
gem 'rantly', github: 'abargnesi/rantly', ref: '4d219ea3eb340a5153a43d051b2e22ccb2bce274', require: false
gem 'rantly', github: 'abargnesi/rantly', ref: '2875f63bfc695d270ecb574c56d1a7d8f6af0153', require: false
gem 'simplecov', require: false
if ENV['CI']
gem 'coveralls', require: false
Expand Down
8 changes: 5 additions & 3 deletions jose.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Gem::Specification.new do |spec|
spec.name = "jose"
spec.version = JOSE::VERSION
spec.authors = ["Andrew Bennett"]
spec.email = ["andrew@pixid.com"]
spec.email = ["potatosaladx@gmail.com"]

spec.summary = %q{JSON Object Signing and Encryption}
spec.description = %q{JSON Object Signing and Encryption}
Expand All @@ -29,9 +29,11 @@ Gem::Specification.new do |spec|

spec.add_dependency "hamster"

spec.add_development_dependency "bundler", "~> 1.12"
spec.add_development_dependency "rake", "~> 11.1"
spec.add_development_dependency "bundler", "~> 1.16"
spec.add_development_dependency "rake", "~> 12.3"
spec.add_development_dependency "minitest"
spec.add_development_dependency "json"
spec.add_development_dependency "rbnacl-libsodium"
spec.add_development_dependency "ed25519"
spec.add_development_dependency "x25519"
end
1 change: 1 addition & 0 deletions lib/jose.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
require 'openssl'
require 'securerandom'
require 'thread'
require 'zlib'

# JOSE stands for JSON Object Signing and Encryption which is a is a set of
# standards established by the [JOSE Working Group](https://datatracker.ietf.org/wg/jose).
Expand Down
3 changes: 3 additions & 0 deletions lib/jose/jwa/curve25519.rb
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,13 @@ def __pick_best_implementation__
end

require 'jose/jwa/ed25519'
require 'jose/jwa/ed25519_cryptorb'
require 'jose/jwa/ed25519_rbnacl'
require 'jose/jwa/x25519'
require 'jose/jwa/x25519_cryptorb'
require 'jose/jwa/x25519_rbnacl'

require 'jose/jwa/curve25519_unsupported'
require 'jose/jwa/curve25519_ruby'
require 'jose/jwa/curve25519_cryptorb'
require 'jose/jwa/curve25519_rbnacl'
67 changes: 67 additions & 0 deletions lib/jose/jwa/curve25519_cryptorb.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
module JOSE::JWA::Curve25519_CryptoRb

extend self

def __ruby__?; false; end

def __supported__?
return @supported ||= begin
begin
require 'ed25519'
rescue LoadError
end
begin
require 'x25519'
rescue LoadError
end
!!(defined?(Ed25519::SigningKey) and defined?(X25519::Scalar))
end
end

def ed25519_keypair(secret = nil)
return JOSE::JWA::Ed25519_CryptoRb.keypair(secret)
end

def ed25519_secret_to_public(sk)
return JOSE::JWA::Ed25519_CryptoRb.sk_to_pk(sk)
end

def ed25519_sign(m, sk)
return JOSE::JWA::Ed25519_CryptoRb.sign(m, sk)
end

def ed25519_verify(sig, m, pk)
return JOSE::JWA::Ed25519_CryptoRb.verify(sig, m, pk)
end

def ed25519ph_keypair(secret = nil)
return JOSE::JWA::Ed25519_CryptoRb.keypair(secret)
end

def ed25519ph_secret_to_public(sk)
return JOSE::JWA::Ed25519_CryptoRb.sk_to_pk(sk)
end

def ed25519ph_sign(m, sk)
return JOSE::JWA::Ed25519_CryptoRb.sign_ph(m, sk)
end

def ed25519ph_verify(sig, m, pk)
return JOSE::JWA::Ed25519_CryptoRb.verify_ph(sig, m, pk)
end

def x25519_keypair(secret = nil)
return JOSE::JWA::X25519_CryptoRb.keypair(secret)
end

def x25519_secret_to_public(sk)
return JOSE::JWA::X25519_CryptoRb.sk_to_pk(sk)
end

def x25519_shared_secret(pk, sk)
return JOSE::JWA::X25519_CryptoRb.shared_secret(pk, sk)
end

end

JOSE::JWA::Curve25519.__register__(JOSE::JWA::Curve25519_CryptoRb)
75 changes: 75 additions & 0 deletions lib/jose/jwa/ed25519.rb
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,79 @@ def verify_ph(sig, m, pk)
return verify(sig, Digest::SHA512.digest(m), pk)
end

def coerce_publickey_bytes!(publickey)
raise ArgumentError, "publickey size must be #{C_publickeybytes} bytes" if not valid_publickey?(publickey)
publickey = publickey.to_bytes(C_bits) if publickey.is_a?(JOSE::JWA::FieldElement)
publickey = publickey.to_bn if not publickey.is_a?(OpenSSL::BN) and publickey.respond_to?(:to_bn)
publickey = publickey.to_s(2).rjust(C_bytes, JOSE::JWA::ZERO_PAD).reverse if publickey.is_a?(OpenSSL::BN)
return publickey
end

def coerce_secret_bytes!(secret)
raise ArgumentError, "secret size must be #{C_secretbytes} bytes" if not valid_secret?(secret)
secret = secret.to_bytes(C_bits) if secret.is_a?(JOSE::JWA::FieldElement)
secret = secret.to_bn if not secret.is_a?(OpenSSL::BN) and secret.respond_to?(:to_bn)
secret = secret.to_s(2).rjust(C_bytes, JOSE::JWA::ZERO_PAD).reverse if secret.is_a?(OpenSSL::BN)
return secret
end

def coerce_secretkey_bytes!(secretkey)
raise ArgumentError, "secretkey size must be #{C_secretkeybytes} bytes" if not valid_secretkey?(secretkey)
secretkey = secretkey.to_bytes(C_secretkeybytes * 8) if secretkey.is_a?(JOSE::JWA::FieldElement)
secretkey = secretkey.to_bn if not secretkey.is_a?(OpenSSL::BN) and secretkey.respond_to?(:to_bn)
secretkey = secretkey.to_s(2).rjust(C_secretkeybytes, JOSE::JWA::ZERO_PAD).reverse if secretkey.is_a?(OpenSSL::BN)
return secretkey
end

def valid_publickey?(publickey)
return true if publickey.is_a?(JOSE::JWA::FieldElement) and publickey.p == C_p
if not publickey.is_a?(OpenSSL::BN) and publickey.respond_to?(:to_bn)
publickey = publickey.to_bn
end
pkbytes = 0
if publickey.is_a?(OpenSSL::BN)
if publickey.num_bytes > C_publickeybytes
pkbytes = publickey.num_bytes
else
pkbytes = C_publickeybytes
end
end
pkbytes = publickey.bytesize if publickey.respond_to?(:bytesize)
return !!(pkbytes == C_publickeybytes)
end

def valid_secret?(secret)
return true if secret.is_a?(JOSE::JWA::FieldElement) and secret.p == C_p
if not secret.is_a?(OpenSSL::BN) and secret.respond_to?(:to_bn)
secret = secret.to_bn
end
seedbytes = 0
if secret.is_a?(OpenSSL::BN)
if secret.num_bytes > C_secretbytes
seedbytes = secret.num_bytes
else
seedbytes = C_secretbytes
end
end
seedbytes = secret.bytesize if secret.respond_to?(:bytesize)
return !!(seedbytes == C_secretbytes)
end

def valid_secretkey?(secretkey)
return true if secretkey.is_a?(JOSE::JWA::FieldElement) and secretkey.p == C_p
if not secretkey.is_a?(OpenSSL::BN) and secretkey.respond_to?(:to_bn)
secretkey = secretkey.to_bn
end
skbytes = 0
if secretkey.is_a?(OpenSSL::BN)
if secretkey.num_bytes > C_secretkeybytes
skbytes = secretkey.num_bytes
else
skbytes = C_secretkeybytes
end
end
skbytes = secretkey.bytesize if secretkey.respond_to?(:bytesize)
return !!(skbytes == C_secretkeybytes)
end

end
55 changes: 55 additions & 0 deletions lib/jose/jwa/ed25519_cryptorb.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
module JOSE::JWA::Ed25519_CryptoRb

extend self

def keypair(secret = nil)
secret ||= Ed25519::SigningKey.generate()
sk = coerce_signing_key!(secret)
pk = sk.verify_key()
return pk.to_bytes(), sk.keypair()
end

def sk_to_pk(sk)
return sk[Ed25519::KEY_SIZE..-1]
end

def sign(m, sk)
signing_key = coerce_signing_key!(sk)
return signing_key.sign(m)
end

def sign_ph(m, sk)
return sign(Digest::SHA512.digest(m), sk)
end

def verify(sig, m, pk)
return Ed25519::VerifyKey.new(pk).verify(sig, m)
end

def verify_ph(sig, m, pk)
return verify(sig, Digest::SHA512.digest(m), pk)
end

def coerce_signing_key!(sk)
return sk if sk.is_a?(Ed25519::SigningKey)
sk =
if not sk.respond_to?(:bytesize)
begin
JOSE::JWA::Ed25519.coerce_secret_bytes!(sk)
rescue ArgumentError
JOSE::JWA::Ed25519.coerce_secretkey_bytes!(sk)
end
else
sk
end
return Ed25519::SigningKey.from_keypair(sk) if sk.bytesize === JOSE::JWA::Ed25519::C_secretkeybytes
return Ed25519::SigningKey.new(sk)
end

def coerce_verify_key!(pk)
return pk if pk.is_a?(Ed25519::VerifyKey)
pk = JOSE::JWA::Ed25519.coerce_publickey_bytes!(pk) if not pk.respond_to?(:bytesize)
return Ed25519::VerifyKey.new(pk)
end

end
47 changes: 47 additions & 0 deletions lib/jose/jwa/x25519_cryptorb.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
module JOSE::JWA::X25519_CryptoRb

extend self

def curve25519(k, u)
k = coerce_scalar!(k)
u = coerce_montgomery_u!(u)
return k.diffie_hellman(u)
end

def x25519(sk, pk)
return curve25519(sk, pk).to_bytes()
end

def x25519_base(sk)
scalar = coerce_scalar!(sk)
return scalar.public_key.to_bytes()
end

def keypair(sk = nil)
sk ||= X25519::Scalar.generate()
scalar = coerce_scalar!(sk)
pk = sk_to_pk(scalar)
return pk, scalar.to_bytes()
end

def shared_secret(pk, sk)
return x25519(sk, pk)
end

def sk_to_pk(sk)
return x25519_base(sk)
end

def coerce_montgomery_u!(pk)
return pk if pk.is_a?(X25519::MontgomeryU)
pk = JOSE::JWA::X25519.coerce_coordinate_bytes!(u) if not pk.respond_to?(:bytesize)
return X25519::MontgomeryU.new(pk)
end

def coerce_scalar!(sk)
return sk if sk.is_a?(X25519::Scalar)
sk = JOSE::JWA::X25519.coerce_scalar_bytes!(sk) if not sk.respond_to?(:bytesize)
return X25519::Scalar.new(sk)
end

end
35 changes: 35 additions & 0 deletions test/jose/jwa/curve25519_cryptorb_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
require 'test_helper'

class JOSE::JWA::Curve25519_CryptoRbTest < Minitest::Test

EdDSA_SECRET = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0].pack('C*')
EdDSA_SK = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,59,106,39,188,206,182,164,45,98,163,168,208,42,111,13,115,101,50,21,119,29,226,67,166,58,192,72,161,139,89,218,41].pack('C*')
EdDSA_PK = [59,106,39,188,206,182,164,45,98,163,168,208,42,111,13,115,101,50,21,119,29,226,67,166,58,192,72,161,139,89,218,41].pack('C*')
EdDSA_M = [].pack('C*')
Ed25519_SIGNATURE = [143,137,91,60,175,226,201,80,96,57,208,226,166,99,130,86,128,4,103,79,232,210,55,120,80,146,228,13,106,175,72,62,79,198,1,104,112,95,49,241,1,89,97,56,206,33,170,53,124,13,50,160,100,244,35,220,62,228,170,58,191,83,248,3].pack('C*')
Ed25519ph_SIGNATURE = [156,203,202,248,117,129,126,7,75,58,114,37,34,97,88,28,107,134,137,45,226,153,205,227,223,176,47,120,25,58,104,151,0,98,205,14,60,170,233,142,178,63,207,181,133,191,210,47,240,169,14,31,162,243,195,196,214,28,175,237,234,99,76,4].pack('C*')
Curve25519_SECRET = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64].pack('C*')
Curve25519_PUBLIC = [47,229,125,163,71,205,98,67,21,40,218,172,95,187,41,7,48,255,246,132,175,196,207,194,237,144,153,95,88,203,59,116].pack('C*')
Curve25519_SHARED = [147,254,162,167,193,174,182,44,253,100,82,255,91,173,174,139,223,252,189,113,150,220,145,12,137,148,64,6,216,93,187,104].pack('C*')

def test_supported_methods
if JOSE::JWA::Curve25519_CryptoRb.__supported__?
[
[[EdDSA_PK, EdDSA_SK], :ed25519_keypair, EdDSA_SECRET],
[EdDSA_PK, :ed25519_secret_to_public, EdDSA_SK],
[Ed25519_SIGNATURE, :ed25519_sign, EdDSA_M, EdDSA_SK],
[true, :ed25519_verify, Ed25519_SIGNATURE, EdDSA_M, EdDSA_PK],
[[EdDSA_PK, EdDSA_SK], :ed25519ph_keypair, EdDSA_SECRET],
[EdDSA_PK, :ed25519ph_secret_to_public, EdDSA_SK],
[Ed25519ph_SIGNATURE, :ed25519ph_sign, EdDSA_M, EdDSA_SK],
[true, :ed25519ph_verify, Ed25519ph_SIGNATURE, EdDSA_M, EdDSA_PK],
[[Curve25519_PUBLIC, Curve25519_SECRET], :x25519_keypair, Curve25519_SECRET],
[Curve25519_PUBLIC, :x25519_secret_to_public, Curve25519_SECRET],
[Curve25519_SHARED, :x25519_shared_secret, Curve25519_PUBLIC, Curve25519_SECRET]
].each do |(expected, function, *args)|
assert_equal expected, JOSE::JWA::Curve25519_CryptoRb.send(function, *args)
end
end
end

end

0 comments on commit ff432bc

Please sign in to comment.