jwa 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +15 -0
  3. data/.gitignore +17 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +16 -0
  6. data/.travis.yml +15 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE.md +23 -0
  9. data/README.md +26 -0
  10. data/Rakefile +6 -0
  11. data/bin/console +14 -0
  12. data/bin/setup +8 -0
  13. data/jwa.gemspec +27 -0
  14. data/lib/jwa.rb +16 -0
  15. data/lib/jwa/algorithms.rb +2 -0
  16. data/lib/jwa/algorithms/content_encryption.rb +29 -0
  17. data/lib/jwa/algorithms/content_encryption/a128_cbc_hs256.rb +29 -0
  18. data/lib/jwa/algorithms/content_encryption/a128_gcm.rb +25 -0
  19. data/lib/jwa/algorithms/content_encryption/a192_cbc_hs384.rb +29 -0
  20. data/lib/jwa/algorithms/content_encryption/a192_gcm.rb +25 -0
  21. data/lib/jwa/algorithms/content_encryption/a256_cbc_hs512.rb +29 -0
  22. data/lib/jwa/algorithms/content_encryption/a256_gcm.rb +25 -0
  23. data/lib/jwa/algorithms/content_encryption/aes_cbc_hs.rb +85 -0
  24. data/lib/jwa/algorithms/content_encryption/aes_gcm.rb +64 -0
  25. data/lib/jwa/algorithms/key_management.rb +56 -0
  26. data/lib/jwa/algorithms/key_management/a128_gcm_kw.rb +21 -0
  27. data/lib/jwa/algorithms/key_management/a128_kw.rb +21 -0
  28. data/lib/jwa/algorithms/key_management/a192_gcm_kw.rb +21 -0
  29. data/lib/jwa/algorithms/key_management/a192_kw.rb +21 -0
  30. data/lib/jwa/algorithms/key_management/a256_gcm_kw.rb +21 -0
  31. data/lib/jwa/algorithms/key_management/a256_kw.rb +21 -0
  32. data/lib/jwa/algorithms/key_management/aes_gcm_kw.rb +26 -0
  33. data/lib/jwa/algorithms/key_management/aes_kw.rb +100 -0
  34. data/lib/jwa/algorithms/key_management/ecdh_es.rb +45 -0
  35. data/lib/jwa/algorithms/key_management/ecdh_es_a128_kw.rb +25 -0
  36. data/lib/jwa/algorithms/key_management/ecdh_es_a192_kw.rb +25 -0
  37. data/lib/jwa/algorithms/key_management/ecdh_es_a256_kw.rb +25 -0
  38. data/lib/jwa/algorithms/key_management/ecdh_es_kw.rb +23 -0
  39. data/lib/jwa/algorithms/key_management/pbes2.rb +27 -0
  40. data/lib/jwa/algorithms/key_management/pbes_hs256_a128_kw.rb +25 -0
  41. data/lib/jwa/algorithms/key_management/pbes_hs384_a192_kw.rb +25 -0
  42. data/lib/jwa/algorithms/key_management/pbes_hs512_a256_kw.rb +25 -0
  43. data/lib/jwa/algorithms/key_management/rsa15.rb +20 -0
  44. data/lib/jwa/algorithms/key_management/rsa_oaep.rb +20 -0
  45. data/lib/jwa/cipher.rb +17 -0
  46. data/lib/jwa/support/concat_kdf.rb +29 -0
  47. data/lib/jwa/support/pbkdf2.rb +48 -0
  48. data/lib/jwa/version.rb +3 -0
  49. data/spec/jwa/algorithms/content_encryption/a128_cbc_hs256_spec.rb +30 -0
  50. data/spec/jwa/algorithms/content_encryption/a128_gcm_spec.rb +42 -0
  51. data/spec/jwa/algorithms/content_encryption/a192_cbc_hs384_spec.rb +34 -0
  52. data/spec/jwa/algorithms/content_encryption/a192_gcm_spec.rb +49 -0
  53. data/spec/jwa/algorithms/content_encryption/a256_cbc_hs512_spec.rb +35 -0
  54. data/spec/jwa/algorithms/content_encryption/a256_gcm_spec.rb +61 -0
  55. data/spec/jwa/algorithms/content_encryption/aes_cbc_hs_shared.rb +96 -0
  56. data/spec/jwa/algorithms/content_encryption/aes_gcm_shared.rb +60 -0
  57. data/spec/jwa/algorithms/content_encryption_spec.rb +7 -0
  58. data/spec/jwa/algorithms/key_management/a128_kw_spec.rb +43 -0
  59. data/spec/jwa/algorithms/key_management/a192_kw_spec.rb +29 -0
  60. data/spec/jwa/algorithms/key_management/a256_kw_spec.rb +29 -0
  61. data/spec/jwa/algorithms/key_management/ecdh_es_spec.rb +36 -0
  62. data/spec/jwa/algorithms/key_management/pbes2_hs256_a128_kw_spec.rb +27 -0
  63. data/spec/jwa/algorithms/key_management/pbes2_hs384_a192_kw_spec.rb +32 -0
  64. data/spec/jwa/algorithms/key_management/pbes2_hs512_a256_kw_spec.rb +32 -0
  65. data/spec/jwa/algorithms/key_management/rsa15_spec.rb +44 -0
  66. data/spec/jwa/algorithms/key_management/rsa_oaep_spec.rb +44 -0
  67. data/spec/jwa/algorithms/key_management_spec.rb +7 -0
  68. data/spec/jwa/cipher_spec.rb +7 -0
  69. data/spec/jwa/support/concat_kdf_spec.rb +32 -0
  70. data/spec/jwa/support/pbkdf2_spec.rb +111 -0
  71. data/spec/jwa_spec.rb +5 -0
  72. data/spec/spec_helper.rb +22 -0
  73. data/spec/support/ec1.json +7 -0
  74. data/spec/support/ec2.json +7 -0
  75. data/spec/support/hex_helpers.rb +9 -0
  76. data/spec/support/oct16.json +4 -0
  77. data/spec/support/oct24.json +4 -0
  78. data/spec/support/oct32.json +4 -0
  79. data/spec/support/rsa1.json +11 -0
  80. data/spec/support/rsa2.json +11 -0
  81. metadata +193 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f154fe25018f76d01fdefa5241f8fef1c97441fc
4
+ data.tar.gz: 03bf77eb30a19f599a3bcca86fb596191d2fdc41
5
+ SHA512:
6
+ metadata.gz: b4e2acc04af0e0e5f4cf6a95b59d08cc5507023525a17669a42900d21af96ff2470a8bcbafb19df63194cdcc5beb2f48e61657da4a9a0a04218d4018849e7618
7
+ data.tar.gz: 9438c78e5962c00f466fb2899b1b4140d18b699a10375858df84efec25a40f4204722634987a513dbb22f281b85017a5e8a2e73b6714b89fa04a69df8985b06c
@@ -0,0 +1,15 @@
1
+ engines:
2
+ duplication:
3
+ enabled: true
4
+ config:
5
+ languages:
6
+ - ruby
7
+ rubocop:
8
+ enabled: true
9
+
10
+ ratings:
11
+ paths:
12
+ - lib/**
13
+
14
+ exclude_paths:
15
+ - spec/**/*
@@ -0,0 +1,17 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /.ruby-gemset
11
+ /.ruby-version
12
+ Gemfile.lock
13
+ /coverage
14
+ .byebug_history
15
+
16
+ # rspec failure tracking
17
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,16 @@
1
+ Metrics/LineLength:
2
+ Max: 120
3
+
4
+ Metrics/BlockLength:
5
+ Exclude:
6
+ - spec/**/*
7
+
8
+ Style/Documentation:
9
+ Enabled: false
10
+
11
+ Style/GuardClause:
12
+ Enabled: false
13
+
14
+ Lint/RescueException:
15
+ Exclude:
16
+ - spec/**/*
@@ -0,0 +1,15 @@
1
+ language: ruby
2
+ dist: trusty
3
+ rvm:
4
+ - jruby-9.1.9.0
5
+ - 2.0.0-p648
6
+ - 2.1.10
7
+ - 2.2.8
8
+ - 2.3.5
9
+ - 2.4.2
10
+
11
+ after_script:
12
+ - bundle exec codeclimate-test-reporter
13
+
14
+ env:
15
+ CODECLIMATE_REPO_TOKEN: 085192d9e3537c39bd01d2f110619ba42dfc7899f5aaa883b612cf0cabdd65d4
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in jwa.gemspec
4
+ gemspec
@@ -0,0 +1,23 @@
1
+ The MIT License (MIT)
2
+ =====================
3
+
4
+ * Copyright © 2017 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.
@@ -0,0 +1,26 @@
1
+ # JWA
2
+
3
+ [![Build Status](https://travis-ci.org/jwt/ruby-jwa.svg)](https://travis-ci.org/jwt/ruby-jwa)
4
+ [![Code Climate](https://codeclimate.com/github/jwt/ruby-jwa/badges/gpa.svg)](https://codeclimate.com/github/jwt/ruby-jwa)
5
+ [![Test Coverage](https://codeclimate.com/github/jwt/ruby-jwa/badges/coverage.svg)](https://codeclimate.com/github/jwt/ruby-jwa/coverage)
6
+
7
+ A ruby implementation of the RFC 7518 JSON Web Algorithms (JWA) standard.
8
+
9
+ ## Installation
10
+
11
+ $ gem install jwa
12
+
13
+ ## Usage
14
+
15
+ TODO: Write usage instructions here
16
+
17
+ ## License
18
+
19
+ The MIT License
20
+
21
+ Copyright © 2017 Francesco Boffa
22
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
23
+
24
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
25
+
26
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'jwa'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require 'irb'
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+
3
+ lib = File.expand_path('../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'jwa/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'jwa'
9
+ spec.version = JWA::VERSION
10
+ spec.authors = ['Francesco Boffa']
11
+ spec.email = ['fra.boffa@gmail.com']
12
+
13
+ spec.summary = 'JSON Web Algorithms implementation in Ruby'
14
+ spec.description = 'A Ruby implementation of the RFC 7518 JSON Web Algorithms (JWA) standard'
15
+ spec.homepage = 'https://github.com/jwt/ruby-jwa'
16
+ spec.license = 'MIT'
17
+
18
+ spec.files = `git ls-files`.split("\n")
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_dependency 'jwk'
22
+
23
+ spec.add_development_dependency 'rspec'
24
+ spec.add_development_dependency 'rake'
25
+ spec.add_development_dependency 'simplecov'
26
+ spec.add_development_dependency 'codeclimate-test-reporter'
27
+ end
@@ -0,0 +1,16 @@
1
+ require 'base64'
2
+ require 'json'
3
+ require 'jwk'
4
+ require 'openssl'
5
+ require 'securerandom'
6
+
7
+ require 'jwa/algorithms'
8
+ require 'jwa/cipher'
9
+ require 'jwa/version'
10
+
11
+ require 'jwa/support/concat_kdf'
12
+ require 'jwa/support/pbkdf2'
13
+
14
+ module JWA
15
+ class BadDecrypt < StandardError; end
16
+ end
@@ -0,0 +1,2 @@
1
+ require 'jwa/algorithms/content_encryption'
2
+ require 'jwa/algorithms/key_management'
@@ -0,0 +1,29 @@
1
+ require 'jwa/algorithms/content_encryption/a128_cbc_hs256'
2
+ require 'jwa/algorithms/content_encryption/a192_cbc_hs384'
3
+ require 'jwa/algorithms/content_encryption/a256_cbc_hs512'
4
+
5
+ require 'jwa/algorithms/content_encryption/a128_gcm'
6
+ require 'jwa/algorithms/content_encryption/a192_gcm'
7
+ require 'jwa/algorithms/content_encryption/a256_gcm'
8
+
9
+ module JWA
10
+ module Algorithms
11
+ module ContentEncryption
12
+ KNOWN_ENCS = {
13
+ 'A128CBC-HS256' => A128CbcHs256,
14
+ 'A192CBC-HS384' => A192CbcHs384,
15
+ 'A256CBC-HS512' => A256CbcHs512,
16
+
17
+ 'A128GCM' => A128Gcm,
18
+ 'A192GCM' => A192Gcm,
19
+ 'A256GCM' => A256Gcm
20
+ }.freeze
21
+
22
+ class << self
23
+ def for(name)
24
+ KNOWN_ENCS[name]
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ require 'jwa/algorithms/content_encryption/aes_cbc_hs'
2
+
3
+ module JWA
4
+ module Algorithms
5
+ module ContentEncryption
6
+ class A128CbcHs256
7
+ include AesCbcHs
8
+
9
+ class << self
10
+ def enc_name
11
+ 'A128CBC-HS256'
12
+ end
13
+
14
+ def key_length
15
+ 32
16
+ end
17
+
18
+ def cipher_name
19
+ 'AES-128-CBC'
20
+ end
21
+
22
+ def hash
23
+ OpenSSL::Digest::SHA256.new
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,25 @@
1
+ require 'jwa/algorithms/content_encryption/aes_gcm'
2
+
3
+ module JWA
4
+ module Algorithms
5
+ module ContentEncryption
6
+ class A128Gcm
7
+ include AesGcm
8
+
9
+ class << self
10
+ def enc_name
11
+ 'A128GCM'
12
+ end
13
+
14
+ def key_length
15
+ 16
16
+ end
17
+
18
+ def cipher_name
19
+ 'aes-128-gcm'
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,29 @@
1
+ require 'jwa/algorithms/content_encryption/aes_cbc_hs'
2
+
3
+ module JWA
4
+ module Algorithms
5
+ module ContentEncryption
6
+ class A192CbcHs384
7
+ include AesCbcHs
8
+
9
+ class << self
10
+ def enc_name
11
+ 'A192CBC-HS384'
12
+ end
13
+
14
+ def key_length
15
+ 48
16
+ end
17
+
18
+ def cipher_name
19
+ 'AES-192-CBC'
20
+ end
21
+
22
+ def hash
23
+ OpenSSL::Digest::SHA384.new
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,25 @@
1
+ require 'jwa/algorithms/content_encryption/aes_gcm'
2
+
3
+ module JWA
4
+ module Algorithms
5
+ module ContentEncryption
6
+ class A192Gcm
7
+ include AesGcm
8
+
9
+ class << self
10
+ def enc_name
11
+ 'A192GCM'
12
+ end
13
+
14
+ def key_length
15
+ 24
16
+ end
17
+
18
+ def cipher_name
19
+ 'aes-192-gcm'
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,29 @@
1
+ require 'jwa/algorithms/content_encryption/aes_cbc_hs'
2
+
3
+ module JWA
4
+ module Algorithms
5
+ module ContentEncryption
6
+ class A256CbcHs512
7
+ include AesCbcHs
8
+
9
+ class << self
10
+ def enc_name
11
+ 'A256CBC-HS512'
12
+ end
13
+
14
+ def key_length
15
+ 64
16
+ end
17
+
18
+ def cipher_name
19
+ 'AES-256-CBC'
20
+ end
21
+
22
+ def hash
23
+ OpenSSL::Digest::SHA512.new
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,25 @@
1
+ require 'jwa/algorithms/content_encryption/aes_gcm'
2
+
3
+ module JWA
4
+ module Algorithms
5
+ module ContentEncryption
6
+ class A256Gcm
7
+ include AesGcm
8
+
9
+ class << self
10
+ def enc_name
11
+ 'A256GCM'
12
+ end
13
+
14
+ def key_length
15
+ 32
16
+ end
17
+
18
+ def cipher_name
19
+ 'aes-256-gcm'
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,85 @@
1
+ require 'jwa/cipher'
2
+
3
+ module JWA
4
+ module Algorithms
5
+ module ContentEncryption
6
+ # Abstract AES in CBC mode, with SHA2 signature for different key sizes.
7
+ module AesCbcHs
8
+ attr_reader :key, :iv
9
+
10
+ def initialize(key, iv = nil)
11
+ @key = key
12
+ @iv = iv || SecureRandom.random_bytes(16)
13
+
14
+ if @key.length != self.class.key_length
15
+ raise ArgumentError, "Invalid Key. Expected length: #{self.class.key_length}. Actual: #{@key.length}."
16
+ end
17
+
18
+ if @iv.length != 16
19
+ raise ArgumentError, "Invalid IV. Expected length: 16. Actual: #{@iv.length}."
20
+ end
21
+ end
22
+
23
+ def encrypt(plaintext, authenticated_data)
24
+ ciphertext = cipher_round(:encrypt, plaintext)
25
+ signature = generate_tag(authenticated_data, ciphertext)
26
+
27
+ [ciphertext, signature]
28
+ end
29
+
30
+ def decrypt(ciphertext, authenticated_data, tag)
31
+ signature = generate_tag(authenticated_data, ciphertext)
32
+ if signature != tag
33
+ raise JWA::BadDecrypt, 'Signature check failed. The AAD may have been tampered.'
34
+ end
35
+
36
+ cipher_round(:decrypt, ciphertext)
37
+ rescue OpenSSL::Cipher::CipherError
38
+ raise JWA::BadDecrypt, 'Invalid ciphertext or authentication tag.'
39
+ end
40
+
41
+ def cipher_round(direction, data)
42
+ cipher.send(direction)
43
+ cipher.key = enc_key
44
+ cipher.iv = @iv
45
+
46
+ cipher.update(data) + cipher.final
47
+ end
48
+
49
+ def generate_tag(authenticated_data, ciphertext)
50
+ length = [authenticated_data.length * 8].pack('Q>') # 64bit big endian
51
+
52
+ to_sign = authenticated_data + @iv + ciphertext + length
53
+ signature = OpenSSL::HMAC.digest(self.class.hash, mac_key, to_sign)
54
+
55
+ signature[0...mac_key.length]
56
+ end
57
+
58
+ def mac_key
59
+ @key[0...self.class.key_length / 2]
60
+ end
61
+
62
+ def enc_key
63
+ @key[self.class.key_length / 2..-1]
64
+ end
65
+
66
+ def cipher
67
+ @cipher ||= Cipher.for(self.class.cipher_name)
68
+ end
69
+
70
+ def self.included(base)
71
+ base.extend(ClassMethods)
72
+ end
73
+
74
+ module ClassMethods
75
+ def available?
76
+ Cipher.for(cipher_name)
77
+ true
78
+ rescue NotImplementedError
79
+ false
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end