jwt_nacl 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +36 -0
- data/.travis.yml +7 -0
- data/CHANGELOG.md +6 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +50 -0
- data/LICENSE.md +9 -0
- data/README.md +117 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/jwt_nacl.gemspec +38 -0
- data/lib/jwt_nacl.rb +64 -0
- data/lib/jwt_nacl/base64_url.rb +42 -0
- data/lib/jwt_nacl/ed25519.rb +45 -0
- data/lib/jwt_nacl/jws.rb +58 -0
- data/lib/jwt_nacl/util.rb +34 -0
- data/lib/jwt_nacl/version.rb +3 -0
- metadata +189 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ad2b78d24e6d521e31f3c32794553484c053fec8
|
4
|
+
data.tar.gz: 558da9dbdb8ff7503d69864e1d4e35c65a855ed4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c8de2dc24a3557ae69b88c97e57e34a6d9257a8e40a756f65beb678936c109edc4469ce469fb707a64abc119633811c5f7f413a69473e9245571fe59df983f3f
|
7
|
+
data.tar.gz: ade53f2ea0edf4f97e193b69f6c661bd53334fce46fd588730ffcfb5cabe0d729902a9d3c127024eba573a0353388a7fa8c63816640d58a762632f55da9dc019
|
data/.gitignore
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
https://raw.githubusercontent.com/github/gitignore/master/Ruby.gitignore
|
2
|
+
|
3
|
+
*.gem
|
4
|
+
*.rbc
|
5
|
+
/.config
|
6
|
+
/coverage/
|
7
|
+
/InstalledFiles
|
8
|
+
/pkg/
|
9
|
+
/spec/reports/
|
10
|
+
/spec/examples.txt
|
11
|
+
/test/tmp/
|
12
|
+
/test/version_tmp/
|
13
|
+
/tmp/
|
14
|
+
|
15
|
+
# Used by dotenv library to load environment variables.
|
16
|
+
# .env
|
17
|
+
|
18
|
+
## Documentation cache and generated files:
|
19
|
+
/.yardoc/
|
20
|
+
/_yardoc/
|
21
|
+
/doc/
|
22
|
+
/rdoc/
|
23
|
+
|
24
|
+
## Environment normalization:
|
25
|
+
/.bundle/
|
26
|
+
/vendor/bundle
|
27
|
+
/lib/bundler/man/
|
28
|
+
|
29
|
+
# for a library or gem, you might want to ignore these files since the code is
|
30
|
+
# intended to run in multiple environments; otherwise, check them in:
|
31
|
+
# Gemfile.lock
|
32
|
+
# .ruby-version
|
33
|
+
# .ruby-gemset
|
34
|
+
|
35
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
36
|
+
.rvmrc
|
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
jwt_nacl (0.1.0)
|
5
|
+
rbnacl (~> 4.0)
|
6
|
+
rbnacl-libsodium (~> 1.0)
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
ansi (1.5.0)
|
12
|
+
builder (3.2.3)
|
13
|
+
docile (1.1.5)
|
14
|
+
ffi (1.9.17)
|
15
|
+
json (2.0.3)
|
16
|
+
minitest (5.10.1)
|
17
|
+
minitest-reporters (1.1.14)
|
18
|
+
ansi
|
19
|
+
builder
|
20
|
+
minitest (>= 5.0)
|
21
|
+
ruby-progressbar
|
22
|
+
rake (12.0.0)
|
23
|
+
rbnacl (4.0.1)
|
24
|
+
ffi
|
25
|
+
rbnacl-libsodium (1.0.11)
|
26
|
+
rbnacl (>= 3.0.1)
|
27
|
+
ruby-progressbar (1.8.1)
|
28
|
+
simplecov (0.13.0)
|
29
|
+
docile (~> 1.1.0)
|
30
|
+
json (>= 1.8, < 3)
|
31
|
+
simplecov-html (~> 0.10.0)
|
32
|
+
simplecov-html (0.10.0)
|
33
|
+
wwtd (1.3.0)
|
34
|
+
yard (0.9.8)
|
35
|
+
|
36
|
+
PLATFORMS
|
37
|
+
ruby
|
38
|
+
|
39
|
+
DEPENDENCIES
|
40
|
+
bundler (~> 1.13)
|
41
|
+
jwt_nacl!
|
42
|
+
minitest (~> 5.0)
|
43
|
+
minitest-reporters (~> 1.1)
|
44
|
+
rake (~> 12.0)
|
45
|
+
simplecov (~> 0.13)
|
46
|
+
wwtd (~> 1.3)
|
47
|
+
yard (~> 0.9)
|
48
|
+
|
49
|
+
BUNDLED WITH
|
50
|
+
1.13.7
|
data/LICENSE.md
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 Gary Fleshman
|
4
|
+
|
5
|
+
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:
|
6
|
+
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
8
|
+
|
9
|
+
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.
|
data/README.md
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
# JWT NaCl [![travis][ci_img]][travis] [![yard docs][yd_img]][yard_docs] [![code climate][cc_img]][code_climate]
|
2
|
+
|
3
|
+
## A JSON Web Token (JWT) implementation using NaCl cryptography
|
4
|
+
|
5
|
+
### Description
|
6
|
+
A Ruby JSON Web Token implementation using Edwards-curve Digital Signature Algorithm ([EdDSA][eddsa]) [curve Ed25519 digital signatures][ed25519] from the state-of-the-art NaCl [Networking and Cryptography library][nacl] by [Daniel J. Bernstein][bernstein].
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Add this line to your application's Gemfile:
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
gem 'jwt_nacl'
|
14
|
+
```
|
15
|
+
|
16
|
+
And then execute:
|
17
|
+
|
18
|
+
$ bundle
|
19
|
+
|
20
|
+
Or install it directly as:
|
21
|
+
|
22
|
+
$ gem install jwt_nacl
|
23
|
+
|
24
|
+
### Philosophy & Design Goals
|
25
|
+
* Convention over configuration
|
26
|
+
* Use of state-of-the-art cryptography, including EdDSA Curve25519 elliptic curves
|
27
|
+
* Minimal API surface area
|
28
|
+
* Thorough test coverage
|
29
|
+
* Modularity for comprehension and extensibility
|
30
|
+
|
31
|
+
### Why NaCl?
|
32
|
+
Cryptography typically exposes a high degree of complexity, due to many possible configuration decisions. One poor choice could result in an insecure system.
|
33
|
+
|
34
|
+
NaCl is different. NaCl provides an expertly-assembled, high-level cryptographic API with the correct configuration already built-in. See [RbNaCl][rbnacl] for more rationale.
|
35
|
+
|
36
|
+
For a more conventional JWT implementation, please refer to the related [json_web_token](https://github.com/garyf/json_web_token) gem.
|
37
|
+
|
38
|
+
## Usage
|
39
|
+
|
40
|
+
### JWT.sign(claims, private_key)
|
41
|
+
|
42
|
+
#### Returns a 3 element hash that includes:
|
43
|
+
* a JSON Web Token
|
44
|
+
* the private key
|
45
|
+
* the public key
|
46
|
+
|
47
|
+
`claims` (required) hash (non-empty)
|
48
|
+
|
49
|
+
`private_key` (optional) string, 32 random byte signing key
|
50
|
+
|
51
|
+
Example
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
require 'jwt_nacl'
|
55
|
+
|
56
|
+
claims = {iss: "mike", exp: 1300819380, :"http://example.com/is_root" => false}
|
57
|
+
|
58
|
+
private_hex = "d2c5c54bc205266f12a8a21809aa2989536959f666a5d68710e6fab94674041a"
|
59
|
+
private_key = [private_hex].pack("H*")
|
60
|
+
|
61
|
+
public_hex = "1e10af4b79b8d005c8b4237161f1350844b2e6c1a8d6aa4817151c04a2751731"
|
62
|
+
public_key = [public_hex].pack("H*")
|
63
|
+
|
64
|
+
# Sign with an elliptical curve Ed25519 digital signature
|
65
|
+
jwt = JWT.sign(claims, private_key)
|
66
|
+
#=> {
|
67
|
+
jwt: "eyJhbGciOiJFZDI1NTE5IiwidHlwIjoiSldUIn0.eyJpc3MiOiJtaWtlIiwiZXhwIjoxMzAwODE5MzgwLCJodHRwOi8vZXhhbXBsZS5jb20vaXNfcm9vdCI6ZmFsc2V9.f2y6Sax9eK9M3JiFCt4ZfzzOL56SWNhydHpPPIoVkm21D3_bJq5DmFLgH8ee2OlzSlZMoq009jLSg6AC0mn4DA",
|
68
|
+
private_key: private_key,
|
69
|
+
public_key: public_key
|
70
|
+
}
|
71
|
+
```
|
72
|
+
|
73
|
+
### JWT.verify(jwt, public_key)
|
74
|
+
|
75
|
+
#### Returns a hash:
|
76
|
+
* \{claims: < JWT claims set >\}, if the digital signature is verified
|
77
|
+
* \{error: "invalid"\}, otherwise
|
78
|
+
|
79
|
+
`jwt` (required) is a JSON web token string
|
80
|
+
|
81
|
+
`public_key` (required) string, 32 byte verifying key
|
82
|
+
|
83
|
+
Example
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
require 'jwt_nacl'
|
87
|
+
|
88
|
+
jwt = "eyJhbGciOiJFZDI1NTE5IiwidHlwIjoiSldUIn0.eyJpc3MiOiJtaWtlIiwiZXhwIjoxMzAwODE5MzgwLCJodHRwOi8vZXhhbXBsZS5jb20vaXNfcm9vdCI6ZmFsc2V9.f2y6Sax9eK9M3JiFCt4ZfzzOL56SWNhydHpPPIoVkm21D3_bJq5DmFLgH8ee2OlzSlZMoq009jLSg6AC0mn4DA"
|
89
|
+
|
90
|
+
hex = "1e10af4b79b8d005c8b4237161f1350844b2e6c1a8d6aa4817151c04a2751731"
|
91
|
+
public_key = [hex].pack("H*")
|
92
|
+
|
93
|
+
# Verify with an elliptical curve Ed25519 public key
|
94
|
+
JWT.verify(jwt, public_key)
|
95
|
+
#=> {
|
96
|
+
claims: {iss: "mike", exp: 1300819380, :"http://example.com/is_root" => false}
|
97
|
+
}
|
98
|
+
```
|
99
|
+
|
100
|
+
### Supported encryption algorithm
|
101
|
+
Ed25519, Edwards-curve Digital Signature Algorithm (EdDSA) using Curve25519
|
102
|
+
|
103
|
+
### Supported Ruby versions
|
104
|
+
Ruby 2.2.6 and up
|
105
|
+
|
106
|
+
[eddsa]: https://en.wikipedia.org/wiki/EdDSA
|
107
|
+
[ed25519]: http://ed25519.cr.yp.to/
|
108
|
+
[nacl]: http://nacl.cr.yp.to/
|
109
|
+
[bernstein]: https://en.wikipedia.org/wiki/Daniel_J._Bernstein
|
110
|
+
|
111
|
+
[rbnacl]: https://github.com/cryptosphere/rbnacl/blob/master/README.md
|
112
|
+
[travis]: https://travis-ci.org/garyf/jwt_nacl
|
113
|
+
[ci_img]: https://travis-ci.org/garyf/jwt_nacl.svg?branch=master
|
114
|
+
[yard_docs]: http://www.rubydoc.info/github/garyf/jwt_nacl
|
115
|
+
[yd_img]: http://img.shields.io/badge/yard-docs-blue.svg
|
116
|
+
[code_climate]: https://codeclimate.com/github/garyf/jwt_nacl
|
117
|
+
[cc_img]: https://codeclimate.com/github/garyf/jwt_nacl/badges/gpa.svg
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "jwt_nacl"
|
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
|
data/bin/setup
ADDED
data/jwt_nacl.gemspec
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'jwt_nacl/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "jwt_nacl"
|
8
|
+
spec.version = JwtNacl::VERSION
|
9
|
+
spec.authors = ["Gary Fleshman"]
|
10
|
+
spec.email = ["gfleshman@newforge-tech.com"]
|
11
|
+
|
12
|
+
spec.summary = "JSON Web Token (JWT) for Ruby using NaCl cryptography"
|
13
|
+
spec.description = <<-HERE
|
14
|
+
A Ruby JSON Web Token implementation using NaCl Ed25519 digital signatures from the
|
15
|
+
state-of-the-art networking and cryptography library by Daniel J. Bernstein.
|
16
|
+
HERE
|
17
|
+
spec.homepage = "https://github.com/garyf/jwt_nacl"
|
18
|
+
spec.license = "MIT"
|
19
|
+
|
20
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
21
|
+
f.match(%r{^(test|spec|features)/})
|
22
|
+
end
|
23
|
+
spec.bindir = "exe"
|
24
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
25
|
+
spec.require_paths = ["lib"]
|
26
|
+
spec.required_ruby_version = ">= 2.2.6"
|
27
|
+
|
28
|
+
spec.add_runtime_dependency "rbnacl", "~> 4.0"
|
29
|
+
spec.add_runtime_dependency "rbnacl-libsodium", "~> 1.0"
|
30
|
+
|
31
|
+
spec.add_development_dependency "bundler", "~> 1.13"
|
32
|
+
spec.add_development_dependency "minitest", "~> 5.0"
|
33
|
+
spec.add_development_dependency "minitest-reporters", "~> 1.1"
|
34
|
+
spec.add_development_dependency "rake", "~> 12.0"
|
35
|
+
spec.add_development_dependency "simplecov", "~> 0.13"
|
36
|
+
spec.add_development_dependency "yard", "~> 0.9"
|
37
|
+
spec.add_development_dependency "wwtd", "~> 1.3"
|
38
|
+
end
|
data/lib/jwt_nacl.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require "jwt_nacl/jws"
|
2
|
+
require "jwt_nacl/util"
|
3
|
+
|
4
|
+
# Encode claims for transmission as a JSON object that is used as the payload
|
5
|
+
# of a JSON Web Signature (JWS) structure, enabling the claims
|
6
|
+
# to be integrity protected with a signature for later verification
|
7
|
+
# @see http://tools.ietf.org/html/rfc7519
|
8
|
+
module JwtNacl
|
9
|
+
module_function
|
10
|
+
|
11
|
+
# @param claims [Hash] input for a digital signature computation
|
12
|
+
# @param private_key [String] 32 random bytes (optional)
|
13
|
+
# @return [Hash] a hash with a signed jwt, private_key, and public_key
|
14
|
+
# @example
|
15
|
+
# claims = {iss: "mike", exp: 1300819380, :"http://example.com/is_root" => false}
|
16
|
+
# private_hex = "d2c5c54bc205266f12a8a21809aa2989536959f666a5d68710e6fab94674041a"
|
17
|
+
# private_key = [private_hex].pack("H*")
|
18
|
+
# public_hex = "1e10af4b79b8d005c8b4237161f1350844b2e6c1a8d6aa4817151c04a2751731"
|
19
|
+
# public_key = [public_hex].pack("H*")
|
20
|
+
# JwtNacl.sign(claims, private_key)
|
21
|
+
# # => {jwt: "eyJhbGciOiJFZDI1NTE5IiwidHlwIjoiSldUIn0.eyJpc3MiOiJtaWtlIiwiZXhwIjoxMzAwODE5MzgwLCJodHRwOi8vZXhhbXBsZS5jb20vaXNfcm9vdCI6ZmFsc2V9.f2y6Sax9eK9M3JiFCt4ZfzzOL56SWNhydHpPPIoVkm21D3_bJq5DmFLgH8ee2OlzSlZMoq009jLSg6AC0mn4DA", private_key: private_key, public_key: public_key}
|
22
|
+
def sign(claims, private_key = nil)
|
23
|
+
Jws.sign(validated_payload(claims), private_key)
|
24
|
+
end
|
25
|
+
|
26
|
+
# @param jwt [String] a JSON Web Token
|
27
|
+
# @param public_key [String] 32 byte verifying key
|
28
|
+
# @return [Hash] +{claims: < the jwt claims set hash >}+ if the jwt verifies,
|
29
|
+
# or +{error: "invalid"}+ otherwise
|
30
|
+
# @example
|
31
|
+
# jwt = "eyJhbGciOiJFZDI1NTE5IiwidHlwIjoiSldUIn0.eyJpc3MiOiJtaWtlIiwiZXhwIjoxMzAwODE5MzgwLCJodHRwOi8vZXhhbXBsZS5jb20vaXNfcm9vdCI6ZmFsc2V9.f2y6Sax9eK9M3JiFCt4ZfzzOL56SWNhydHpPPIoVkm21D3_bJq5DmFLgH8ee2OlzSlZMoq009jLSg6AC0mn4DA"
|
32
|
+
# hex = "1e10af4b79b8d005c8b4237161f1350844b2e6c1a8d6aa4817151c04a2751731"
|
33
|
+
# public_key = [hex].pack("H*")
|
34
|
+
# JwtNacl.verify(jwt, public_key)
|
35
|
+
# # => {claims: {iss: "mike", exp: 1300819380, :"http://example.com/is_root" => false}}
|
36
|
+
def verify(jwt, public_key)
|
37
|
+
verified_claims(Jws.verify(jwt, public_key))
|
38
|
+
end
|
39
|
+
|
40
|
+
def validated_payload(claims)
|
41
|
+
raise("invalid claims") if !claims || claims.empty? || !claims.is_a?(Hash)
|
42
|
+
claims.to_json
|
43
|
+
end
|
44
|
+
|
45
|
+
def verified_claims(hsh)
|
46
|
+
return {error: "invalid"} if hsh[:error]
|
47
|
+
{claims: decoded_claims(hsh[:jwt].split(".")[1])}
|
48
|
+
end
|
49
|
+
|
50
|
+
def decoded_claims(str)
|
51
|
+
Util.symbolize_keys(
|
52
|
+
JSON.parse(
|
53
|
+
Base64Url.decode(str)
|
54
|
+
)
|
55
|
+
)
|
56
|
+
end
|
57
|
+
|
58
|
+
private_class_method :validated_payload,
|
59
|
+
:verified_claims,
|
60
|
+
:decoded_claims
|
61
|
+
end
|
62
|
+
|
63
|
+
# alias
|
64
|
+
JWT = JwtNacl
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require "base64"
|
2
|
+
|
3
|
+
module JwtNacl
|
4
|
+
# Provide base64url encoding and decoding functions without padding, based upon standard
|
5
|
+
# base64 encoding and decoding functions that do use padding
|
6
|
+
# @see http://tools.ietf.org/html/rfc7515#appendix-C
|
7
|
+
module Base64Url
|
8
|
+
module_function
|
9
|
+
|
10
|
+
# @param str [String]
|
11
|
+
# @return [String] a urlsafe_encode64 string with all trailing "=" padding removed
|
12
|
+
# @example
|
13
|
+
# Base64Url.encode("foo")
|
14
|
+
# # => "Zm9v"
|
15
|
+
def encode(str)
|
16
|
+
base64_padding_removed(Base64.urlsafe_encode64(str))
|
17
|
+
end
|
18
|
+
|
19
|
+
# @param str [String] encoded as url_encode64
|
20
|
+
# @return [String] with trailing "=" padding added before decoding
|
21
|
+
# @example
|
22
|
+
# Base64Url.decode("YmFy")
|
23
|
+
# # => "bar"
|
24
|
+
def decode(str)
|
25
|
+
Base64.urlsafe_decode64(base64_padding_added(str))
|
26
|
+
end
|
27
|
+
|
28
|
+
def base64_padding_removed(encoded)
|
29
|
+
encoded.gsub(/[=]/, "")
|
30
|
+
end
|
31
|
+
|
32
|
+
def base64_padding_added(str)
|
33
|
+
mod = str.length % 4
|
34
|
+
return str if mod == 0
|
35
|
+
raise("Invalid base64 string") if mod == 1
|
36
|
+
"#{str}#{"=" * (4 - mod)}"
|
37
|
+
end
|
38
|
+
|
39
|
+
private_class_method :base64_padding_removed,
|
40
|
+
:base64_padding_added
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require "rbnacl/libsodium"
|
2
|
+
require "rbnacl"
|
3
|
+
|
4
|
+
module JwtNacl
|
5
|
+
# Sign or verify JSON Web Signature (JWS) signing input using Edwards-curve
|
6
|
+
# Digital Signature Algorithm (EdDSA) curve Ed25519
|
7
|
+
# @see https://ed25519.cr.yp.to/
|
8
|
+
module Ed25519
|
9
|
+
module_function
|
10
|
+
|
11
|
+
# @param message [String] input payload for a digital signature computation
|
12
|
+
# @param seed [String] 32 random bytes (optional)
|
13
|
+
# @return [Hash] a hash with the private_key, public_key, and signature
|
14
|
+
# @example
|
15
|
+
# Ed25519.sign(message, seed)
|
16
|
+
# # => {private_key: private_key, public_key: public_key, signature: signature}
|
17
|
+
def sign(message, seed = nil)
|
18
|
+
signing_key = seed ? RbNaCl::SigningKey.new(seed) : RbNaCl::SigningKey.generate
|
19
|
+
{
|
20
|
+
private_key: signing_key.to_bytes,
|
21
|
+
public_key: signing_key.verify_key.to_bytes,
|
22
|
+
signature: signing_key.sign(message)
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
# @param public_key [String] 32 byte key used to authenticate a digital signature
|
27
|
+
# @param signature [String] alleged signature to be checked
|
28
|
+
# @param message [String] message to be authenticated
|
29
|
+
# @return [String, Boolean] verified message or false
|
30
|
+
# @example
|
31
|
+
# Ed25519.verify(public_key, signature, message)
|
32
|
+
# # => message
|
33
|
+
def verify(public_key, signature, message)
|
34
|
+
verify_key(public_key, signature, message)
|
35
|
+
message
|
36
|
+
rescue
|
37
|
+
false
|
38
|
+
end
|
39
|
+
|
40
|
+
def verify_key(public_key, signature, message)
|
41
|
+
RbNaCl::VerifyKey.new(public_key)
|
42
|
+
.verify(signature, message)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/jwt_nacl/jws.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require "jwt_nacl/base64_url"
|
2
|
+
require "jwt_nacl/ed25519"
|
3
|
+
|
4
|
+
module JwtNacl
|
5
|
+
# Represent content to be secured with digital signatures
|
6
|
+
# @see http://tools.ietf.org/html/rfc7515
|
7
|
+
module Jws
|
8
|
+
HEADER = {
|
9
|
+
alg: "Ed25519",
|
10
|
+
typ: "JWT"
|
11
|
+
}
|
12
|
+
JWS_PARTS = 3
|
13
|
+
|
14
|
+
module_function
|
15
|
+
|
16
|
+
# @param payload [String] input for a digital signature computation
|
17
|
+
# @param private_key [String] 32 random bytes (optional)
|
18
|
+
# @return [Hash] a hash with a signed jwt, private_key, and public_key
|
19
|
+
# @example
|
20
|
+
# Jws.sign(payload, private_key)
|
21
|
+
# # => {jwt: jwt, private_key: private_key, public_key: public_key}
|
22
|
+
def sign(payload, private_key = nil)
|
23
|
+
signing_input = encode_input(payload)
|
24
|
+
hsh = Ed25519.sign(signing_input, private_key)
|
25
|
+
{
|
26
|
+
jwt: "#{signing_input}.#{Base64Url.encode(hsh[:signature])}",
|
27
|
+
private_key: hsh[:private_key],
|
28
|
+
public_key: hsh[:public_key]
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
# @param jwt [String] input to be authenticated
|
33
|
+
# @param public_key [String] 32 byte key used to authenticate a digital signature
|
34
|
+
# @return [Hash] a hash with a verified jwt (e.g. jwt: jwt) or not verified (e.g. error: "invalid")
|
35
|
+
# @example
|
36
|
+
# Jws.verify(jwt, public_key)
|
37
|
+
# # => {jwt: jwt}
|
38
|
+
def verify(jwt, public_key)
|
39
|
+
verified?(jwt, public_key) ? {jwt: jwt} : {error: "invalid"}
|
40
|
+
end
|
41
|
+
|
42
|
+
def encode_input(payload)
|
43
|
+
"#{Base64Url.encode(HEADER.to_json)}.#{Base64Url.encode(payload)}"
|
44
|
+
end
|
45
|
+
|
46
|
+
def verified?(jwt, public_key)
|
47
|
+
ary = jwt.split(".")
|
48
|
+
return unless ary.length == JWS_PARTS
|
49
|
+
signature = Base64Url.decode(ary[2])
|
50
|
+
message = "#{ary[0]}.#{ary[1]}"
|
51
|
+
|
52
|
+
Ed25519.verify(public_key, signature, message)
|
53
|
+
end
|
54
|
+
|
55
|
+
private_class_method :encode_input,
|
56
|
+
:verified?
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require "rbnacl/libsodium"
|
2
|
+
require "rbnacl"
|
3
|
+
|
4
|
+
module JwtNacl
|
5
|
+
# Utility methods
|
6
|
+
module Util
|
7
|
+
ED25519_SEEDBYTES = 32
|
8
|
+
|
9
|
+
module_function
|
10
|
+
|
11
|
+
def ed25519_random_seed
|
12
|
+
RbNaCl::Random.random_bytes(ED25519_SEEDBYTES)
|
13
|
+
end
|
14
|
+
|
15
|
+
# @param hsh [Hash]
|
16
|
+
# @return [Hash] a new hash with all keys converted to symbols,
|
17
|
+
# provided that they respond to .to_sym
|
18
|
+
# @example
|
19
|
+
# Util.symbolize_keys({"a" => 0, "b" => "2", c: "3"})
|
20
|
+
# # => {a: 0, b: "2", c: "3"}
|
21
|
+
# @see cf. rails activesupport/lib/active_support/core_ext/hash/keys.rb
|
22
|
+
def symbolize_keys(hsh)
|
23
|
+
transform_keys(hsh) { |key| key.to_sym rescue key }
|
24
|
+
end
|
25
|
+
|
26
|
+
def transform_keys(hsh)
|
27
|
+
result = Hash.new
|
28
|
+
hsh.keys.each { |k| result[yield(k)] = hsh[k] }
|
29
|
+
result
|
30
|
+
end
|
31
|
+
|
32
|
+
private_class_method :transform_keys
|
33
|
+
end
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,189 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jwt_nacl
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Gary Fleshman
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-01-29 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rbnacl
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '4.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rbnacl-libsodium
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.13'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.13'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '5.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '5.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: minitest-reporters
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.1'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.1'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rake
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '12.0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '12.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: simplecov
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0.13'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0.13'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: yard
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0.9'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0.9'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: wwtd
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '1.3'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '1.3'
|
139
|
+
description: |2
|
140
|
+
A Ruby JSON Web Token implementation using NaCl Ed25519 digital signatures from the
|
141
|
+
state-of-the-art networking and cryptography library by Daniel J. Bernstein.
|
142
|
+
email:
|
143
|
+
- gfleshman@newforge-tech.com
|
144
|
+
executables: []
|
145
|
+
extensions: []
|
146
|
+
extra_rdoc_files: []
|
147
|
+
files:
|
148
|
+
- ".gitignore"
|
149
|
+
- ".travis.yml"
|
150
|
+
- CHANGELOG.md
|
151
|
+
- Gemfile
|
152
|
+
- Gemfile.lock
|
153
|
+
- LICENSE.md
|
154
|
+
- README.md
|
155
|
+
- Rakefile
|
156
|
+
- bin/console
|
157
|
+
- bin/setup
|
158
|
+
- jwt_nacl.gemspec
|
159
|
+
- lib/jwt_nacl.rb
|
160
|
+
- lib/jwt_nacl/base64_url.rb
|
161
|
+
- lib/jwt_nacl/ed25519.rb
|
162
|
+
- lib/jwt_nacl/jws.rb
|
163
|
+
- lib/jwt_nacl/util.rb
|
164
|
+
- lib/jwt_nacl/version.rb
|
165
|
+
homepage: https://github.com/garyf/jwt_nacl
|
166
|
+
licenses:
|
167
|
+
- MIT
|
168
|
+
metadata: {}
|
169
|
+
post_install_message:
|
170
|
+
rdoc_options: []
|
171
|
+
require_paths:
|
172
|
+
- lib
|
173
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
174
|
+
requirements:
|
175
|
+
- - ">="
|
176
|
+
- !ruby/object:Gem::Version
|
177
|
+
version: 2.2.6
|
178
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
179
|
+
requirements:
|
180
|
+
- - ">="
|
181
|
+
- !ruby/object:Gem::Version
|
182
|
+
version: '0'
|
183
|
+
requirements: []
|
184
|
+
rubyforge_project:
|
185
|
+
rubygems_version: 2.6.8
|
186
|
+
signing_key:
|
187
|
+
specification_version: 4
|
188
|
+
summary: JSON Web Token (JWT) for Ruby using NaCl cryptography
|
189
|
+
test_files: []
|