diff options
author | elijah <elijah@riseup.net> | 2016-10-05 14:35:56 -0700 |
---|---|---|
committer | elijah <elijah@riseup.net> | 2016-10-05 14:35:56 -0700 |
commit | 7abfbd6abae14fa6a72350f7b75268ff561354ee (patch) | |
tree | af5c969c905a8d2a95f2b2aa7c4dd6f4b8763126 /vendor/acme-client/lib/acme/client/crypto.rb | |
parent | cc57bc6c0ff99d88f3bfeff1b04297e9b91e6988 (diff) | |
parent | f95e08ef7d8defbde4a19e138b1ac4ebc9677669 (diff) |
Merge branch 'develop'
# Conflicts:
# lib/leap_cli/version.rb
Diffstat (limited to 'vendor/acme-client/lib/acme/client/crypto.rb')
-rw-r--r-- | vendor/acme-client/lib/acme/client/crypto.rb | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/vendor/acme-client/lib/acme/client/crypto.rb b/vendor/acme-client/lib/acme/client/crypto.rb new file mode 100644 index 0000000..dfa5cdc --- /dev/null +++ b/vendor/acme-client/lib/acme/client/crypto.rb @@ -0,0 +1,98 @@ +class Acme::Client::Crypto + attr_reader :private_key + + def initialize(private_key) + @private_key = private_key + end + + def generate_signed_jws(header:, payload:) + header = { typ: 'JWT', alg: jws_alg, jwk: jwk }.merge(header) + + encoded_header = urlsafe_base64(header.to_json) + encoded_payload = urlsafe_base64(payload.to_json) + signature_data = "#{encoded_header}.#{encoded_payload}" + + signature = private_key.sign digest, signature_data + encoded_signature = urlsafe_base64(signature) + + { + protected: encoded_header, + payload: encoded_payload, + signature: encoded_signature + }.to_json + end + + def thumbprint + urlsafe_base64 digest.digest(jwk.to_json) + end + + def digest + OpenSSL::Digest::SHA256.new + end + + def urlsafe_base64(data) + Base64.urlsafe_encode64(data).sub(/[\s=]*\z/, '') + end + + private + + def jws_alg + { 'RSA' => 'RS256', 'EC' => 'ES256' }.fetch(jwk[:kty]) + end + + def jwk + @jwk ||= case private_key + when OpenSSL::PKey::RSA + rsa_jwk + when OpenSSL::PKey::EC + ec_jwk + else + raise ArgumentError, "Can't handle #{private_key} as private key, only OpenSSL::PKey::RSA and OpenSSL::PKey::EC" + end + end + + def rsa_jwk + { + e: urlsafe_base64(public_key.e.to_s(2)), + kty: 'RSA', + n: urlsafe_base64(public_key.n.to_s(2)) + } + end + + def ec_jwk + { + crv: curve_name, + kty: 'EC', + x: urlsafe_base64(coordinates[:x].to_s(2)), + y: urlsafe_base64(coordinates[:y].to_s(2)) + } + end + + def curve_name + { + 'prime256v1' => 'P-256', + 'secp384r1' => 'P-384', + 'secp521r1' => 'P-521' + }.fetch(private_key.group.curve_name) { raise ArgumentError, 'Unknown EC curve' } + end + + # rubocop:disable Metrics/AbcSize + def coordinates + @coordinates ||= begin + hex = public_key.to_bn.to_s(16) + data_len = hex.length - 2 + hex_x = hex[2, data_len / 2] + hex_y = hex[2 + data_len / 2, data_len / 2] + + { + x: OpenSSL::BN.new([hex_x].pack('H*'), 2), + y: OpenSSL::BN.new([hex_y].pack('H*'), 2) + } + end + end + # rubocop:enable Metrics/AbcSize + + def public_key + @public_key ||= private_key.public_key + end +end |