jwe 0.2.0 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fa8fe82eeb7d259a610ce7e6a89374a8d5cd7ff2
4
- data.tar.gz: 0a1a8862400049ee407852f0e21cbb0cb6a8210b
3
+ metadata.gz: 05c87a9be4e5864d4f31495bd609ff3d4abf1a68
4
+ data.tar.gz: dc5edbbac44a91be23673da3f5d0b2beb91a8245
5
5
  SHA512:
6
- metadata.gz: 40e851c53020b8c790080090ba7ca53e9e4264a5bcb990dd78598d2b0109ca5fe83e77fe2a3ae40c2ee5fcf75265e6074985f1038ef5ebd4003df8fa23fc5b59
7
- data.tar.gz: f01772b4e6dc52ccded47081617c2890d21faf017b00ee285e525138e7d65765d9b2e38889b391159cf876e20328e81423afdf0123bcdd9497cc6581d4ad5478
6
+ metadata.gz: a26d929b4124949bbac9137855a32b42a9ab9165c0025e405e924937e99c9222e88096a1d5e40c93360f4e491df4f43ebe80a4f6d9fe0bba8bc34b9d10a94d99
7
+ data.tar.gz: b3e9575add99b0ad449c083a3981d419aaeaf17748591f00e293fcb61e556257b2b371af2c13c7ce8f294cfe72ec39d0f5cac7d6f2b46dd2c2864045410962dc
data/README.md CHANGED
@@ -84,9 +84,9 @@ Key management:
84
84
  * RSA1_5
85
85
  * RSA-OAEP (default)
86
86
  * ~~RSA-OAEP-256~~
87
- * ~~A128KW~~
88
- * ~~A192KW~~
89
- * ~~A256KW~~
87
+ * A128KW
88
+ * A192KW
89
+ * A256KW
90
90
  * dir
91
91
  * ~~ECDH-ES~~
92
92
  * ~~ECDH-ES+A128KW~~
@@ -1,3 +1,4 @@
1
+ require 'jwe/alg/a128_kw'
1
2
  require 'jwe/alg/dir'
2
3
  require 'jwe/alg/rsa_oaep'
3
4
  require 'jwe/alg/rsa15'
@@ -0,0 +1,13 @@
1
+ require 'jwe/alg/aes_kw'
2
+
3
+ module JWE
4
+ module Alg
5
+ class A128Kw
6
+ include AesKw
7
+
8
+ def cipher_name
9
+ 'AES-128-ECB'
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require 'jwe/alg/aes_kw'
2
+
3
+ module JWE
4
+ module Alg
5
+ class A192Kw
6
+ include AesKw
7
+
8
+ def cipher_name
9
+ 'AES-192-ECB'
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require 'jwe/alg/aes_kw'
2
+
3
+ module JWE
4
+ module Alg
5
+ class A256Kw
6
+ include AesKw
7
+
8
+ def cipher_name
9
+ 'AES-256-ECB'
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,84 @@
1
+ module JWE
2
+ module Alg
3
+ module AesKw
4
+ attr_accessor :key
5
+ attr_accessor :iv
6
+
7
+ def initialize(key = nil, iv = "\xA6\xA6\xA6\xA6\xA6\xA6\xA6\xA6")
8
+ self.iv = iv.force_encoding('ASCII-8BIT')
9
+ self.key = key.force_encoding('ASCII-8BIT')
10
+ end
11
+
12
+ def encrypt(cek)
13
+ a = iv
14
+ r = cek.scan(/.{8}/m)
15
+
16
+ 6.times do |j|
17
+ r.length.times do |i|
18
+ b = encrypt_round(a + r[i])
19
+
20
+ a = b.chars.first(8).join
21
+ r[i] = b.chars.last(8).join
22
+
23
+ t = (r.length * j) + i + 1
24
+ a = xor(a, t)
25
+ end
26
+ end
27
+
28
+ ([a] + r).join
29
+ end
30
+
31
+ def decrypt(encrypted_cek)
32
+ c = encrypted_cek.scan(/.{8}/m)
33
+ a = c[0]
34
+
35
+ r = c[1..c.length]
36
+
37
+ 5.downto(0) do |j|
38
+ r.length.downto(1) do |i|
39
+ t = (r.length * j) + i
40
+ a = xor(a, t)
41
+
42
+ b = decrypt_round(a + r[i - 1])
43
+
44
+ a = b.chars.first(8).join
45
+ r[i - 1] = b.chars.last(8).join
46
+ end
47
+ end
48
+
49
+ if a != iv
50
+ raise StandardError.new('The encrypted key has been tampered. Do not use this key.')
51
+ end
52
+
53
+ r.join
54
+ end
55
+
56
+ def cipher
57
+ @cipher ||= OpenSSL::Cipher.new(cipher_name)
58
+ rescue RuntimeError
59
+ raise JWE::NotImplementedError.new("The version of OpenSSL linked to your Ruby does not support the cipher #{cipher_name}.")
60
+ end
61
+
62
+ def encrypt_round(data)
63
+ cipher.encrypt
64
+ cipher.key = key
65
+ cipher.padding = 0
66
+ cipher.update(data) + cipher.final
67
+ end
68
+
69
+ def decrypt_round(data)
70
+ cipher.decrypt
71
+ cipher.key = key
72
+ cipher.padding = 0
73
+ cipher.update(data) + cipher.final
74
+ end
75
+
76
+ def xor(data, t)
77
+ t = ([0] * (data.length - 1)) + [t]
78
+ data = data.chars.map(&:ord)
79
+
80
+ data.zip(t).map { |a, b| (a ^ b).chr }.join
81
+ end
82
+ end
83
+ end
84
+ end
@@ -1,3 +1,3 @@
1
1
  module JWE
2
- VERSION = '0.2.0'.freeze
2
+ VERSION = '0.3.0'.freeze
3
3
  end
@@ -4,11 +4,19 @@ module JWE
4
4
  module Zip
5
5
  class Def
6
6
  def compress(payload)
7
- Zlib::Deflate.deflate(payload)
7
+ zlib = Zlib::Deflate.new(Zlib::DEFAULT_COMPRESSION, -Zlib::MAX_WBITS)
8
+ zlib.deflate(payload)
9
+ zlib.finish
8
10
  end
9
11
 
12
+ # Was using RFC 1950 instead of 1951.
10
13
  def decompress(payload)
11
14
  Zlib::Inflate.inflate(payload)
15
+
16
+ # Keeping compatibility for old encoded tokens
17
+ rescue Zlib::DataError
18
+ inflate = Zlib::Inflate.new(-Zlib::MAX_WBITS)
19
+ inflate.inflate(payload)
12
20
  end
13
21
  end
14
22
  end
@@ -1,6 +1,9 @@
1
1
  require 'jwe/alg/dir'
2
2
  require 'jwe/alg/rsa_oaep'
3
3
  require 'jwe/alg/rsa15'
4
+ require 'jwe/alg/a128_kw'
5
+ require 'jwe/alg/a192_kw'
6
+ require 'jwe/alg/a256_kw'
4
7
  require 'openssl'
5
8
 
6
9
  describe JWE::Alg do
@@ -64,3 +67,34 @@ describe JWE::Alg::Rsa15 do
64
67
  expect(alg.decrypt(ciphertext)).to eq 'random key'
65
68
  end
66
69
  end
70
+
71
+ [
72
+ JWE::Alg::A128Kw,
73
+ JWE::Alg::A192Kw,
74
+ JWE::Alg::A256Kw
75
+ ].each_with_index do |klass, i|
76
+ describe klass do
77
+ let(:kek) { SecureRandom.random_bytes(16 + i * 8) }
78
+ let(:cek) { SecureRandom.random_bytes(32) }
79
+ let(:alg) { klass.new(kek) }
80
+
81
+ describe '#encrypt' do
82
+ it 'returns an encrypted string' do
83
+ expect(alg.encrypt(cek)).to_not eq cek
84
+ end
85
+ end
86
+
87
+ it 'decrypts the encrypted key to the original key' do
88
+ ciphertext = alg.encrypt(cek)
89
+ expect(alg.decrypt(ciphertext)).to eq cek
90
+ end
91
+
92
+ it 'raises when trying to decrypt tampered keys' do
93
+ alg = klass.new(kek, "\xA7\xA7\xA7\xA7\xA6\xA6\xA6\xA6")
94
+ ciphertext = alg.encrypt(cek)
95
+
96
+ bad_alg = klass.new(kek, "\xA7\xA7\xA7\xA7\xA7\xA7\xA7\xA7")
97
+ expect { bad_alg.decrypt(ciphertext) }.to raise_error(StandardError)
98
+ end
99
+ end
100
+ end
@@ -18,4 +18,10 @@ describe JWE::Zip::Def do
18
18
  deflated = deflate.compress('hello world')
19
19
  expect(deflate.decompress(deflated)).to eq 'hello world'
20
20
  end
21
+
22
+ it 'can deflate an RFC 1950 compressed message' do
23
+ deflated = Zlib::Deflate.deflate('hello world')
24
+ deflate = JWE::Zip::Def.new
25
+ expect(deflate.decompress(deflated)).to eq 'hello world'
26
+ end
21
27
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jwe
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Francesco Boffa
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-04-02 00:00:00.000000000 Z
11
+ date: 2017-05-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -70,6 +70,10 @@ files:
70
70
  - jwe.gemspec
71
71
  - lib/jwe.rb
72
72
  - lib/jwe/alg.rb
73
+ - lib/jwe/alg/a128_kw.rb
74
+ - lib/jwe/alg/a192_kw.rb
75
+ - lib/jwe/alg/a256_kw.rb
76
+ - lib/jwe/alg/aes_kw.rb
73
77
  - lib/jwe/alg/dir.rb
74
78
  - lib/jwe/alg/rsa15.rb
75
79
  - lib/jwe/alg/rsa_oaep.rb