Class: SmartHealthCardsTestKit::Utils::Key

Inherits:
Object
  • Object
show all
Defined in:
lib/smart_health_cards_test_kit/utils/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.



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

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:

  • (InvalidKeyError)


16
17
18
# File 'lib/smart_health_cards_test_kit/utils/key.rb', line 16

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) ⇒ SmartHealthCardsTestKit::Utils::Key

Create a key from a JWK

Parameters:

  • jwk_key (Hash) —

    The JWK represented by a Hash

Returns:



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/smart_health_cards_test_kit/utils/key.rb', line 24

def self.from_jwk(jwk_key)
  jwk_key = jwk_key.transform_keys(&:to_sym)
  curvename = 'prime256v1'
  group = OpenSSL::PKey::EC::Group.new(curvename)
  public_key_bn = ['04'].pack('H*') + Base64.urlsafe_decode64(jwk_key[:x]) + Base64.urlsafe_decode64(jwk_key[:y])
  point = OpenSSL::PKey::EC::Point.new(group, OpenSSL::BN.new(public_key_bn, 2))

  asn1 = OpenSSL::ASN1::Sequence([
    OpenSSL::ASN1::Sequence([
      OpenSSL::ASN1::ObjectId("id-ecPublicKey"),
      OpenSSL::ASN1::ObjectId(curvename)
    ]),
    OpenSSL::ASN1::BitString(point.to_octet_string(:uncompressed))
  ])

  key = OpenSSL::PKey::EC.new(asn1.to_der)
  key.private_key = OpenSSL::BN.new(Base64.urlsafe_decode64(jwk_key[:d]), 2) if jwk_key[:d]

  key.private_key? ? SmartHealthCardsTestKit::Utils::PrivateKey.new(key) : SmartHealthCardsTestKit::Utils::PublicKey.new(key)
end

Instance Method Details

#coordinates ⇒ Object



69
70
71
72
73
74
75
76
77
78
79
# File 'lib/smart_health_cards_test_kit/utils/key.rb', line 69

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

#group ⇒ Object



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

def group
  @key.group
end

#kid ⇒ Object



61
62
63
# File 'lib/smart_health_cards_test_kit/utils/key.rb', line 61

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

#public_coordinates ⇒ Object



65
66
67
# File 'lib/smart_health_cards_test_kit/utils/key.rb', line 65

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

#to_json(*_args) ⇒ Object



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

def to_json(*_args)
  to_jwk.to_json
end

#to_jwk ⇒ Object



57
58
59
# File 'lib/smart_health_cards_test_kit/utils/key.rb', line 57

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