json-canonicalization 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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