Class: JSON::JWE

Inherits:
Object
  • Object
show all
Includes:
JOSE
Defined in:
lib/json/jwe.rb

Defined Under Namespace

Classes: DecryptionFailed, InvalidFormat, UnexpectedAlgorithm

Constant Summary collapse

NUM_OF_SEGMENTS =
5

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from JOSE

#secure_compare, #with_jwk_support

Constructor Details

#initialize(input = nil) ⇒ JWE

Returns a new instance of JWE.



25
26
27
# File 'lib/json/jwe.rb', line 25

def initialize(input = nil)
  self.plain_text = input.to_s
end

Instance Attribute Details

#auth_dataObject

Returns the value of attribute auth_data.



15
16
17
# File 'lib/json/jwe.rb', line 15

def auth_data
  @auth_data
end

#authentication_tag=(value) ⇒ Object

Sets the attribute authentication_tag

Parameters:

  • value

    the value to set the attribute authentication_tag to.



20
21
22
# File 'lib/json/jwe.rb', line 20

def authentication_tag=(value)
  @authentication_tag = value
end

#cipher_textObject

Returns the value of attribute cipher_text.



15
16
17
# File 'lib/json/jwe.rb', line 15

def cipher_text
  @cipher_text
end

#content_encryption_keyObject

Returns the value of attribute content_encryption_key.



15
16
17
# File 'lib/json/jwe.rb', line 15

def content_encryption_key
  @content_encryption_key
end

#encryption_keyObject

Returns the value of attribute encryption_key.



15
16
17
# File 'lib/json/jwe.rb', line 15

def encryption_key
  @encryption_key
end

#ivObject

Returns the value of attribute iv.



15
16
17
# File 'lib/json/jwe.rb', line 15

def iv
  @iv
end

#jwe_encrypted_key=(value) ⇒ Object

Sets the attribute jwe_encrypted_key

Parameters:

  • value

    the value to set the attribute jwe_encrypted_key to.



20
21
22
# File 'lib/json/jwe.rb', line 20

def jwe_encrypted_key=(value)
  @jwe_encrypted_key = value
end

#mac_keyObject

Returns the value of attribute mac_key.



15
16
17
# File 'lib/json/jwe.rb', line 15

def mac_key
  @mac_key
end

#plain_textObject

Returns the value of attribute plain_text.



15
16
17
# File 'lib/json/jwe.rb', line 15

def plain_text
  @plain_text
end

#private_key_or_secretObject

Returns the value of attribute private_key_or_secret.



15
16
17
# File 'lib/json/jwe.rb', line 15

def private_key_or_secret
  @private_key_or_secret
end

#public_key_or_secretObject

Returns the value of attribute public_key_or_secret.



15
16
17
# File 'lib/json/jwe.rb', line 15

def public_key_or_secret
  @public_key_or_secret
end

Class Method Details

.decode_compact_serialized(input, private_key_or_secret, algorithms = nil, encryption_methods = nil, _allow_blank_payload = false) ⇒ Object



262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
# File 'lib/json/jwe.rb', line 262

def decode_compact_serialized(input, private_key_or_secret, algorithms = nil, encryption_methods = nil, _allow_blank_payload = false)
  unless input.count('.') + 1 == NUM_OF_SEGMENTS
    raise InvalidFormat.new("Invalid JWE Format. JWE should include #{NUM_OF_SEGMENTS} segments.")
  end
  jwe = new
  _header_json_, jwe.jwe_encrypted_key, jwe.iv, jwe.cipher_text, jwe.authentication_tag = input.split('.', NUM_OF_SEGMENTS).collect do |segment|
    begin
      Base64.urlsafe_decode64 segment
    rescue ArgumentError
      raise DecryptionFailed
    end
  end
  jwe.auth_data = input.split('.').first
  jwe.header = JSON.parse(_header_json_).with_indifferent_access
  unless private_key_or_secret == :skip_decryption
    jwe.decrypt! private_key_or_secret, algorithms, encryption_methods
  end
  jwe
end

.decode_json_serialized(input, private_key_or_secret, algorithms = nil, encryption_methods = nil, _allow_blank_payload = false) ⇒ Object



282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
# File 'lib/json/jwe.rb', line 282

def decode_json_serialized(input, private_key_or_secret, algorithms = nil, encryption_methods = nil, _allow_blank_payload = false)
  input = input.with_indifferent_access
  jwe_encrypted_key = if input[:recipients].present?
    input[:recipients].first[:encrypted_key]
  else
    input[:encrypted_key]
  end
  compact_serialized = [
    input[:protected],
    jwe_encrypted_key,
    input[:iv],
    input[:ciphertext],
    input[:tag]
  ].join('.')
  decode_compact_serialized compact_serialized, private_key_or_secret, algorithms, encryption_methods
end

Instance Method Details

#as_json(options = {}) ⇒ Object



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/json/jwe.rb', line 84

def as_json(options = {})
  case options[:syntax]
  when :general
    {
      protected:  Base64.urlsafe_encode64(header.to_json, padding: false),
      recipients: [{
        encrypted_key: Base64.urlsafe_encode64(jwe_encrypted_key, padding: false)
      }],
      iv:         Base64.urlsafe_encode64(iv, padding: false),
      ciphertext: Base64.urlsafe_encode64(cipher_text, padding: false),
      tag:        Base64.urlsafe_encode64(authentication_tag, padding: false)
    }
  else
    {
      protected:     Base64.urlsafe_encode64(header.to_json, padding: false),
      encrypted_key: Base64.urlsafe_encode64(jwe_encrypted_key, padding: false),
      iv:            Base64.urlsafe_encode64(iv, padding: false),
      ciphertext:    Base64.urlsafe_encode64(cipher_text, padding: false),
      tag:           Base64.urlsafe_encode64(authentication_tag, padding: false)
    }
  end
end

#decrypt!(private_key_or_secret, algorithms = nil, encryption_methods = nil) ⇒ Object



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/json/jwe.rb', line 42

def decrypt!(private_key_or_secret, algorithms = nil, encryption_methods = nil)
  raise UnexpectedAlgorithm.new('Unexpected alg header') unless algorithms.blank? || Array(algorithms).include?(alg)
  raise UnexpectedAlgorithm.new('Unexpected enc header') unless encryption_methods.blank? || Array(encryption_methods).include?(enc)
  self.private_key_or_secret = with_jwk_support private_key_or_secret
  self.content_encryption_key = decrypt_content_encryption_key
  self.mac_key, self.encryption_key = derive_encryption_and_mac_keys

  verify_cbc_authentication_tag! if cbc?

  cipher.decrypt
  cipher.key = encryption_key
  cipher.iv = iv # NOTE: 'iv' has to be set after 'key' for GCM
  if gcm?
    # https://github.com/ruby/openssl/issues/63
    raise DecryptionFailed.new('Invalid authentication tag') if authentication_tag.length < 16
    cipher.auth_tag = authentication_tag
    cipher.auth_data = auth_data
  end

  begin
    self.plain_text = cipher.update(cipher_text) + cipher.final
  rescue OpenSSL::OpenSSLError
    # Ensure that the same error is raised for invalid PKCS7 padding
    # as for invalid signatures. This prevents padding-oracle attacks.
    raise DecryptionFailed
  end

  self
end

#encrypt!(public_key_or_secret) ⇒ Object



29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/json/jwe.rb', line 29

def encrypt!(public_key_or_secret)
  self.public_key_or_secret = with_jwk_support public_key_or_secret
  cipher.encrypt
  self.content_encryption_key = generate_content_encryption_key
  self.mac_key, self.encryption_key = derive_encryption_and_mac_keys
  cipher.key = encryption_key
  self.iv = cipher.random_iv # NOTE: 'iv' has to be set after 'key' for GCM
  self.auth_data = Base64.urlsafe_encode64 header.to_json, padding: false
  cipher.auth_data = auth_data if gcm?
  self.cipher_text = cipher.update(plain_text) + cipher.final
  self
end

#to_sObject



72
73
74
75
76
77
78
79
80
81
82
# File 'lib/json/jwe.rb', line 72

def to_s
  [
    header.to_json,
    jwe_encrypted_key,
    iv,
    cipher_text,
    authentication_tag
  ].collect do |segment|
    Base64.urlsafe_encode64 segment.to_s, padding: false
  end.join('.')
end