Module: Clickwrap::CanonicalJson

Defined in:
lib/clickwrap/canonical_json.rb

Overview

Canonical JSON serialization, implementing the JSON Canonicalization Scheme (RFC 8785, https://www.rfc-editor.org/rfc/rfc8785).

Receipts are digested and verified by code that may be years newer than the code that wrote them, and by verifiers written in other languages. So the bytes have to be reproducible from the data alone: no Ruby object serialization, no YAML, no hash insertion order, no database column order, no locale-dependent number formatting.

The Clickwrap profile adds a few rules on top of RFC 8785, applied by the callers in ReceiptSchema rather than here:

* timestamps are UTC strings with exactly six fractional digits;
* digests are "<algorithm>:<lowercase hex>" strings;
* identifiers are strings, never numbers;
* a value that was never collected is an explicit state object, never
`null` and never a missing key that could mean either; and
* host extensions use keys prefixed `x_`.

Defined Under Namespace

Classes: SerializationError

Constant Summary collapse

MAX_EXACT_INTEGER =

Integers above this magnitude cannot survive a round trip through the IEEE 754 double that RFC 8785 assumes, so serializing one would produce bytes another verifier could not reproduce.

(2**53) - 1
ESCAPES =
{
  "\b" => "\\b",
  "\t" => "\\t",
  "\n" => "\\n",
  "\f" => "\\f",
  "\r" => "\\r",
  '"' => '\\"',
  "\\" => "\\\\"
}.freeze

Class Method Summary collapse

Class Method Details

.canonical?(json_text) ⇒ Boolean

True when json_text is already in canonical form.

Returns:

  • (Boolean)


60
61
62
63
64
# File 'lib/clickwrap/canonical_json.rb', line 60

def canonical?(json_text)
  canonicalize(json_text) == json_text
rescue JSON::ParserError, SerializationError
  false
end

.canonicalize(json_text) ⇒ Object

Parses JSON text and returns the canonical bytes for it. Useful for verifying a receipt that arrived as arbitrarily formatted JSON.



55
56
57
# File 'lib/clickwrap/canonical_json.rb', line 55

def canonicalize(json_text)
  generate(JSON.parse(json_text))
end

.generate(value) ⇒ Object Also known as: dump

Returns the canonical UTF-8 JSON bytes for value.



45
46
47
48
49
# File 'lib/clickwrap/canonical_json.rb', line 45

def generate(value)
  buffer = +""
  write(value, buffer)
  buffer.force_encoding(Encoding::UTF_8)
end