-
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
26 changed files
with
419 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
language: ruby | ||
bundler_args: --without debug | ||
script: "bundle exec rspec spec" | ||
before_install: | ||
- 'gem update --system --conservative || (gem i "rubygems-update:~>2.7" --no-document && update_rubygems)' | ||
- 'gem update bundler --conservative' | ||
env: | ||
- CI=true | ||
rvm: | ||
- 2.2.2 | ||
- 2.3 | ||
- 2.4 | ||
- 2.5 | ||
- 2.6 | ||
- jruby | ||
- rbx-3 | ||
cache: bundler | ||
sudo: false | ||
matrix: | ||
allow_failures: | ||
- rvm: jruby | ||
- rvm: rbx-3 | ||
dist: trusty |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
* Gregg Kellogg <[email protected]> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
source "https://rubygems.org" | ||
gemspec | ||
|
||
group :development, :test do | ||
gem 'simplecov', require: false, platform: :mri | ||
gem 'coveralls', require: false, platform: :mri | ||
gem 'benchmark-ips' | ||
gem 'rake' | ||
end | ||
|
||
group :debug do | ||
gem "byebug", platforms: :mri | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,92 @@ | ||
# json-canonicalization | ||
An implementation of the JSON Canonicalization Scheme for Ruby | ||
|
||
Implements version 5 of [draft-rundgren-json-canonicalization-scheme-05](https://tools.ietf.org/html/draft-rundgren-json-canonicalization-scheme-05#page-5). | ||
|
||
[![Gem Version](https://badge.fury.io/rb/json-canonicalization.png)](http://badge.fury.io/rb/json-canonicalization) | ||
[![Build Status](https://travis-ci.org/dryruby/json-canonicalization.png?branch=master)](http://travis-ci.org/dryruby/json-canonicalization) | ||
[![Coverage Status](https://coveralls.io/repos/dryruby/json-canonicalization/badge.svg)](https://coveralls.io/r/dryruby/json-canonicalization) | ||
|
||
# Description | ||
|
||
Cryptographic operations like hashing and signing depend on that the target | ||
data does not change during serialization, transport, or parsing. | ||
By applying the rules defined by JCS (JSON Canonicalization Scheme), | ||
data provided in the JSON [[RFC8259](https://tools.ietf.org/html/rfc8259)] | ||
format can be exchanged "as is", while still being subject to secure cryptographic operations. | ||
JCS achieves this by building on the serialization formats for JSON | ||
primitives as defined by ECMAScript [[ES6](https://www.ecma-international.org/ecma-262/6.0/index.html)], | ||
constraining JSON data to the<br>I-JSON [[RFC7493](https://tools.ietf.org/html//rfc7493)] subset, | ||
and through a platform independent property sorting scheme. | ||
|
||
Working document: https://cyberphone.github.io/ietf-json-canon<br> | ||
Published IETF Draft: https://tools.ietf.org/html/draft-rundgren-json-canonicalization-scheme-05 | ||
|
||
The JSON Canonicalization Scheme concept in a nutshell: | ||
- Serialization of primitive JSON data types using methods compatible with ECMAScript's `JSON.stringify()` | ||
- Lexicographic sorting of JSON `Object` properties in a *recursive* process | ||
- JSON `Array` data is also subject to canonicalization, *but element order remains untouched* | ||
|
||
### Sample Input: | ||
```code | ||
{ | ||
"numbers": [333333333.33333329, 1E30, 4.50, 2e-3, 0.000000000000000000000000001], | ||
"string": "\u20ac$\u000F\u000aA'\u0042\u0022\u005c\\\"\/", | ||
"literals": [null, true, false] | ||
} | ||
``` | ||
### Expected Output: | ||
```code | ||
{"literals":[null,true,false],"numbers":[333333333.3333333,1e+30,4.5,0.002,1e-27],"string":"€$\u000f\nA'B\"\\\\\"/"} | ||
``` | ||
## Usage | ||
The library accepts Ruby input and generates canonical JSON via the `#to_json_c14n` method. This is based on the standard JSON gem's version of `#to_json` with overloads for `Hash`, `String` and `Numeric` | ||
|
||
```ruby | ||
data = { | ||
"numbers" => [ | ||
333333333.3333333, | ||
1.0e+30, | ||
4.5, | ||
0.002, | ||
1.0e-27 | ||
], | ||
"string" => "€$\u000F\nA'B\"\\\\\"/", | ||
"literals" => [nil, true, false] | ||
} | ||
|
||
puts data.to_json_c14n | ||
=> | ||
``` | ||
|
||
## Documentation | ||
Full documentation available on [RubyDoc](http://rubydoc.info/gems/json-canonicalization/file/README.md) | ||
|
||
### Principal Classes | ||
* {JSON::Canonicalization} | ||
|
||
## Dependencies | ||
* [Ruby](http://ruby-lang.org/) (>= 2.2.2) | ||
* [JSON](https://rubygems.org/gems/json) (>= 2.1) | ||
|
||
## Author | ||
* [Gregg Kellogg](http://github.com/gkellogg) - <http://kellogg-assoc.com/> | ||
|
||
## Contributing | ||
* Do your best to adhere to the existing coding conventions and idioms. | ||
* Don't use hard tabs, and don't leave trailing whitespace on any line. | ||
* Do document every method you add using [YARD][] annotations. Read the | ||
[tutorial][YARD-GS] or just look at the existing code for examples. | ||
* Don't touch the `json-ld.gemspec`, `VERSION` or `AUTHORS` files. If you need to | ||
change them, do so on your private branch only. | ||
* Do feel free to add yourself to the `CREDITS` file and the corresponding | ||
list in the the `README`. Alphabetical order applies. | ||
* Do note that in order for us to merge any non-trivial changes (as a rule | ||
of thumb, additions larger than about 15 lines of code), we need an | ||
explicit [public domain dedication][PDD] on record from you. | ||
|
||
##License | ||
|
||
This is free and unencumbered public domain software. For more information, | ||
see <http://unlicense.org/> or the accompanying {file:UNLICENSE} file. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
#!/usr/bin/env ruby | ||
$:.unshift(File.expand_path(File.join(File.dirname(__FILE__), 'lib'))) | ||
require 'rubygems' | ||
|
||
namespace :gem do | ||
desc "Build the json-canonicalization-#{File.read('VERSION').chomp}.gem file" | ||
task :build do | ||
sh "gem build json-canonicalization.gemspec && mv json-canonicalization-#{File.read('VERSION').chomp}.gem pkg/" | ||
end | ||
|
||
desc "Release the json-canonicalization-#{File.read('VERSION').chomp}.gem file" | ||
task :release do | ||
sh "gem push pkg/json-canonicalization-#{File.read('VERSION').chomp}.gem" | ||
end | ||
end | ||
|
||
desc 'Default: run specs.' | ||
task default: :spec | ||
task specs: :spec | ||
|
||
require 'rspec/core/rake_task' | ||
desc 'Run specifications' | ||
RSpec::Core::RakeTask.new do |spec| | ||
spec.rspec_opts = %w(--options spec/spec.opts) if File.exists?('spec/spec.opts') | ||
end | ||
|
||
desc "Run specifications for continuous integration" | ||
RSpec::Core::RakeTask.new("spec:ci") do |spec| | ||
spec.rspec_opts = %w(--options spec/spec.opts) if File.exists?('spec/spec.opts') | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
0.1.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"numbers": [333333333.33333329, 1E30, 4.50, | ||
2e-3, 0.000000000000000000000000001], | ||
"string": "\u20ac$\u000F\u000aA'\u0042\u0022\u005c\\\"\/", | ||
"literals": [null, true, false] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
#!/usr/bin/env ruby -rubygems | ||
# -*- encoding: utf-8 -*- | ||
|
||
Gem::Specification.new do |gem| | ||
gem.version = File.read('VERSION').chomp | ||
gem.date = File.mtime('VERSION').strftime('%Y-%m-%d') | ||
|
||
gem.name = "json-canonicalization" | ||
gem.homepage = "http://github.com/dryruby/json-canonicalization" | ||
gem.license = 'Unlicense' | ||
gem.summary = "JSON Canonicalization for Ruby." | ||
gem.description = "JSON::Canonicalization generates canonical JSON output from Ruby objects." | ||
|
||
gem.authors = ['Gregg Kellogg'] | ||
|
||
gem.platform = Gem::Platform::RUBY | ||
gem.files = %w(AUTHORS README.md LICENSE VERSION) + Dir.glob('lib/**/*.rb') | ||
gem.test_files = Dir.glob('spec/**/*.rb') + Dir.glob('spec/**/*.json') | ||
|
||
gem.required_ruby_version = '>= 2.2.2' | ||
gem.requirements = [] | ||
gem.add_development_dependency 'rspec', '~> 3.8' | ||
gem.add_development_dependency 'yard' , '~> 0.9' | ||
|
||
gem.post_install_message = nil | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
# -*- encoding: utf-8 -*- | ||
# frozen_string_literal: true | ||
$:.unshift(File.expand_path("../ld", __FILE__)) | ||
require 'json' | ||
|
||
module JSON | ||
## | ||
# `JSON::Canonicalization` generates canonical JSON output from Ruby objects | ||
module Canonicalization | ||
autoload :VERSION, 'json/ld/version' | ||
end | ||
end | ||
|
||
class Object | ||
# Default canonicalization output for Ruby objects | ||
# @return [String] | ||
def to_json_c14n | ||
self.to_json | ||
end | ||
end | ||
|
||
class Array | ||
def to_json_c14n | ||
'[' + self.map(&:to_json_c14n).join(',') + ']' | ||
end | ||
end | ||
|
||
class Numeric | ||
def to_json_c14n | ||
raise RangeError if self.is_a?(Float) && (self.nan? || self.infinite?) | ||
return "0" if self.zero? | ||
num = self | ||
if num < 0 | ||
num, sign = -num, '-' | ||
end | ||
native_rep = "%.15E" % num | ||
decimal, exponential = native_rep.split('E') | ||
exp_val = exponential.to_i | ||
exponential = exp_val > 0 ? ('+' + exp_val.to_s) : exp_val.to_s | ||
|
||
integral, fractional = decimal.split('.') | ||
fractional = fractional.sub(/0+$/, '') # Remove trailing zeros | ||
|
||
if exp_val > 0 && exp_val < 21 | ||
while exp_val > 0 | ||
integral += fractional.to_s[0] || '0' | ||
fractional = fractional.to_s[1..-1] | ||
exp_val -= 1 | ||
end | ||
exponential = nil | ||
elsif exp_val == 0 | ||
exponential = nil | ||
elsif exp_val < 0 && exp_val > -7 | ||
# Small numbers are shown as 0.etc with e-6 as lower limit | ||
fractional, integral, exponential = integral + fractional.to_s, '0', nil | ||
fractional = ("0" * (-exp_val - 1)) + fractional | ||
end | ||
|
||
fractional = nil if fractional.to_s.empty? | ||
sign.to_s + integral + (fractional ? ".#{fractional}" : '') + (exponential ? "e#{exponential}" : '') | ||
end | ||
end | ||
|
||
class Hash | ||
# Output JSON with keys sorted lexicographically | ||
# @return [String] | ||
def to_json_c14n | ||
"{" + self. | ||
keys. | ||
sort_by {|k| k.encode(Encoding::UTF_16)}. | ||
map {|k| k.to_json_c14n + ':' + self[k].to_json_c14n} | ||
.join(',') + | ||
'}' | ||
end | ||
end | ||
|
||
class String | ||
# Output JSON with control characters escaped | ||
# @return [String] | ||
def to_json_c14n | ||
self.to_json | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# -*- encoding: utf-8 -*- | ||
# frozen_string_literal: true | ||
module JSON::Canonicalization::VERSION | ||
VERSION_FILE = File.join(File.expand_path(File.dirname(__FILE__)), "..", "..", "..", "VERSION") | ||
MAJOR, MINOR, TINY, EXTRA = File.read(VERSION_FILE).chomp.split(".") | ||
|
||
STRING = [MAJOR, MINOR, TINY, EXTRA].compact.join('.') | ||
|
||
## | ||
# @return [String] | ||
def self.to_s() STRING end | ||
|
||
## | ||
# @return [String] | ||
def self.to_str() STRING end | ||
|
||
## | ||
# @return [Array(Integer, Integer, Integer)] | ||
def self.to_a() STRING.split(".") end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
require_relative 'spec_helper' | ||
|
||
describe "conversions" do | ||
Dir.glob(File.expand_path("../input/*.json", __FILE__)).each do |input| | ||
it "converts #{input.split('/').last}" do | ||
expected = File.read(input.sub('input', 'output')) | ||
data = JSON.parse(File.read(input)) | ||
expect(data.to_json_c14n).to eq expected | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
[ | ||
56, | ||
{ | ||
"d": true, | ||
"10": null, | ||
"1": [ ] | ||
} | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"peach": "This sorting order", | ||
"péché": "is wrong according to French", | ||
"pêche": "but canonicalization MUST", | ||
"sin": "ignore locale" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"1": {"f": {"f": "hi","F": 5} ,"\n": 56.0}, | ||
"10": { }, | ||
"": "empty", | ||
"a": { }, | ||
"111": [ {"e": "yes","E": "no" } ], | ||
"A": { } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"Unnormalized Unicode":"A\u030a" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"numbers": [333333333.33333329, 1E30, 4.50, 2e-3, 0.000000000000000000000000001], | ||
"string": "\u20ac$\u000F\u000aA'\u0042\u0022\u005c\\\"\/", | ||
"literals": [null, true, false] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
{ | ||
"\u20ac": "Euro Sign", | ||
"\r": "Carriage Return", | ||
"\u000a": "Newline", | ||
"1": "One", | ||
"\u0080": "Control\u007f", | ||
"\ud83d\ude02": "Smiley", | ||
"\u00f6": "Latin Small Letter O With Diaeresis", | ||
"\ufb33": "Hebrew Letter Dalet With Dagesh", | ||
"</script>": "Browser Challenge" | ||
} |
Oops, something went wrong.