sidetree 0.1.0 → 0.1.3

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.
@@ -11,6 +11,57 @@ module Sidetree
11
11
  @suffix = suffix
12
12
  end
13
13
 
14
+ # Generate create operation with new generate key.
15
+ # @param [String] method DID method defined in +Sidetree::Params::METHODS+.
16
+ # @return [Sidetree::OP::Create]
17
+ def self.generate(method: Sidetree::Params::METHODS[:ion])
18
+ recovery_key = Sidetree::Key.generate
19
+ update_key = Sidetree::Key.generate
20
+ signing_key = Sidetree::Key.generate(id: "signing-key")
21
+ document = Sidetree::Model::Document.new(public_keys: [signing_key])
22
+ did =
23
+ Sidetree::DID.create(
24
+ document,
25
+ update_key,
26
+ recovery_key,
27
+ method: method
28
+ )
29
+ did.create_op
30
+ end
31
+
32
+ # Parse create operation data from json string
33
+ # @param [String] create_data create operation data(json string).
34
+ # @return [Sidetree::OP::Create]
35
+ # @raise [Sidetree::Error]
36
+ def self.from_json(create_data)
37
+ begin
38
+ json = JSON.parse(create_data, symbolize_names: true)
39
+ json.each do |k, v|
40
+ case k
41
+ when :type, :suffixData, :delta
42
+ else
43
+ raise Sidetree::Error,
44
+ "Create operation missing or unknown property"
45
+ end
46
+ end
47
+ if json[:type] && json[:type] != Sidetree::OP::Type::CREATE
48
+ raise Sidetree::Error, "Create operation type incorrect"
49
+ end
50
+ suffix = Sidetree::Model::Suffix.from_object(json[:suffixData])
51
+ delta = nil
52
+ begin
53
+ # For compatibility with data pruning
54
+ delta = Sidetree::Model::Delta.from_object(json[:delta]) if json[
55
+ :delta
56
+ ]
57
+ rescue Sidetree::Error
58
+ end
59
+ Create.new(suffix, delta)
60
+ rescue JSON::ParserError
61
+ raise Sidetree::Error, "create_data not json"
62
+ end
63
+ end
64
+
14
65
  def type
15
66
  Sidetree::OP::Type::CREATE
16
67
  end
@@ -31,20 +82,20 @@ module Sidetree
31
82
  expected_base64 =
32
83
  Base64.urlsafe_encode64(json.to_json_c14n, padding: false)
33
84
  unless expected_base64 == base64_str
34
- raise Error, 'Initial state object and JCS string mismatch.'
85
+ raise Error, "Initial state object and JCS string mismatch."
35
86
  end
36
87
 
37
88
  Create.new(
38
- Sidetree::Model::Suffix.parse(json[:suffixData]),
39
- Sidetree::Model::Delta.parse(json[:delta])
89
+ Sidetree::Model::Suffix.from_object(json[:suffixData]),
90
+ Sidetree::Model::Delta.from_object(json[:delta])
40
91
  )
41
92
  rescue JSON::ParserError
42
- raise Error, 'Long form initial state should be encoded jcs.'
93
+ raise Error, "Long form initial state should be encoded jcs."
43
94
  end
44
95
  end
45
96
 
46
97
  def to_h
47
- { suffixData: suffix.to_h, delta: delta.to_h }
98
+ { suffixData: suffix.to_h, delta: delta&.to_h }
48
99
  end
49
100
 
50
101
  # Generate long_suffix for DID.
@@ -0,0 +1,82 @@
1
+ module Sidetree
2
+ module OP
3
+ # Deactivate operation class
4
+ # https://identity.foundation/sidetree/spec/#deactivate
5
+ class Deactivate < Base
6
+ attr_reader :did_suffix
7
+ attr_reader :signed_data
8
+ attr_reader :revealed_value
9
+
10
+ # Initialize
11
+ # @param [String] did_suffix
12
+ # @param [JSON::JWS] signed_data
13
+ # @param [String] revealed_value
14
+ def initialize(did_suffix, signed_data, revealed_value)
15
+ Sidetree::Validator.validate_encoded_multi_hash!(
16
+ did_suffix,
17
+ "#{type} didSuffix"
18
+ )
19
+ Sidetree::Validator.validate_encoded_multi_hash!(
20
+ revealed_value,
21
+ "#{type} revealValue"
22
+ )
23
+ @did_suffix = did_suffix
24
+ @signed_data = signed_data
25
+ @revealed_value = revealed_value
26
+ end
27
+
28
+ # Parse Deactivate operation data from json string
29
+ # @param [String] deactivate_data deactivate operation data(json string).
30
+ # @return [Sidetree::OP::Deactivate]
31
+ # @raise [Sidetree::Error]
32
+ def self.from_json(deactivate_data)
33
+ begin
34
+ json = JSON.parse(deactivate_data, symbolize_names: true)
35
+ jws, revealed_value, did_suffix = nil, nil, nil
36
+ json.each do |k, v|
37
+ case k
38
+ when :type
39
+ unless v == Sidetree::OP::Type::DEACTIVATE
40
+ raise Sidetree::Error, "Deactivate operation type incorrect"
41
+ end
42
+ when :didSuffix
43
+ did_suffix = v
44
+ when :revealValue
45
+ revealed_value = v
46
+ when :signedData
47
+ jws = Sidetree::Util::JWS.parse(v)
48
+ unless jws.keys.length == 2
49
+ raise Sidetree::Error,
50
+ "Deactivate operation signed data missing or unknown property"
51
+ end
52
+ Sidetree::Util::JWK.validate!(
53
+ Sidetree::Util::JWK.parse(jws["recoveryKey"])
54
+ )
55
+ else
56
+ raise Sidetree::Error,
57
+ "Unexpected property #{k.to_s} in deactivate operation"
58
+ end
59
+ end
60
+ if jws
61
+ Validator.validate_canonicalize_object_hash!(
62
+ jws["recoveryKey"],
63
+ revealed_value,
64
+ "Deactivate key"
65
+ )
66
+ end
67
+ unless did_suffix
68
+ raise Sidetree::Error, "The deactivate didSuffix must be a string"
69
+ end
70
+ Deactivate.new(did_suffix, jws, revealed_value)
71
+ rescue JSON::ParserError
72
+ raise Sidetree::Error, "deactivate_data not json"
73
+ rescue JSON::JWS::InvalidFormat
74
+ raise Sidetree::Error, "Invalid signedData"
75
+ end
76
+ end
77
+ def type
78
+ Sidetree::OP::Type::DEACTIVATE
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,19 @@
1
+ module Sidetree
2
+ module OP
3
+ # Recover operation class.
4
+ # https://identity.foundation/sidetree/spec/#recover
5
+ class Recover < Updatable
6
+ # Parse Recover operation data from json string
7
+ # @param [String] recover_data recover operation data(json string).
8
+ # @return [Sidetree::OP::Recover]
9
+ # @raise [Sidetree::Error]
10
+ def self.from_json(recover_data)
11
+ parse_json(recover_data, Sidetree::OP::Type::RECOVER)
12
+ end
13
+
14
+ def type
15
+ Sidetree::OP::Type::RECOVER
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,98 @@
1
+ module Sidetree
2
+ module OP
3
+ class Updatable < Base
4
+ attr_reader :did_suffix
5
+ attr_reader :delta
6
+ attr_reader :signed_data
7
+ attr_reader :revealed_value
8
+
9
+ # Initialize
10
+ # @param [String] did_suffix
11
+ # @param [Sidetree::Model::Delta] delta
12
+ # @param [JSON::JWS] signed_data
13
+ # @param [String] revealed_value
14
+ # @raise [Sidetree::Error]
15
+ def initialize(did_suffix, delta, signed_data, revealed_value)
16
+ Sidetree::Validator.validate_encoded_multi_hash!(
17
+ did_suffix,
18
+ "#{type} didSuffix"
19
+ )
20
+ Sidetree::Validator.validate_encoded_multi_hash!(
21
+ revealed_value,
22
+ "#{type} revealValue"
23
+ )
24
+ if signed_data
25
+ Validator.validate_canonicalize_object_hash!(
26
+ signed_data[key_name],
27
+ revealed_value,
28
+ type
29
+ )
30
+ end
31
+ @did_suffix = did_suffix
32
+ @delta = delta
33
+ @signed_data = signed_data
34
+ @revealed_value = revealed_value
35
+ end
36
+
37
+ def self.parse_json(data, type)
38
+ begin
39
+ json = JSON.parse(data, symbolize_names: true)
40
+ delta, jws, revealed_value, did_suffix = nil, nil, nil, nil
41
+ json.each do |k, v|
42
+ case k
43
+ when :type
44
+ unless v == type
45
+ raise Sidetree::Error,
46
+ "#{type.capitalize} operation type incorrect"
47
+ end
48
+ when :didSuffix
49
+ did_suffix = v
50
+ when :revealValue
51
+ revealed_value = v
52
+ when :signedData
53
+ jws = Sidetree::Util::JWS.parse(v)
54
+ unless jws.keys.length ==
55
+ (type == Sidetree::OP::Type::RECOVER ? 3 : 2)
56
+ raise Sidetree::Error,
57
+ "#{type.capitalize} operation signed data missing or unknown property"
58
+ end
59
+ key_name =
60
+ (
61
+ if type == Sidetree::OP::Type::RECOVER
62
+ "recoveryKey"
63
+ else
64
+ "updateKey"
65
+ end
66
+ )
67
+ Sidetree::Util::JWK.validate!(
68
+ Sidetree::Util::JWK.parse(jws[key_name])
69
+ )
70
+ when :delta
71
+ delta = Sidetree::Model::Delta.from_object(v)
72
+ else
73
+ raise Sidetree::Error,
74
+ "Unexpected property #{k.to_s} in #{type} operation"
75
+ end
76
+ end
77
+ unless did_suffix
78
+ raise Sidetree::Error, "The #{type} didSuffix must be a string"
79
+ end
80
+ Module.const_get("Sidetree::OP::#{type.capitalize}").new(
81
+ did_suffix,
82
+ delta,
83
+ jws,
84
+ revealed_value
85
+ )
86
+ rescue JSON::ParserError
87
+ raise Sidetree::Error, "data dose not json"
88
+ rescue JSON::JWS::InvalidFormat
89
+ raise Sidetree::Error, "Invalid signedData"
90
+ end
91
+ end
92
+
93
+ def key_name
94
+ type == Sidetree::OP::Type::UPDATE ? "updateKey" : "recoveryKey"
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,19 @@
1
+ module Sidetree
2
+ module OP
3
+ # Update operation class.
4
+ # https://identity.foundation/sidetree/spec/#update
5
+ class Update < Updatable
6
+ # Parse update operation data from json string
7
+ # @param [String] update_data update operation data(json string).
8
+ # @return [Sidetree::OP::Update]
9
+ # @raise [Sidetree::Error]
10
+ def self.from_json(update_data)
11
+ parse_json(update_data, Sidetree::OP::Type::UPDATE)
12
+ end
13
+
14
+ def type
15
+ Sidetree::OP::Type::UPDATE
16
+ end
17
+ end
18
+ end
19
+ end
data/lib/sidetree/op.rb CHANGED
@@ -1,28 +1,28 @@
1
1
  module Sidetree
2
2
  module OP
3
3
  module Type
4
- CREATE = 'create'
5
- UPDATE = 'update'
6
- RECOVER = 'recover'
7
- DEACTIVATE = 'deactivate'
4
+ CREATE = "create"
5
+ UPDATE = "update"
6
+ RECOVER = "recover"
7
+ DEACTIVATE = "deactivate"
8
8
  end
9
9
 
10
10
  # Sidetree patch actions. These are the valid values in the action property of a patch.
11
11
  module PatchAction
12
- REPLACE = 'replace'
13
- ADD_PUBLIC_KEYS = 'add-public-keys'
14
- REMOVE_PUBLIC_KEYS = 'remove-public-keys'
15
- ADD_SERVICES = 'add-services'
16
- REMOVE_SERVICES = 'remove-services'
12
+ REPLACE = "replace"
13
+ ADD_PUBLIC_KEYS = "add-public-keys"
14
+ REMOVE_PUBLIC_KEYS = "remove-public-keys"
15
+ ADD_SERVICES = "add-services"
16
+ REMOVE_SERVICES = "remove-services"
17
17
  end
18
18
 
19
19
  # DID Document public key purpose.
20
20
  module PublicKeyPurpose
21
- AUTHENTICATION = 'authentication'
22
- ASSERTION_METHOD = 'assertionMethod'
23
- CAPABILITY_INVOCATION = 'capabilityInvocation'
24
- CAPABILITY_DELEGATION = 'capabilityDelegation'
25
- KEY_AGREEMENT = 'keyAgreement'
21
+ AUTHENTICATION = "authentication"
22
+ ASSERTION_METHOD = "assertionMethod"
23
+ CAPABILITY_INVOCATION = "capabilityInvocation"
24
+ CAPABILITY_DELEGATION = "capabilityDelegation"
25
+ KEY_AGREEMENT = "keyAgreement"
26
26
 
27
27
  module_function
28
28
 
@@ -31,7 +31,11 @@ module Sidetree
31
31
  end
32
32
  end
33
33
 
34
- autoload :Base, 'sidetree/op/base'
35
- autoload :Create, 'sidetree/op/create'
34
+ autoload :Base, "sidetree/op/base"
35
+ autoload :Updatable, "sidetree/op/updatable"
36
+ autoload :Create, "sidetree/op/create"
37
+ autoload :Recover, "sidetree/op/recover"
38
+ autoload :Update, "sidetree/op/update"
39
+ autoload :Deactivate, "sidetree/op/deactivate"
36
40
  end
37
41
  end
@@ -0,0 +1,41 @@
1
+ module Sidetree
2
+ module Util
3
+ module AnchoredDataSerializer
4
+ DELIMITER = "."
5
+
6
+ module_function
7
+
8
+ # Serialize given data as Anchor String.
9
+ # @param [Integer] op_count Number of operations
10
+ # @param [String] uri Core Index File uri.
11
+ # @return [String] Anchor String
12
+ # @raise [Sidetree::Error]
13
+ def serialize(op_count, uri)
14
+ if op_count > Sidetree::Params::MAX_OPERATION_COUNT
15
+ raise Sidetree::Error, "Number of operations greater than max"
16
+ end
17
+ "#{op_count}#{DELIMITER}#{uri}"
18
+ end
19
+
20
+ # Deserializes the given string that is read from the blockchain into data.
21
+ # @param [String] anchor_str Anchor String
22
+ # @return [Array[Integer, String]]
23
+ # @raise [Sidetree::Error]
24
+ def deserialize(anchor_str)
25
+ data = anchor_str.split(DELIMITER)
26
+ raise Sidetree::Error, "Invalid anchor string" unless data.length == 2
27
+ unless data[0] =~ /^[1-9]\d*$/
28
+ raise Sidetree::Error,
29
+ "Number of operations in anchor string is not positive number"
30
+ end
31
+
32
+ count = data[0].to_i
33
+ if count > Sidetree::Params::MAX_OPERATION_COUNT
34
+ raise Sidetree::Error,
35
+ "Number of operations in anchor string greater than max"
36
+ end
37
+ [count, data[1]]
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,38 @@
1
+ require "zlib"
2
+
3
+ module Sidetree
4
+ module Util
5
+ module Compressor
6
+ # The estimated ratio/multiplier of decompressed Sidetree CAS file size compared against the compressed file size.
7
+ ESTIMATE_DECOMPRESSION_MULTIPLIER = 3
8
+
9
+ module_function
10
+
11
+ # Compresses teh data in gzip and return it as buffer.
12
+ # @param [String] data Data to be compressed.
13
+ # @return [String] compressed data.
14
+ def compress(data)
15
+ io = StringIO.new("w")
16
+ Zlib::GzipWriter.wrap(io) do |w|
17
+ w.mtime = 0
18
+ w.write data
19
+ end
20
+ io.string.force_encoding("binary")
21
+ end
22
+
23
+ # Decompresses +compressed+.
24
+ # @param [String] compressed compressed data.
25
+ # @return [String] decompressed data.
26
+ # @raise [Sidetree::Error] raise if data exceeds max_bytes size.
27
+ def decompress(compressed, max_bytes: nil)
28
+ if max_bytes && compressed.bytesize > max_bytes
29
+ raise Sidetree::Error, "Exceed maximum compressed chunk file size."
30
+ end
31
+ io = StringIO.new(compressed)
32
+ result = StringIO.new
33
+ Zlib::GzipReader.wrap(io) { |gz| result << gz.read }
34
+ result.string
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,47 @@
1
+ module Sidetree
2
+ module Util
3
+ module JWK
4
+ module_function
5
+
6
+ # Parse jwk hash object.
7
+ # The returned jwk does not include the kid.
8
+ # @param [Hash] params
9
+ # @return [JSON::JWK]
10
+ def parse(params)
11
+ jwk = JSON::JWK.new(params)
12
+ jwk.delete("kid")
13
+ jwk
14
+ end
15
+
16
+ # Validate jwk object as sidetree jwk.
17
+ # @param [JSON::JWK] jwk
18
+ # @raise [Sidetree::Error]
19
+ def validate!(jwk)
20
+ raise Sidetree::Error unless jwk.is_a?(JSON::JWK)
21
+ jwk.keys.each do |k|
22
+ unless %w[kty crv x y].include?(k)
23
+ raise Sidetree::Error, "JWK Es256k has unknown property"
24
+ end
25
+ end
26
+ unless jwk[:kty] == "EC"
27
+ raise Sidetree::Error, "JWK Es256k missing or invalid kty"
28
+ end
29
+ unless jwk[:crv] == "secp256k1"
30
+ raise Sidetree::Error, "JWK Es256k missing or invalid crv"
31
+ end
32
+ unless jwk[:x].is_a?(String)
33
+ raise Sidetree::Error, "JWK Es256k missing or invalid type x"
34
+ end
35
+ unless jwk[:y].is_a?(String)
36
+ raise Sidetree::Error, "JWK Es256k missing or invalid type y"
37
+ end
38
+ unless jwk[:x].length == 43
39
+ raise Sidetree::Error, "JWK Es256k has incorrect length of x"
40
+ end
41
+ unless jwk[:y].length == 43
42
+ raise Sidetree::Error, "JWK Es256k has incorrect length of y"
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,52 @@
1
+ module Sidetree
2
+ module Util
3
+ module JWS
4
+ module_function
5
+
6
+ # Sign to +claim+ by +private_key+.
7
+ # @param [Hash] claim
8
+ # @param [Sidetree::Key] private_key Private key used by sign.
9
+ # @return [JSON::JWS]
10
+ def sign(claim, private_key)
11
+ jwt = JSON::JWT.new(claim)
12
+ jwt.header.delete(:typ)
13
+ jwt.sign(private_key.jws_sign_key, :ES256K)
14
+ end
15
+
16
+ # Parse +jws_string+ to JSON::JWS
17
+ # @param [String] jws_string JWS data string.
18
+ # @return [JSON::JWS]
19
+ # @raise [Sidetree::Error]
20
+ def parse(jws_string)
21
+ jws =
22
+ JSON::JWS.decode_compact_serialized(jws_string, :skip_verification)
23
+ validate!(jws)
24
+ jws
25
+ end
26
+
27
+ # Check whether valid +jws+ or not as sidetree jws.
28
+ # @param [JSON::JWS] jws
29
+ # @return [Boolean]
30
+ def valid?(jws)
31
+ begin
32
+ validate!(jws)
33
+ true
34
+ rescue Sidetree::Error
35
+ false
36
+ end
37
+ end
38
+
39
+ # Validate +jws+ as sidetree jws.
40
+ # @param [JSON::JWS] jws
41
+ # @raise [Sidetree::Error]
42
+ def validate!(jws)
43
+ unless jws.header.length == 1
44
+ raise Sidetree::Error, "jws header missing or unknown property"
45
+ end
46
+ unless jws.header[:alg] == "ES256K"
47
+ raise Sidetree::Error, "jws header missing or incorrect alg"
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,8 @@
1
+ module Sidetree
2
+ module Util
3
+ autoload :Compressor, "sidetree/util/compressor"
4
+ autoload :AnchoredDataSerializer, "sidetree/util/anchored_data_serializer"
5
+ autoload :JWS, "sidetree/util/jws"
6
+ autoload :JWK, "sidetree/util/jwk"
7
+ end
8
+ end