jwe 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.codeclimate.yml +13 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.rubocop.yml +3 -0
- data/.travis.yml +8 -0
- data/Gemfile +4 -0
- data/LICENSE.md +24 -0
- data/Rakefile +7 -0
- data/jwe.gemspec +21 -0
- data/lib/jwe.rb +64 -0
- data/lib/jwe/alg.rb +16 -0
- data/lib/jwe/alg/dir.rb +19 -0
- data/lib/jwe/alg/rsa15.rb +19 -0
- data/lib/jwe/alg/rsa_oaep.rb +19 -0
- data/lib/jwe/base64.rb +16 -0
- data/lib/jwe/enc.rb +19 -0
- data/lib/jwe/enc/a128cbc_hs256.rb +21 -0
- data/lib/jwe/enc/a128gcm.rb +17 -0
- data/lib/jwe/enc/a192cbc_hs384.rb +21 -0
- data/lib/jwe/enc/a192gcm.rb +17 -0
- data/lib/jwe/enc/a256cbc_hs512.rb +21 -0
- data/lib/jwe/enc/a256gcm.rb +17 -0
- data/lib/jwe/enc/aes_cbc_hs.rb +89 -0
- data/lib/jwe/enc/aes_gcm.rb +73 -0
- data/lib/jwe/serialization/compact.rb +18 -0
- data/lib/jwe/version.rb +4 -0
- data/lib/jwe/zip.rb +14 -0
- data/lib/jwe/zip/def.rb +15 -0
- data/spec/jwe/alg_spec.rb +66 -0
- data/spec/jwe/base64_spec.rb +27 -0
- data/spec/jwe/enc_spec.rb +211 -0
- data/spec/jwe/serialization_spec.rb +18 -0
- data/spec/jwe/zip_spec.rb +21 -0
- data/spec/jwe_spec.rb +60 -0
- data/spec/keys/rsa.pem +27 -0
- data/spec/spec_helper.rb +9 -0
- metadata +121 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: cb166f2f458cf97d14cdee1860f05aeaa53c5cfd
|
4
|
+
data.tar.gz: bd0dbea752a5d38424cd4b45b1f03e2fe85ae550
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 775afd82c19c9501a6534c313cdd50bfda73397b8720d79d924efc3632d86de893b344d37e383d0755f5f5499f33166ae074f41803cc11008e924b8d592708df
|
7
|
+
data.tar.gz: ffb94097e58257985a0d6334628a2bfa42217e36020a060ea621c49313364067505241d2b163f76027311c5f4071152b11696d5a095de8a083e90cdcbbb14184
|
data/.codeclimate.yml
ADDED
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.md
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
=====================
|
3
|
+
|
4
|
+
* Copyright © 2016 Francesco Boffa
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
7
|
+
a copy of this software and associated documentation files (the
|
8
|
+
"Software"), to deal in the Software without restriction, including
|
9
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
10
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
11
|
+
permit persons to whom the Software is furnished to do so, subject to
|
12
|
+
the following conditions:
|
13
|
+
|
14
|
+
The above copyright notice and this permission notice shall be
|
15
|
+
included in all copies or substantial portions of the Software.
|
16
|
+
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
18
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
19
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
20
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
21
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
22
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
23
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
24
|
+
|
data/Rakefile
ADDED
data/jwe.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
lib = File.expand_path('../lib/', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require 'jwe/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'jwe'
|
7
|
+
s.version = JWE::VERSION
|
8
|
+
s.summary = 'JSON Web Encryption implementation in Ruby'
|
9
|
+
s.description = 'A Ruby implementation of the RFC 7516 JSON Web Encryption (JWE) standard'
|
10
|
+
s.authors = [ 'Francesco Boffa' ]
|
11
|
+
s.email = 'fra.boffa@gmail.com'
|
12
|
+
s.homepage = 'http://github.com/aomega08/jwe'
|
13
|
+
s.license = 'MIT'
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.require_paths = %w(lib)
|
17
|
+
|
18
|
+
s.add_development_dependency 'rspec'
|
19
|
+
s.add_development_dependency 'rake'
|
20
|
+
s.add_development_dependency 'codeclimate-test-reporter'
|
21
|
+
end
|
data/lib/jwe.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'json'
|
3
|
+
require 'openssl'
|
4
|
+
require 'securerandom'
|
5
|
+
|
6
|
+
require 'jwe/base64'
|
7
|
+
require 'jwe/serialization/compact'
|
8
|
+
require 'jwe/alg'
|
9
|
+
require 'jwe/enc'
|
10
|
+
require 'jwe/zip'
|
11
|
+
|
12
|
+
module JWE
|
13
|
+
class DecodeError < Exception; end
|
14
|
+
class NotImplementedError < Exception; end
|
15
|
+
class BadCEK < Exception; end
|
16
|
+
class InvalidData < Exception; end
|
17
|
+
|
18
|
+
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' ]
|
19
|
+
VALID_ENC = [ 'A128CBC-HS256', 'A192CBC-HS384', 'A256CBC-HS512', 'A128GCM', 'A192GCM', 'A256GCM' ]
|
20
|
+
VALID_ZIP = [ 'DEF' ]
|
21
|
+
|
22
|
+
def self.encrypt(payload, key, alg: 'RSA-OAEP', enc: 'A128GCM', zip: nil)
|
23
|
+
raise ArgumentError.new("\"#{alg}\" is not a valid alg method") unless VALID_ALG.include?(alg)
|
24
|
+
raise ArgumentError.new("\"#{enc}\" is not a valid enc method") unless VALID_ENC.include?(enc)
|
25
|
+
raise ArgumentError.new("\"#{zip}\" is not a valid zip method") unless zip.nil? || zip == '' || VALID_ZIP.include?(zip)
|
26
|
+
|
27
|
+
header = { alg: alg, enc: enc }
|
28
|
+
header[:zip] = zip if zip and zip != ''
|
29
|
+
|
30
|
+
cipher = Enc.for(enc).new
|
31
|
+
cipher.cek = key if alg == 'dir'
|
32
|
+
|
33
|
+
if zip and zip != ''
|
34
|
+
payload = Zip.for(zip).new.compress(payload)
|
35
|
+
end
|
36
|
+
|
37
|
+
ciphertext = cipher.encrypt(payload, Base64::jwe_encode(header.to_json))
|
38
|
+
encrypted_cek = Alg.for(alg).new(key).encrypt(cipher.cek)
|
39
|
+
|
40
|
+
Serialization::Compact.encode(header.to_json, encrypted_cek, cipher.iv, ciphertext, cipher.tag)
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.decrypt(payload, key)
|
44
|
+
header, enc_key, iv, ciphertext, tag = Serialization::Compact.decode(payload)
|
45
|
+
header = JSON.parse(header)
|
46
|
+
base64header = payload.split('.').first
|
47
|
+
|
48
|
+
raise ArgumentError.new("\"#{header['alg']}\" is not a valid alg method") unless VALID_ALG.include?(header['alg'])
|
49
|
+
raise ArgumentError.new("\"#{header['enc']}\" is not a valid enc method") unless VALID_ENC.include?(header['enc'])
|
50
|
+
raise ArgumentError.new("\"#{header['zip']}\" is not a valid zip method") unless header['zip'].nil? || VALID_ZIP.include?(header['zip'])
|
51
|
+
|
52
|
+
cek = Alg.for(header['alg']).new(key).decrypt(enc_key)
|
53
|
+
cipher = Enc.for(header['enc']).new(cek, iv)
|
54
|
+
cipher.tag = tag
|
55
|
+
|
56
|
+
plaintext = cipher.decrypt(ciphertext, base64header)
|
57
|
+
|
58
|
+
if header['zip']
|
59
|
+
Zip.for(header['zip']).new.decompress(plaintext)
|
60
|
+
else
|
61
|
+
plaintext
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/jwe/alg.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'jwe/alg/dir'
|
2
|
+
require 'jwe/alg/rsa_oaep'
|
3
|
+
require 'jwe/alg/rsa15'
|
4
|
+
|
5
|
+
module JWE
|
6
|
+
module Alg
|
7
|
+
def self.for(alg)
|
8
|
+
klass = alg.gsub(/[-\+]/, '_').downcase.sub(/^[a-z\d]*/) { $&.capitalize }
|
9
|
+
klass.gsub!(/_([a-z\d]*)/i) { $1.capitalize }
|
10
|
+
const_get(klass)
|
11
|
+
|
12
|
+
rescue NameError
|
13
|
+
raise NotImplementedError.new("Unsupported alg type: #{alg}")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/jwe/alg/dir.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
module JWE
|
2
|
+
module Alg
|
3
|
+
class Rsa15
|
4
|
+
attr_accessor :key
|
5
|
+
|
6
|
+
def initialize(key)
|
7
|
+
self.key = key
|
8
|
+
end
|
9
|
+
|
10
|
+
def encrypt(cek)
|
11
|
+
key.public_encrypt(cek)
|
12
|
+
end
|
13
|
+
|
14
|
+
def decrypt(encrypted_cek)
|
15
|
+
key.private_decrypt(encrypted_cek)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module JWE
|
2
|
+
module Alg
|
3
|
+
class RsaOaep
|
4
|
+
attr_accessor :key
|
5
|
+
|
6
|
+
def initialize(key)
|
7
|
+
self.key = key
|
8
|
+
end
|
9
|
+
|
10
|
+
def encrypt(cek)
|
11
|
+
key.public_encrypt(cek, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
|
12
|
+
end
|
13
|
+
|
14
|
+
def decrypt(encrypted_cek)
|
15
|
+
key.private_decrypt(encrypted_cek, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/jwe/base64.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
module JWE
|
2
|
+
module Base64
|
3
|
+
def self.jwe_encode(payload)
|
4
|
+
::Base64.urlsafe_encode64(payload).gsub('=', '')
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.jwe_decode(payload)
|
8
|
+
padlen = 4 - (payload.length % 4)
|
9
|
+
if padlen < 4
|
10
|
+
pad = "=" * padlen
|
11
|
+
payload += pad
|
12
|
+
end
|
13
|
+
::Base64.urlsafe_decode64(payload)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/jwe/enc.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'jwe/enc/a128cbc_hs256'
|
2
|
+
require 'jwe/enc/a192cbc_hs384'
|
3
|
+
require 'jwe/enc/a256cbc_hs512'
|
4
|
+
require 'jwe/enc/a128gcm'
|
5
|
+
require 'jwe/enc/a192gcm'
|
6
|
+
require 'jwe/enc/a256gcm'
|
7
|
+
|
8
|
+
module JWE
|
9
|
+
module Enc
|
10
|
+
def self.for(enc)
|
11
|
+
klass = enc.gsub(/[-\+]/, '_').downcase.sub(/^[a-z\d]*/) { $&.capitalize }
|
12
|
+
klass.gsub!(/_([a-z\d]*)/i) { $1.capitalize }
|
13
|
+
const_get(klass)
|
14
|
+
|
15
|
+
rescue NameError
|
16
|
+
raise NotImplementedError.new("Unsupported enc type: #{enc}")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module JWE
|
2
|
+
module Enc
|
3
|
+
module AesCbcHs
|
4
|
+
attr_accessor :cek
|
5
|
+
attr_accessor :iv
|
6
|
+
attr_accessor :tag
|
7
|
+
|
8
|
+
def initialize(cek = nil, iv = nil)
|
9
|
+
self.iv = iv
|
10
|
+
self.cek = cek
|
11
|
+
end
|
12
|
+
|
13
|
+
def encrypt(cleartext, authenticated_data)
|
14
|
+
raise JWE::BadCEK.new("The supplied key is invalid. Required length: #{key_length}") if cek.length != key_length
|
15
|
+
|
16
|
+
cipher.encrypt
|
17
|
+
cipher.key = enc_key
|
18
|
+
cipher.iv = iv
|
19
|
+
|
20
|
+
ciphertext = cipher.update(cleartext) + cipher.final
|
21
|
+
length = [ciphertext.length * 8].pack('Q>') # 64bit big endian
|
22
|
+
|
23
|
+
to_sign = authenticated_data + iv + ciphertext + length
|
24
|
+
signature = OpenSSL::HMAC.digest(OpenSSL::Digest.new(hash_name), mac_key, to_sign)
|
25
|
+
self.tag = signature[0 ... mac_key.length]
|
26
|
+
|
27
|
+
ciphertext
|
28
|
+
end
|
29
|
+
|
30
|
+
def decrypt(ciphertext, authenticated_data)
|
31
|
+
raise JWE::BadCEK.new("The supplied key is invalid. Required length: #{key_length}") if cek.length != key_length
|
32
|
+
|
33
|
+
length = [ciphertext.length * 8].pack('Q>') # 64bit big endian
|
34
|
+
to_sign = authenticated_data + iv + ciphertext + length
|
35
|
+
signature = OpenSSL::HMAC.digest(OpenSSL::Digest.new(hash_name), mac_key, to_sign)
|
36
|
+
if signature[0 ... mac_key.length] != tag
|
37
|
+
raise JWE::InvalidData.new("Authentication tag verification failed")
|
38
|
+
end
|
39
|
+
|
40
|
+
cipher.decrypt
|
41
|
+
cipher.key = enc_key
|
42
|
+
cipher.iv = iv
|
43
|
+
|
44
|
+
cipher.update(ciphertext) + cipher.final
|
45
|
+
rescue OpenSSL::Cipher::CipherError
|
46
|
+
raise JWE::InvalidData.new("Invalid ciphertext or authentication tag")
|
47
|
+
end
|
48
|
+
|
49
|
+
def iv
|
50
|
+
@iv ||= SecureRandom.random_bytes(16)
|
51
|
+
end
|
52
|
+
|
53
|
+
def cek
|
54
|
+
@cek ||= SecureRandom.random_bytes(key_length)
|
55
|
+
end
|
56
|
+
|
57
|
+
def mac_key
|
58
|
+
cek[0 ... key_length / 2]
|
59
|
+
end
|
60
|
+
|
61
|
+
def enc_key
|
62
|
+
cek[key_length / 2 .. -1 ]
|
63
|
+
end
|
64
|
+
|
65
|
+
def cipher
|
66
|
+
@cipher ||= OpenSSL::Cipher.new(cipher_name)
|
67
|
+
rescue RuntimeError
|
68
|
+
raise JWE::NotImplementedError.new("The version of OpenSSL linked to your Ruby does not support the cipher #{cipher_name}.")
|
69
|
+
end
|
70
|
+
|
71
|
+
def tag
|
72
|
+
@tag || ""
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.included(base)
|
76
|
+
base.extend(ClassMethods)
|
77
|
+
end
|
78
|
+
|
79
|
+
module ClassMethods
|
80
|
+
def available?
|
81
|
+
new.cipher
|
82
|
+
true
|
83
|
+
rescue JWE::NotImplementedError
|
84
|
+
false
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module JWE
|
2
|
+
module Enc
|
3
|
+
module AesGcm
|
4
|
+
attr_accessor :cek
|
5
|
+
attr_accessor :iv
|
6
|
+
attr_accessor :tag
|
7
|
+
|
8
|
+
def initialize(cek = nil, iv = nil)
|
9
|
+
self.iv = iv
|
10
|
+
self.cek = cek
|
11
|
+
end
|
12
|
+
|
13
|
+
def encrypt(cleartext, authenticated_data)
|
14
|
+
raise JWE::BadCEK.new("The supplied key is too short. Required length: #{key_length}") if cek.length < key_length
|
15
|
+
|
16
|
+
cipher.encrypt
|
17
|
+
cipher.key = cek
|
18
|
+
cipher.iv = iv
|
19
|
+
cipher.auth_data = authenticated_data
|
20
|
+
|
21
|
+
ciphertext = cipher.update(cleartext) + cipher.final
|
22
|
+
self.tag = cipher.auth_tag
|
23
|
+
|
24
|
+
ciphertext
|
25
|
+
end
|
26
|
+
|
27
|
+
def decrypt(ciphertext, authenticated_data)
|
28
|
+
raise JWE::BadCEK.new("The supplied key is too short. Required length: #{key_length}") if cek.length < key_length
|
29
|
+
|
30
|
+
cipher.decrypt
|
31
|
+
cipher.key = cek
|
32
|
+
cipher.iv = iv
|
33
|
+
cipher.auth_tag = tag
|
34
|
+
cipher.auth_data = authenticated_data
|
35
|
+
|
36
|
+
cipher.update(ciphertext) + cipher.final
|
37
|
+
rescue OpenSSL::Cipher::CipherError
|
38
|
+
raise JWE::InvalidData.new("Invalid ciphertext or authentication tag")
|
39
|
+
end
|
40
|
+
|
41
|
+
def iv
|
42
|
+
@iv ||= SecureRandom.random_bytes(12)
|
43
|
+
end
|
44
|
+
|
45
|
+
def cek
|
46
|
+
@cek ||= SecureRandom.random_bytes(key_length)
|
47
|
+
end
|
48
|
+
|
49
|
+
def cipher
|
50
|
+
@cipher ||= OpenSSL::Cipher.new(cipher_name)
|
51
|
+
rescue RuntimeError
|
52
|
+
raise JWE::NotImplementedError.new("The version of OpenSSL linked to your Ruby does not support the cipher #{cipher_name}.")
|
53
|
+
end
|
54
|
+
|
55
|
+
def tag
|
56
|
+
@tag || ""
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.included(base)
|
60
|
+
base.extend(ClassMethods)
|
61
|
+
end
|
62
|
+
|
63
|
+
module ClassMethods
|
64
|
+
def available?
|
65
|
+
new.cipher
|
66
|
+
true
|
67
|
+
rescue JWE::NotImplementedError
|
68
|
+
false
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module JWE
|
2
|
+
module Serialization
|
3
|
+
class Compact
|
4
|
+
def self.encode(header, encrypted_cek, iv, ciphertext, tag)
|
5
|
+
[ header, encrypted_cek, iv, ciphertext, tag ].map { |piece| JWE::Base64::jwe_encode(piece) }.join '.'
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.decode(payload)
|
9
|
+
parts = payload.split('.')
|
10
|
+
raise JWE::DecodeError.new('Not enaugh or too many segments') unless parts.length == 5
|
11
|
+
|
12
|
+
parts.map do |part|
|
13
|
+
JWE::Base64.jwe_decode(part)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/jwe/version.rb
ADDED
data/lib/jwe/zip.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'jwe/zip/def'
|
2
|
+
|
3
|
+
module JWE
|
4
|
+
module Zip
|
5
|
+
def self.for(zip)
|
6
|
+
klass = zip.gsub(/[-\+]/, '_').downcase.sub(/^[a-z\d]*/) { $&.capitalize }
|
7
|
+
klass.gsub!(/_([a-z\d]*)/i) { $1.capitalize }
|
8
|
+
const_get(klass)
|
9
|
+
|
10
|
+
rescue NameError
|
11
|
+
raise NotImplementedError.new("Unsupported zip type: #{zip}")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/jwe/zip/def.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'jwe/alg/dir'
|
2
|
+
require 'jwe/alg/rsa_oaep'
|
3
|
+
require 'jwe/alg/rsa15'
|
4
|
+
require 'openssl'
|
5
|
+
|
6
|
+
describe JWE::Alg do
|
7
|
+
describe '.for' do
|
8
|
+
it 'returns a class for the specified alg' do
|
9
|
+
expect(JWE::Alg.for('RSA-OAEP')).to eq JWE::Alg::RsaOaep
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'raises an error for a not-implemented alg' do
|
13
|
+
expect { JWE::Alg.for('ERSA-4096-MAGIC') }.to raise_error(JWE::NotImplementedError)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe JWE::Alg::Dir do
|
19
|
+
# The direct encryption method does not Encrypt the CEK.
|
20
|
+
# When building the final JWE object, the "Encrypted CEK" part is left blank
|
21
|
+
|
22
|
+
describe '#encrypt' do
|
23
|
+
it 'returns an empty string' do
|
24
|
+
expect(JWE::Alg::Dir.new('whatever').encrypt('any')).to eq ''
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#decrypt' do
|
29
|
+
it 'returns the original key' do
|
30
|
+
expect(JWE::Alg::Dir.new('whatever').decrypt('any')).to eq 'whatever'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
key_path = File.dirname(__FILE__) + '/../keys/rsa.pem'
|
36
|
+
key = OpenSSL::PKey::RSA.new File.read(key_path)
|
37
|
+
|
38
|
+
describe JWE::Alg::RsaOaep do
|
39
|
+
let(:alg) { JWE::Alg::RsaOaep.new(key) }
|
40
|
+
|
41
|
+
describe '#encrypt' do
|
42
|
+
it 'returns an encrypted string' do
|
43
|
+
expect(alg.encrypt('random key')).to_not eq 'random key'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'decrypts the encrypted key to the original key' do
|
48
|
+
ciphertext = alg.encrypt('random key')
|
49
|
+
expect(alg.decrypt(ciphertext)).to eq 'random key'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe JWE::Alg::Rsa15 do
|
54
|
+
let(:alg) { JWE::Alg::Rsa15.new(key) }
|
55
|
+
|
56
|
+
describe '#encrypt' do
|
57
|
+
it 'returns an encrypted string' do
|
58
|
+
expect(alg.encrypt('random key')).to_not eq 'random key'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'decrypts the encrypted key to the original key' do
|
63
|
+
ciphertext = alg.encrypt('random key')
|
64
|
+
expect(alg.decrypt(ciphertext)).to eq 'random key'
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'jwe/base64'
|
2
|
+
|
3
|
+
module JWE
|
4
|
+
describe Base64 do
|
5
|
+
describe '.jwe_encode' do
|
6
|
+
it 'encodes the payload using the urlsafe encoding' do
|
7
|
+
# "me?" encodes to "bWU/" in standard encoding
|
8
|
+
expect(Base64.jwe_encode("me?")).to_not include '/'
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'strips the standard padding' do
|
12
|
+
expect(Base64.jwe_encode("a")).to_not end_with '='
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '.jwe_decode' do
|
17
|
+
it 'decodes the payload using the urlsafe encoding' do
|
18
|
+
# "me?" encodes to "bWU/" in standard encoding
|
19
|
+
expect(Base64.jwe_decode("bWU_")).to eq "me?"
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'fixes the padding' do
|
23
|
+
expect(Base64.jwe_decode("YQ")).to eq "a"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,211 @@
|
|
1
|
+
require 'jwe/enc/a128cbc_hs256'
|
2
|
+
require 'jwe/enc/a192cbc_hs384'
|
3
|
+
require 'jwe/enc/a256cbc_hs512'
|
4
|
+
require 'jwe/enc/a128gcm'
|
5
|
+
require 'jwe/enc/a192gcm'
|
6
|
+
require 'jwe/enc/a256gcm'
|
7
|
+
|
8
|
+
describe JWE::Enc do
|
9
|
+
describe '.for' do
|
10
|
+
it 'returns a class for the specified enc' do
|
11
|
+
expect(JWE::Enc.for('A128GCM')).to eq JWE::Enc::A128gcm
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'raises an error for a not-implemented enc' do
|
15
|
+
expect { JWE::Enc.for('ERSA-4096-MAGIC') }.to raise_error(JWE::NotImplementedError)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
gcm = [
|
21
|
+
{
|
22
|
+
class: JWE::Enc::A128gcm,
|
23
|
+
keylen: 16,
|
24
|
+
helloworld: "\"\xC6\xE4h\x8AI\x83\x90v\xAF\xE2\x11".force_encoding('BINARY'),
|
25
|
+
tag: "\x85|\xF7\xE1\x94\tVG\x84\xE1\xA8\x81\a\xF4\xC60".force_encoding('BINARY'),
|
26
|
+
ivlen: 12,
|
27
|
+
iv: "\x0" * 12
|
28
|
+
},
|
29
|
+
{
|
30
|
+
class: JWE::Enc::A192gcm,
|
31
|
+
keylen: 24,
|
32
|
+
helloworld: "\x9F\xA4\xEC\xCCa\x86\tRO\xD7\xE3\x8D".force_encoding('BINARY'),
|
33
|
+
tag: "\xF6\xC0\xB8\x91A\xB1\xF0}\xD4u\xD0_\xCD\xA7\x17'".force_encoding('BINARY'),
|
34
|
+
ivlen: 12,
|
35
|
+
iv: "\x0" * 12
|
36
|
+
},
|
37
|
+
{
|
38
|
+
class: JWE::Enc::A256gcm,
|
39
|
+
keylen: 32,
|
40
|
+
helloworld: "\xFDq\xDC\xDD\x87\x9DK\x97\x03G\x99\f".force_encoding('BINARY'),
|
41
|
+
tag: "\xC6\xF1\r\xDD\x14\x7Fqf,6\x0EK\x7F\x9D\x1D\t".force_encoding('BINARY'),
|
42
|
+
ivlen: 12,
|
43
|
+
iv: "\x0" * 12
|
44
|
+
},
|
45
|
+
{
|
46
|
+
class: JWE::Enc::A128cbcHs256,
|
47
|
+
keylen: 32,
|
48
|
+
helloworld: "\a\x02F\xA4m%\xDFH\xB4\xA4.\xBF:\xBF$\xE2".force_encoding('BINARY'),
|
49
|
+
tag: "\xD2\xC2\xA5M\xF1e\x00\xDB}\xDB\x15\x9F\xFF\x8A\x7F\x94".force_encoding('BINARY'),
|
50
|
+
ivlen: 16,
|
51
|
+
iv: "\x0" * 16
|
52
|
+
},
|
53
|
+
{
|
54
|
+
class: JWE::Enc::A192cbcHs384,
|
55
|
+
keylen: 48,
|
56
|
+
helloworld: "p\xFES\xF0\xB4\xCC]8\x1D\xDE\x8Dt\xE7tMh".force_encoding('BINARY'),
|
57
|
+
tag: "\xEA\xF4\xD73M\xC6\x1D\x13\x0E\x9E\xAE%L\xD3\x04#\x80:\xA8}\xD7\x16E{".force_encoding('BINARY'),
|
58
|
+
ivlen: 16,
|
59
|
+
iv: "\x0" * 16
|
60
|
+
},
|
61
|
+
{
|
62
|
+
class: JWE::Enc::A256cbcHs512,
|
63
|
+
keylen: 64,
|
64
|
+
helloworld: "c\xFD\\\xB9Z\xB6\xE3\xB7\xEE\xA1\xD8\xDF\xB5\xB2\xF8\xEB".force_encoding('BINARY'),
|
65
|
+
tag: "\xD2W\xCAE\xBC\xE9\xC5\xCF\xD5\xE0\x88@j\xE4\xA1-\x16\xDA\x8F5(\x1D\x0E\x15.\xDC\x11\x12\x00`\xCER".force_encoding('BINARY'),
|
66
|
+
ivlen: 16,
|
67
|
+
iv: "\x0" * 16
|
68
|
+
}
|
69
|
+
]
|
70
|
+
|
71
|
+
gcm.each do |group|
|
72
|
+
describe group[:class] do
|
73
|
+
let(:klass) { group[:class] }
|
74
|
+
let(:key) { 'a' * group[:keylen] }
|
75
|
+
let(:plaintext) { 'hello world!' }
|
76
|
+
|
77
|
+
describe '#encrypt' do
|
78
|
+
context 'when an invalid key is used' do
|
79
|
+
it 'raises an error' do
|
80
|
+
enc = klass.new('small')
|
81
|
+
expect { enc.encrypt('plain', 'auth') }.to raise_error(JWE::BadCEK)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context 'with a valid key' do
|
86
|
+
it 'returns the encrypted payload' do
|
87
|
+
enc = klass.new(key, group[:iv])
|
88
|
+
expect(enc.encrypt(plaintext, '').force_encoding('BINARY')).to eq group[:helloworld]
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'sets an authentication tag' do
|
92
|
+
enc = klass.new(key, group[:iv])
|
93
|
+
enc.encrypt(plaintext, '')
|
94
|
+
expect(enc.tag).to eq group[:tag]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe '#decrypt' do
|
100
|
+
context 'when an invalid key is used' do
|
101
|
+
it 'raises an error' do
|
102
|
+
enc = klass.new('small')
|
103
|
+
expect { enc.decrypt('plain', 'auth') }.to raise_error(JWE::BadCEK)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context 'with a valid key' do
|
108
|
+
context 'when a valid tag is authenticated' do
|
109
|
+
it 'returns the plaintext' do
|
110
|
+
enc = klass.new(key, group[:iv])
|
111
|
+
enc.tag = group[:tag]
|
112
|
+
expect(enc.decrypt(group[:helloworld], '')).to eq plaintext
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
context 'when the tag is not valid' do
|
117
|
+
it 'raises an error' do
|
118
|
+
enc = klass.new(key, group[:iv])
|
119
|
+
enc.tag = "random"
|
120
|
+
expect { enc.decrypt(group[:helloworld], '') }.to raise_error(JWE::InvalidData)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
context 'when the tag is not set' do
|
125
|
+
it 'raises an error' do
|
126
|
+
enc = klass.new(key, group[:iv])
|
127
|
+
expect { enc.decrypt(group[:helloworld], '') }.to raise_error(JWE::InvalidData)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
context 'when the ciphertext is not valid' do
|
132
|
+
it 'raises an error' do
|
133
|
+
enc = klass.new(key, group[:iv])
|
134
|
+
enc.tag = group[:tag]
|
135
|
+
expect { enc.decrypt("random", '') }.to raise_error(JWE::InvalidData)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe '#cipher' do
|
142
|
+
context 'when the cipher is not supported by the OpenSSL lib' do
|
143
|
+
it 'raises an error' do
|
144
|
+
enc = klass.new
|
145
|
+
allow(enc).to receive(:cipher_name) { 'bad-cipher-128' }
|
146
|
+
expect { enc.cipher }.to raise_error(JWE::NotImplementedError)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
context 'when the cipher is supported' do
|
151
|
+
it 'returns the cipher object' do
|
152
|
+
enc = klass.new
|
153
|
+
allow(enc).to receive(:cipher_name) { OpenSSL::Cipher.ciphers.first }
|
154
|
+
expect(enc.cipher).to be_an OpenSSL::Cipher
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
describe '#cek' do
|
160
|
+
context 'when a key is not specified in initialization' do
|
161
|
+
it "returns a randomly generated #{group[:keylen]}-bytes key" do
|
162
|
+
expect(klass.new.cek.length).to eq group[:keylen]
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
context 'when a cek is given' do
|
167
|
+
it 'returns the cek' do
|
168
|
+
expect(klass.new('cek').cek).to eq 'cek'
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
describe '#iv' do
|
174
|
+
context 'when an iv is not specified in initialization' do
|
175
|
+
it "returns a randomly generated #{group[:ivlen]}-bytes iv" do
|
176
|
+
expect(klass.new.iv.length).to eq group[:ivlen]
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
context 'when a iv is given' do
|
181
|
+
it 'returns the iv' do
|
182
|
+
expect(klass.new('cek', 'iv').iv).to eq 'iv'
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
describe '.available?' do
|
188
|
+
context 'when the cipher is not available' do
|
189
|
+
it 'is false' do
|
190
|
+
allow_any_instance_of(klass).to receive(:cipher) { raise JWE::NotImplementedError.new }
|
191
|
+
expect(klass.available?).to be_falsey
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
context 'when the cipher is available' do
|
196
|
+
it 'is true' do
|
197
|
+
allow_any_instance_of(klass).to receive(:cipher)
|
198
|
+
expect(klass.available?).to be_truthy
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
describe 'full roundtrip' do
|
204
|
+
it 'decrypts the ciphertext to the original plaintext' do
|
205
|
+
enc = klass.new
|
206
|
+
ciphertext = enc.encrypt(plaintext, '')
|
207
|
+
expect(enc.decrypt(ciphertext, '')).to eq plaintext
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
describe JWE::Serialization::Compact do
|
2
|
+
describe '#encode' do
|
3
|
+
it 'returns components base64ed and joined with a dot' do
|
4
|
+
components = [ 'a', 'b', 'c', 'd', 'e' ]
|
5
|
+
expect(JWE::Serialization::Compact.encode(*components)).to eq 'YQ.Yg.Yw.ZA.ZQ'
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
describe '#decode' do
|
10
|
+
it 'returns an array with the 5 components' do
|
11
|
+
expect(JWE::Serialization::Compact.decode('YQ.Yg.Yw.ZA.ZQ')).to eq [ 'a', 'b', 'c', 'd', 'e' ]
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'raises an error when passed a badly formatted payload' do
|
15
|
+
expect { JWE::Serialization::Compact.decode('YQ.YQ.Yg.Yw.ZA.ZQ') }.to raise_error(JWE::DecodeError)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'jwe/zip/def'
|
2
|
+
|
3
|
+
describe JWE::Zip do
|
4
|
+
describe '.for' do
|
5
|
+
it 'returns a class for the specified zip' do
|
6
|
+
expect(JWE::Zip.for('DEF')).to eq JWE::Zip::Def
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'raises an error for a not-implemented zip' do
|
10
|
+
expect { JWE::Zip.for('BZIP2+JPG') }.to raise_error(JWE::NotImplementedError)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe JWE::Zip::Def do
|
16
|
+
it 'deflates and inflates to original payload' do
|
17
|
+
deflate = JWE::Zip::Def.new
|
18
|
+
deflated = deflate.compress("hello world")
|
19
|
+
expect(deflate.decompress(deflated)).to eq "hello world"
|
20
|
+
end
|
21
|
+
end
|
data/spec/jwe_spec.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
describe JWE do
|
2
|
+
let(:plaintext) { "The true sign of intelligence is not knowledge but imagination." }
|
3
|
+
let(:rsa_key) { OpenSSL::PKey::RSA.new File.read(File.dirname(__FILE__) + '/keys/rsa.pem') }
|
4
|
+
let(:password) { SecureRandom.random_bytes(64) }
|
5
|
+
|
6
|
+
it 'roundtrips' do
|
7
|
+
encrypted = JWE.encrypt(plaintext, rsa_key)
|
8
|
+
result = JWE.decrypt(encrypted, rsa_key)
|
9
|
+
|
10
|
+
expect(result).to eq plaintext
|
11
|
+
end
|
12
|
+
|
13
|
+
describe 'when using DEF compression' do
|
14
|
+
it 'roundtrips' do
|
15
|
+
encrypted = JWE.encrypt(plaintext, rsa_key, zip: 'DEF')
|
16
|
+
result = JWE.decrypt(encrypted, rsa_key)
|
17
|
+
|
18
|
+
expect(result).to eq plaintext
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe 'when using dir alg method' do
|
23
|
+
it 'roundtrips' do
|
24
|
+
encrypted = JWE.encrypt(plaintext, password, alg: 'dir')
|
25
|
+
result = JWE.decrypt(encrypted, password)
|
26
|
+
|
27
|
+
expect(result).to eq plaintext
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'raises when passed a bad alg' do
|
32
|
+
expect { JWE.encrypt(plaintext, rsa_key, alg: 'TEST') }.to raise_error(ArgumentError)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'raises when passed a bad enc' do
|
36
|
+
expect { JWE.encrypt(plaintext, rsa_key, enc: 'TEST') }.to raise_error(ArgumentError)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'raises when passed a bad zip' do
|
40
|
+
expect { JWE.encrypt(plaintext, rsa_key, zip: 'TEST') }.to raise_error(ArgumentError)
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'raises when decoding a bad alg' do
|
44
|
+
hdr = { alg: 'TEST', enc: 'A128GCM' }
|
45
|
+
payload = JWE::Base64.jwe_encode(hdr.to_json) + ".QY.QY.QY.QY"
|
46
|
+
expect { JWE.decrypt(payload, rsa_key) }.to raise_error(ArgumentError)
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'raises when decoding a bad enc' do
|
50
|
+
hdr = { alg: 'A192CBC-HS384', enc: 'TEST' }
|
51
|
+
payload = JWE::Base64.jwe_encode(hdr.to_json) + ".QY.QY.QY.QY"
|
52
|
+
expect { JWE.decrypt(payload, rsa_key) }.to raise_error(ArgumentError)
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'raises when decoding a bad zip' do
|
56
|
+
hdr = { alg: 'A192CBC-HS384', enc: 'A128GCM', zip: 'TEST' }
|
57
|
+
payload = JWE::Base64.jwe_encode(hdr.to_json) + ".QY.QY.QY.QY"
|
58
|
+
expect { JWE.decrypt(payload, rsa_key) }.to raise_error(ArgumentError)
|
59
|
+
end
|
60
|
+
end
|
data/spec/keys/rsa.pem
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
-----BEGIN RSA PRIVATE KEY-----
|
2
|
+
MIIEogIBAAKCAQEAsqm+NfpjE/i27FvgfOZmoQsC8WcokDRT7pJwK6fVL8nPs1KF
|
3
|
+
0YYHgtQMtsgh2KR1Z+y6cFiiXfzbksMP7XWn5h3G9uZVzaUAz3LM07TUqSA+9dkx
|
4
|
+
/QA9Q3VWP5iNBgo59E7LqkAKIE0wfx/rzH85VUCZLFBW5tjcaxzRWyCI9RcpPwmp
|
5
|
+
LtmNqoOxdhoy4O7r1mNTrcjlh+l/4I/yavS0+TXeImvOJkIbhIJhhbjE+GDiLEH4
|
6
|
+
GxE8j2SThs1nxJtboO1MMZr9hhoHL4Z5qRu6/t+ckO9ONYwUu8eDwQCOsluXsAoe
|
7
|
+
CdprYk92M/bvfhtV/C37AUZ6iZUFCS0/FE/VrQIDAQABAoIBAGL+jKdqAmX5hJm4
|
8
|
+
Ws25+Bm5eTr7Ns2YQP1K5J4703M0NkKdMgqjYhwKlLTedWqNzYP09mTzp5u+VIeg
|
9
|
+
T336mDp4O1toyxg0GhvX90hCxSak+F3Op9UQweFT7aM1SsaS+gO1eUHvU+0L+Bgo
|
10
|
+
PsZDpCfpsDWOmmg0twUepZ4BjAGIk8wBPA+cWi8Vmbvnwrwo1643LvtA3p76qUwJ
|
11
|
+
EPllQMmEnJ6gUNxQVgqQt/QM0UKPtZ5FyOK3zPcztY5xVO3SJCVvcKkciDo2M7wp
|
12
|
+
x9qkRrnBYmouNaQjZJZLHKngHr0DF16sw3ajk8qBZW3d2O72loiOxzAhMmnzm68f
|
13
|
+
dDHGNd0CgYEA2JueNMglqYWwOBQipboSMrDprSVR2We19Ji3VS1nXV+tvLsMyspq
|
14
|
+
YinH0SkW/xbLnpsOr9yC9jkW0KwFFqPK7TXnrXTVs3a4nAAaDyG8ciZQB2nKSRHt
|
15
|
+
H8DrM4IvIa1wU+mxj/Kdkp1L8dD6LLoLpmMsnxpjcvATImuZd5KqInsCgYEA0yeR
|
16
|
+
frrx8fOMo40WVVpinxxqycIHIBeI/jHeiwU8kGilQJcrWw3VxpoJriuLsNW2HWR+
|
17
|
+
nYz3Th6/FrJv6qhzwTFkgoqRS2Tw7qP+4gxk75hUR0M2a5d4Z0fjltQHaoP+P0kj
|
18
|
+
5iPgshDFDmRcMEPUFx+KtI+g59bTlo6U0gmJY/cCgYBiy/gJExE6lSOfMG/tL0WF
|
19
|
+
oXOz6cW/Z7JycgWM8DypNi7EWnynMlP7mhrtp9Q5XWhaW1cDl4yUSc3CN/PKM8Mn
|
20
|
+
FuMpFpUyWgAyB0nbhQOy/Q6bkwEU+vww84lT4RkmPzlwzLKUeZCtgtlU3oB9Tg5q
|
21
|
+
QenkV+DsV9wiYvmItHitaQKBgFNbe4ScKIdrrkmimP55ABXwEfg0MLvqjppK9Z/M
|
22
|
+
IWyg4xvskaEQhSQyC0BG0I6uz4Yq9hEcZUThvm4nYycv+QJ7jUI7kcBByRtsgmKa
|
23
|
+
of40FJFNZ15yHYYoSyBv872I/gXdyd5Aq6OgGyrjU8F6BXBbc1Z0nQDpPf5hqz5/
|
24
|
+
pU1hAoGAWLjOMTJCFoOOxtdZ39oJmSDN0hImu+KtYWAxa4BmBDimLxwa1Xn4qEsn
|
25
|
+
THDodsfMJc7HxYfeyzFZoqjf7vm2Et9eI+/PjT1CQx3DTYxsjk+8BaMqG6p/49Qr
|
26
|
+
//JbxdDS735BW/A5rU4TEiJfcV66lT7gI8lL8cFsV1rYPMJWkqc=
|
27
|
+
-----END RSA PRIVATE KEY-----
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jwe
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Francesco Boffa
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-01-13 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: codeclimate-test-reporter
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: A Ruby implementation of the RFC 7516 JSON Web Encryption (JWE) standard
|
56
|
+
email: fra.boffa@gmail.com
|
57
|
+
executables: []
|
58
|
+
extensions: []
|
59
|
+
extra_rdoc_files: []
|
60
|
+
files:
|
61
|
+
- ".codeclimate.yml"
|
62
|
+
- ".gitignore"
|
63
|
+
- ".rspec"
|
64
|
+
- ".rubocop.yml"
|
65
|
+
- ".travis.yml"
|
66
|
+
- Gemfile
|
67
|
+
- LICENSE.md
|
68
|
+
- Rakefile
|
69
|
+
- jwe.gemspec
|
70
|
+
- lib/jwe.rb
|
71
|
+
- lib/jwe/alg.rb
|
72
|
+
- lib/jwe/alg/dir.rb
|
73
|
+
- lib/jwe/alg/rsa15.rb
|
74
|
+
- lib/jwe/alg/rsa_oaep.rb
|
75
|
+
- lib/jwe/base64.rb
|
76
|
+
- lib/jwe/enc.rb
|
77
|
+
- lib/jwe/enc/a128cbc_hs256.rb
|
78
|
+
- lib/jwe/enc/a128gcm.rb
|
79
|
+
- lib/jwe/enc/a192cbc_hs384.rb
|
80
|
+
- lib/jwe/enc/a192gcm.rb
|
81
|
+
- lib/jwe/enc/a256cbc_hs512.rb
|
82
|
+
- lib/jwe/enc/a256gcm.rb
|
83
|
+
- lib/jwe/enc/aes_cbc_hs.rb
|
84
|
+
- lib/jwe/enc/aes_gcm.rb
|
85
|
+
- lib/jwe/serialization/compact.rb
|
86
|
+
- lib/jwe/version.rb
|
87
|
+
- lib/jwe/zip.rb
|
88
|
+
- lib/jwe/zip/def.rb
|
89
|
+
- spec/jwe/alg_spec.rb
|
90
|
+
- spec/jwe/base64_spec.rb
|
91
|
+
- spec/jwe/enc_spec.rb
|
92
|
+
- spec/jwe/serialization_spec.rb
|
93
|
+
- spec/jwe/zip_spec.rb
|
94
|
+
- spec/jwe_spec.rb
|
95
|
+
- spec/keys/rsa.pem
|
96
|
+
- spec/spec_helper.rb
|
97
|
+
homepage: http://github.com/aomega08/jwe
|
98
|
+
licenses:
|
99
|
+
- MIT
|
100
|
+
metadata: {}
|
101
|
+
post_install_message:
|
102
|
+
rdoc_options: []
|
103
|
+
require_paths:
|
104
|
+
- lib
|
105
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0'
|
115
|
+
requirements: []
|
116
|
+
rubyforge_project:
|
117
|
+
rubygems_version: 2.4.5.1
|
118
|
+
signing_key:
|
119
|
+
specification_version: 4
|
120
|
+
summary: JSON Web Encryption implementation in Ruby
|
121
|
+
test_files: []
|