forked from jwt/ruby-jwe
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathjwe.rb
More file actions
67 lines (52 loc) · 2.79 KB
/
Copy pathjwe.rb
File metadata and controls
67 lines (52 loc) · 2.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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
require 'base64'
require 'json'
require 'openssl'
require 'securerandom'
require 'jwe/base64'
require 'jwe/serialization/compact'
require 'jwe/alg'
require 'jwe/enc'
require 'jwe/zip'
module JWE
class DecodeError < RuntimeError; end
class NotImplementedError < RuntimeError; end
class BadCEK < RuntimeError; end
class InvalidData < RuntimeError; end
VALID_ALG = ['RSA1_5', 'RSA-OAEP', 'RSA-OAEP-256', 'A128KW', 'A192KW', 'A256KW', 'dir', 'ECDH-ES', 'ECDH-ES+A128KW', 'ECDH-ES+A192KW', 'ECDH-ES+A256KW', 'A128GCMKW', 'A192GCMKW', 'A256GCMKW', 'PBES2-HS256+A128KW', 'PBES2-HS384+A192KW', 'PBES2-HS512+A256KW'].freeze
VALID_ENC = ['A128CBC-HS256', 'A192CBC-HS384', 'A256CBC-HS512', 'A128GCM', 'A192GCM', 'A256GCM'].freeze
VALID_ZIP = ['DEF'].freeze
def self.encrypt(payload, key, alg: 'RSA-OAEP', enc: 'A128GCM', zip: nil, headers: {})
raise ArgumentError.new("\"#{alg}\" is not a valid alg method") unless VALID_ALG.include?(alg)
raise ArgumentError.new("\"#{enc}\" is not a valid enc method") unless VALID_ENC.include?(enc)
raise ArgumentError.new("\"#{zip}\" is not a valid zip method") unless zip.nil? || zip == '' || VALID_ZIP.include?(zip)
raise ArgumentError.new('The key must not be nil or blank') if key.nil? || (key.is_a?(String) && key.strip == '')
header = { alg: alg, enc: enc }
header[:zip] = zip if zip && zip != ''
header.merge!(headers) if headers.is_a?(Hash)
cipher = Enc.for(enc).new
cipher.cek = key if alg == 'dir'
payload = Zip.for(zip).new.compress(payload) if zip && zip != ''
ciphertext = cipher.encrypt(payload, Base64.jwe_encode(header.to_json))
encrypted_cek = Alg.for(alg).new(key).encrypt(cipher.cek)
Serialization::Compact.encode(header.to_json, encrypted_cek, cipher.iv, ciphertext, cipher.tag)
end
def self.decrypt(payload, key, &keyfinder)
header, enc_key, iv, ciphertext, tag = Serialization::Compact.decode(payload)
header = JSON.parse(header)
base64header = payload.split('.').first
raise ArgumentError.new("\"#{header['alg']}\" is not a valid alg method") unless VALID_ALG.include?(header['alg'])
raise ArgumentError.new("\"#{header['enc']}\" is not a valid enc method") unless VALID_ENC.include?(header['enc'])
raise ArgumentError.new("\"#{header['zip']}\" is not a valid zip method") unless header['zip'].nil? || VALID_ZIP.include?(header['zip'])
key = yield(header) if keyfinder
raise ArgumentError.new('The key must not be nil or blank') if key.nil? || (key.is_a?(String) && key.strip == '')
cek = Alg.for(header['alg']).new(key).decrypt(enc_key)
cipher = Enc.for(header['enc']).new(cek, iv)
cipher.tag = tag
plaintext = cipher.decrypt(ciphertext, base64header)
if header['zip']
Zip.for(header['zip']).new.decompress(plaintext)
else
plaintext
end
end
end