json-canonicalization 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 75fbded244fc0096ae6a46a90730fbf8a3fe1913d9a58fe60ec9fc91fb238522
4
+ data.tar.gz: 817b4072f362414762a9cb19eec0551dab9c4290bc8720640faedb050ff64b75
5
+ SHA512:
6
+ metadata.gz: 5b8cf4b810ce1ca14b30232cd172d9bb3350b9d0aaaefd7c02cacad1fdb47c87ec7f88091b51c86621319bbeb59df53be34dcb81743b8a70307a90678a19862e
7
+ data.tar.gz: fc41d6154f7fb136396028f6c03ca3bfbb6e0ce65a676990773ad3361eef48c868990196b63ba790a57e537e12096ac8997036eefc7a7ab1773a94f4672fafcc
data/AUTHORS ADDED
@@ -0,0 +1 @@
1
+ * Gregg Kellogg <gregg@greggkellogg.net>
data/LICENSE ADDED
@@ -0,0 +1,24 @@
1
+ This is free and unencumbered software released into the public domain.
2
+
3
+ Anyone is free to copy, modify, publish, use, compile, sell, or
4
+ distribute this software, either in source code form or as a compiled
5
+ binary, for any purpose, commercial or non-commercial, and by any
6
+ means.
7
+
8
+ In jurisdictions that recognize copyright laws, the author or authors
9
+ of this software dedicate any and all copyright interest in the
10
+ software to the public domain. We make this dedication for the benefit
11
+ of the public at large and to the detriment of our heirs and
12
+ successors. We intend this dedication to be an overt act of
13
+ relinquishment in perpetuity of all present and future rights to this
14
+ software under copyright law.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ For more information, please refer to <http://unlicense.org>
data/README.md ADDED
@@ -0,0 +1,92 @@
1
+ # json-canonicalization
2
+ An implementation of the JSON Canonicalization Scheme for Ruby
3
+
4
+ Implements version 5 of [draft-rundgren-json-canonicalization-scheme-05](https://tools.ietf.org/html/draft-rundgren-json-canonicalization-scheme-05#page-5).
5
+
6
+ [![Gem Version](https://badge.fury.io/rb/json-canonicalization.png)](http://badge.fury.io/rb/json-canonicalization)
7
+ [![Build Status](https://travis-ci.org/dryruby/json-canonicalization.png?branch=master)](http://travis-ci.org/dryruby/json-canonicalization)
8
+ [![Coverage Status](https://coveralls.io/repos/dryruby/json-canonicalization/badge.svg)](https://coveralls.io/r/dryruby/json-canonicalization)
9
+
10
+ # Description
11
+
12
+ Cryptographic operations like hashing and signing depend on that the target
13
+ data does not change during serialization, transport, or parsing.
14
+ By applying the rules defined by JCS (JSON Canonicalization Scheme),
15
+ data provided in the JSON [[RFC8259](https://tools.ietf.org/html/rfc8259)]
16
+ format can be exchanged "as is", while still being subject to secure cryptographic operations.
17
+ JCS achieves this by building on the serialization formats for JSON
18
+ primitives as defined by ECMAScript [[ES6](https://www.ecma-international.org/ecma-262/6.0/index.html)],
19
+ constraining JSON data to the<br>I-JSON [[RFC7493](https://tools.ietf.org/html//rfc7493)] subset,
20
+ and through a platform independent property sorting scheme.
21
+
22
+ Working document: https://cyberphone.github.io/ietf-json-canon<br>
23
+ Published IETF Draft: https://tools.ietf.org/html/draft-rundgren-json-canonicalization-scheme-05
24
+
25
+ The JSON Canonicalization Scheme concept in a nutshell:
26
+ - Serialization of primitive JSON data types using methods compatible with ECMAScript's `JSON.stringify()`
27
+ - Lexicographic sorting of JSON `Object` properties in a *recursive* process
28
+ - JSON `Array` data is also subject to canonicalization, *but element order remains untouched*
29
+
30
+ ### Sample Input:
31
+ ```code
32
+ {
33
+ "numbers": [333333333.33333329, 1E30, 4.50, 2e-3, 0.000000000000000000000000001],
34
+ "string": "\u20ac$\u000F\u000aA'\u0042\u0022\u005c\\\"\/",
35
+ "literals": [null, true, false]
36
+ }
37
+ ```
38
+ ### Expected Output:
39
+ ```code
40
+ {"literals":[null,true,false],"numbers":[333333333.3333333,1e+30,4.5,0.002,1e-27],"string":"€$\u000f\nA'B\"\\\\\"/"}
41
+ ```
42
+ ## Usage
43
+ 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`
44
+
45
+ ```ruby
46
+ data = {
47
+ "numbers" => [
48
+ 333333333.3333333,
49
+ 1.0e+30,
50
+ 4.5,
51
+ 0.002,
52
+ 1.0e-27
53
+ ],
54
+ "string" => "€$\u000F\nA'B\"\\\\\"/",
55
+ "literals" => [nil, true, false]
56
+ }
57
+
58
+ puts data.to_json_c14n
59
+ =>
60
+ ```
61
+
62
+ ## Documentation
63
+ Full documentation available on [RubyDoc](http://rubydoc.info/gems/json-canonicalization/file/README.md)
64
+
65
+ ### Principal Classes
66
+ * {JSON::Canonicalization}
67
+
68
+ ## Dependencies
69
+ * [Ruby](http://ruby-lang.org/) (>= 2.2.2)
70
+ * [JSON](https://rubygems.org/gems/json) (>= 2.1)
71
+
72
+ ## Author
73
+ * [Gregg Kellogg](http://github.com/gkellogg) - <http://kellogg-assoc.com/>
74
+
75
+ ## Contributing
76
+ * Do your best to adhere to the existing coding conventions and idioms.
77
+ * Don't use hard tabs, and don't leave trailing whitespace on any line.
78
+ * Do document every method you add using [YARD][] annotations. Read the
79
+ [tutorial][YARD-GS] or just look at the existing code for examples.
80
+ * Don't touch the `json-ld.gemspec`, `VERSION` or `AUTHORS` files. If you need to
81
+ change them, do so on your private branch only.
82
+ * Do feel free to add yourself to the `CREDITS` file and the corresponding
83
+ list in the the `README`. Alphabetical order applies.
84
+ * Do note that in order for us to merge any non-trivial changes (as a rule
85
+ of thumb, additions larger than about 15 lines of code), we need an
86
+ explicit [public domain dedication][PDD] on record from you.
87
+
88
+ ##License
89
+
90
+ This is free and unencumbered public domain software. For more information,
91
+ see <http://unlicense.org/> or the accompanying {file:UNLICENSE} file.
92
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+ module JSON::Canonicalization::VERSION
4
+ VERSION_FILE = File.join(File.expand_path(File.dirname(__FILE__)), "..", "..", "..", "VERSION")
5
+ MAJOR, MINOR, TINY, EXTRA = File.read(VERSION_FILE).chomp.split(".")
6
+
7
+ STRING = [MAJOR, MINOR, TINY, EXTRA].compact.join('.')
8
+
9
+ ##
10
+ # @return [String]
11
+ def self.to_s() STRING end
12
+
13
+ ##
14
+ # @return [String]
15
+ def self.to_str() STRING end
16
+
17
+ ##
18
+ # @return [Array(Integer, Integer, Integer)]
19
+ def self.to_a() STRING.split(".") end
20
+ end
@@ -0,0 +1,83 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+ $:.unshift(File.expand_path("../ld", __FILE__))
4
+ require 'json'
5
+
6
+ module JSON
7
+ ##
8
+ # `JSON::Canonicalization` generates canonical JSON output from Ruby objects
9
+ module Canonicalization
10
+ autoload :VERSION, 'json/ld/version'
11
+ end
12
+ end
13
+
14
+ class Object
15
+ # Default canonicalization output for Ruby objects
16
+ # @return [String]
17
+ def to_json_c14n
18
+ self.to_json
19
+ end
20
+ end
21
+
22
+ class Array
23
+ def to_json_c14n
24
+ '[' + self.map(&:to_json_c14n).join(',') + ']'
25
+ end
26
+ end
27
+
28
+ class Numeric
29
+ def to_json_c14n
30
+ raise RangeError if self.is_a?(Float) && (self.nan? || self.infinite?)
31
+ return "0" if self.zero?
32
+ num = self
33
+ if num < 0
34
+ num, sign = -num, '-'
35
+ end
36
+ native_rep = "%.15E" % num
37
+ decimal, exponential = native_rep.split('E')
38
+ exp_val = exponential.to_i
39
+ exponential = exp_val > 0 ? ('+' + exp_val.to_s) : exp_val.to_s
40
+
41
+ integral, fractional = decimal.split('.')
42
+ fractional = fractional.sub(/0+$/, '') # Remove trailing zeros
43
+
44
+ if exp_val > 0 && exp_val < 21
45
+ while exp_val > 0
46
+ integral += fractional.to_s[0] || '0'
47
+ fractional = fractional.to_s[1..-1]
48
+ exp_val -= 1
49
+ end
50
+ exponential = nil
51
+ elsif exp_val == 0
52
+ exponential = nil
53
+ elsif exp_val < 0 && exp_val > -7
54
+ # Small numbers are shown as 0.etc with e-6 as lower limit
55
+ fractional, integral, exponential = integral + fractional.to_s, '0', nil
56
+ fractional = ("0" * (-exp_val - 1)) + fractional
57
+ end
58
+
59
+ fractional = nil if fractional.to_s.empty?
60
+ sign.to_s + integral + (fractional ? ".#{fractional}" : '') + (exponential ? "e#{exponential}" : '')
61
+ end
62
+ end
63
+
64
+ class Hash
65
+ # Output JSON with keys sorted lexicographically
66
+ # @return [String]
67
+ def to_json_c14n
68
+ "{" + self.
69
+ keys.
70
+ sort_by {|k| k.encode(Encoding::UTF_16)}.
71
+ map {|k| k.to_json_c14n + ':' + self[k].to_json_c14n}
72
+ .join(',') +
73
+ '}'
74
+ end
75
+ end
76
+
77
+ class String
78
+ # Output JSON with control characters escaped
79
+ # @return [String]
80
+ def to_json_c14n
81
+ self.to_json
82
+ end
83
+ end
data/spec/c14n_spec.rb ADDED
@@ -0,0 +1,11 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe "conversions" do
4
+ Dir.glob(File.expand_path("../input/*.json", __FILE__)).each do |input|
5
+ it "converts #{input.split('/').last}" do
6
+ expected = File.read(input.sub('input', 'output'))
7
+ data = JSON.parse(File.read(input))
8
+ expect(data.to_json_c14n).to eq expected
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,8 @@
1
+ [
2
+ 56,
3
+ {
4
+ "d": true,
5
+ "10": null,
6
+ "1": [ ]
7
+ }
8
+ ]
@@ -0,0 +1,6 @@
1
+ {
2
+ "peach": "This sorting order",
3
+ "péché": "is wrong according to French",
4
+ "pêche": "but canonicalization MUST",
5
+ "sin": "ignore locale"
6
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "1": {"f": {"f": "hi","F": 5} ,"\n": 56.0},
3
+ "10": { },
4
+ "": "empty",
5
+ "a": { },
6
+ "111": [ {"e": "yes","E": "no" } ],
7
+ "A": { }
8
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "Unnormalized Unicode":"A\u030a"
3
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "numbers": [333333333.33333329, 1E30, 4.50, 2e-3, 0.000000000000000000000000001],
3
+ "string": "\u20ac$\u000F\u000aA'\u0042\u0022\u005c\\\"\/",
4
+ "literals": [null, true, false]
5
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "\u20ac": "Euro Sign",
3
+ "\r": "Carriage Return",
4
+ "\u000a": "Newline",
5
+ "1": "One",
6
+ "\u0080": "Control\u007f",
7
+ "\ud83d\ude02": "Smiley",
8
+ "\u00f6": "Latin Small Letter O With Diaeresis",
9
+ "\ufb33": "Hebrew Letter Dalet With Dagesh",
10
+ "</script>": "Browser Challenge"
11
+ }
@@ -0,0 +1,37 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe "conversions" do
4
+ {
5
+ -1/0.0 => RangeError,
6
+ -9007199254740992 => '-9007199254740992',
7
+ 0 => '0',
8
+ 0.000001 => '0.000001',
9
+ 0/0.0 => RangeError,
10
+ 1/0.0 => RangeError,
11
+ 1e+21 => '1e+21',
12
+ 9.999999999999997e+22 => '9.999999999999997e+22',
13
+ 9.999999999999997e-7 => '9.999999999999997e-7',
14
+ 9007199254740992 => '9007199254740992',
15
+ 9007199254740994 => '9007199254740994',
16
+ 9007199254740996 => '9007199254740996',
17
+ 999999999999999700000 => '999999999999999700000',
18
+ 999999999999999900000 => '999999999999999900000',
19
+ # -5e-324 => '-5e-324', # Outside Ruby Range
20
+ # 1.0000000000000001e+23 => '1.0000000000000001e+23', # Outside Ruby Range
21
+ # 295147905179352830000 => '295147905179352830000', # Outside Ruby Range
22
+ #-1.7976931348623157e+308 => '-1.7976931348623157e+308', # Outside Ruby Range
23
+ #1.7976931348623157e+308 => '1.7976931348623157e+308', # Outside Ruby Range
24
+ #1e+23 => '1e+23', # Outside Ruby
25
+ #5e-324 => '5e-324', # Outside Ruby Range
26
+ }.each do |data, expected|
27
+ if expected.is_a?(String)
28
+ it "converts #{data} to #{expected}" do
29
+ expect(data.to_json_c14n).to eq expected
30
+ end
31
+ else
32
+ it "raises #{expected} for #{data}" do
33
+ expect {data.to_json_c14n}.to raise_error(expected)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1 @@
1
+ [56,{"1":[],"10":null,"d":true}]
@@ -0,0 +1 @@
1
+ {"peach":"This sorting order","péché":"is wrong according to French","pêche":"but canonicalization MUST","sin":"ignore locale"}
@@ -0,0 +1 @@
1
+ {"":"empty","1":{"\n":56,"f":{"F":5,"f":"hi"}},"10":{},"111":[{"E":"no","e":"yes"}],"A":{},"a":{}}
@@ -0,0 +1 @@
1
+ {"Unnormalized Unicode":"Å"}
@@ -0,0 +1 @@
1
+ {"literals":[null,true,false],"numbers":[333333333.3333333,1e+30,4.5,0.002,1e-27],"string":"€$\u000f\nA'B\"\\\\\"/"}
@@ -0,0 +1 @@
1
+ {"\n":"Newline","\r":"Carriage Return","1":"One","</script>":"Browser Challenge","€":"Control","ö":"Latin Small Letter O With Diaeresis","€":"Euro Sign","😂":"Smiley","דּ":"Hebrew Letter Dalet With Dagesh"}
@@ -0,0 +1,24 @@
1
+ $:.unshift(File.join("../../lib", __FILE__))
2
+
3
+ require "bundler/setup"
4
+ require 'rspec'
5
+
6
+ begin
7
+ require 'simplecov'
8
+ require 'coveralls' unless ENV['NOCOVERALLS']
9
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([
10
+ SimpleCov::Formatter::HTMLFormatter,
11
+ (Coveralls::SimpleCov::Formatter unless ENV['NOCOVERALLS'])
12
+ ])
13
+ SimpleCov.start do
14
+ add_filter "/spec/"
15
+ end
16
+ rescue LoadError
17
+ end
18
+
19
+ require 'json/canonicalization'
20
+
21
+ ::RSpec.configure do |c|
22
+ c.filter_run focus: true
23
+ c.run_all_when_everything_filtered = true
24
+ end
metadata ADDED
@@ -0,0 +1,106 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: json-canonicalization
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Gregg Kellogg
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-03-19 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: '3.8'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.8'
27
+ - !ruby/object:Gem::Dependency
28
+ name: yard
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.9'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.9'
41
+ description: JSON::Canonicalization generates canonical JSON output from Ruby objects.
42
+ email:
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files: []
46
+ files:
47
+ - AUTHORS
48
+ - LICENSE
49
+ - README.md
50
+ - VERSION
51
+ - lib/json/canonicalization.rb
52
+ - lib/json/canonicalization/version.rb
53
+ - spec/c14n_spec.rb
54
+ - spec/input/arrays.json
55
+ - spec/input/french.json
56
+ - spec/input/structures.json
57
+ - spec/input/unicode.json
58
+ - spec/input/values.json
59
+ - spec/input/wierd.json
60
+ - spec/number_spec.rb
61
+ - spec/output/arrays.json
62
+ - spec/output/french.json
63
+ - spec/output/structures.json
64
+ - spec/output/unicode.json
65
+ - spec/output/values.json
66
+ - spec/output/wierd.json
67
+ - spec/spec_helper.rb
68
+ homepage: http://github.com/dryruby/json-canonicalization
69
+ licenses:
70
+ - Unlicense
71
+ metadata: {}
72
+ post_install_message:
73
+ rdoc_options: []
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: 2.2.2
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ requirements: []
87
+ rubygems_version: 3.0.2
88
+ signing_key:
89
+ specification_version: 4
90
+ summary: JSON Canonicalization for Ruby.
91
+ test_files:
92
+ - spec/spec_helper.rb
93
+ - spec/c14n_spec.rb
94
+ - spec/number_spec.rb
95
+ - spec/input/french.json
96
+ - spec/input/arrays.json
97
+ - spec/input/structures.json
98
+ - spec/input/unicode.json
99
+ - spec/input/wierd.json
100
+ - spec/input/values.json
101
+ - spec/output/french.json
102
+ - spec/output/arrays.json
103
+ - spec/output/structures.json
104
+ - spec/output/unicode.json
105
+ - spec/output/wierd.json
106
+ - spec/output/values.json