Class: HealthCards::Key

Inherits:
Object
  • Object
show all
Defined in:
lib/health_cards/key.rb

Overview

Methods to generate signing keys and jwk

Direct Known Subclasses

PrivateKey, PublicKey

Constant Summary collapse

BASE =
{ kty: 'EC', crv: 'P-256' }.freeze
DIGEST =
OpenSSL::Digest.new('SHA256')

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(ec_key) ⇒ Key

Returns a new instance of Key.



33
34
35
# File 'lib/health_cards/key.rb', line 33

def initialize(ec_key)
  @key = ec_key
end

Class Method Details

.enforce_valid_key_type!(obj, allow_nil: false) ⇒ Object

Checks if obj is the the correct key type or nil

Parameters:

  • obj

    Object that should be of same type as caller or nil

  • allow_nil (defaults to: false)

    Allow/Disallow key to be nil

Raises:



15
16
17
# File 'lib/health_cards/key.rb', line 15

def self.enforce_valid_key_type!(obj, allow_nil: false)
  raise InvalidKeyError.new(self, obj) unless obj.is_a?(self) || (allow_nil && obj.nil?)
end

.from_jwk(jwk_key) ⇒ HealthCards::Key

Create a key from a JWK

Parameters:

  • jwk_key (Hash)

    The JWK represented by a Hash

Returns:



23
24
25
26
27
28
29
30
31
# File 'lib/health_cards/key.rb', line 23

def self.from_jwk(jwk_key)
  jwk_key = jwk_key.transform_keys(&:to_sym)
  group = OpenSSL::PKey::EC::Group.new('prime256v1')
  key = OpenSSL::PKey::EC.new(group)
  key.private_key = OpenSSL::BN.new(Base64.urlsafe_decode64(jwk_key[:d]), 2) if jwk_key[:d]
  public_key_bn = ['04'].pack('H*') + Base64.urlsafe_decode64(jwk_key[:x]) + Base64.urlsafe_decode64(jwk_key[:y])
  key.public_key = OpenSSL::PKey::EC::Point.new(group, OpenSSL::BN.new(public_key_bn, 2))
  key.private_key? ? HealthCards::PrivateKey.new(key) : HealthCards::PublicKey.new(key)
end

Instance Method Details

#coordinatesObject



57
58
59
60
61
62
63
64
65
66
67
# File 'lib/health_cards/key.rb', line 57

def coordinates
  return @coordinates if @coordinates

  key_binary = @key.public_key.to_bn.to_s(2)
  coords = { x: key_binary[1, key_binary.length / 2],
             y: key_binary[key_binary.length / 2 + 1, key_binary.length] }
  coords[:d] = @key.private_key.to_s(2) if @key.private_key?
  @coordinates = coords.transform_values do |val|
    Base64.urlsafe_encode64(val, padding: false)
  end.merge(BASE)
end

#groupObject



37
38
39
# File 'lib/health_cards/key.rb', line 37

def group
  @key.group
end

#kidObject



49
50
51
# File 'lib/health_cards/key.rb', line 49

def kid
  Base64.urlsafe_encode64(DIGEST.digest(public_coordinates.to_json), padding: false)
end

#public_coordinatesObject



53
54
55
# File 'lib/health_cards/key.rb', line 53

def public_coordinates
  coordinates.slice(:crv, :kty, :x, :y)
end

#to_json(*_args) ⇒ Object



41
42
43
# File 'lib/health_cards/key.rb', line 41

def to_json(*_args)
  to_jwk.to_json
end

#to_jwkObject



45
46
47
# File 'lib/health_cards/key.rb', line 45

def to_jwk
  coordinates.merge(kid: kid, use: 'sig', alg: 'ES256')
end