ruby-jsonld-signatures 0.0.10
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 +7 -0
- data/AUTHORS +2 -0
- data/LICENSE +21 -0
- data/README.md +199 -0
- data/VERSION +1 -0
- data/lib/json/ld/signature.rb +50 -0
- data/lib/json/ld/signature/ed25519Signer.rb +98 -0
- data/lib/json/ld/signature/ed25519Verifier.rb +68 -0
- data/lib/json/ld/signature/rsaSigner.rb +100 -0
- data/lib/json/ld/signature/rsaVerifier.rb +83 -0
- metadata +186 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 4935f61cd06bb5e3d67e927527fb9f2b0965c28a61a94c32313dcfa7c39b7a9d
|
4
|
+
data.tar.gz: 603d4cd7d729f4f9d5a903f613cb0b4a75c32a0b726cd7d8dc4e0f4871a857f8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6c04d6231e6c1b0eb9257160f7fed3c26f64e2913a3aec70589d5fcceeae3538fd79ac2d2030025a31094f0442350f5c6afbfeecd6f1caf0ecbdd52db3404a05
|
7
|
+
data.tar.gz: 39c321c7477204ec0301f74a0f3e67625e25da1db90eaabcd056f7192da57660683cb69a5ead0a75933a4a08a22ca7010f1ebeb59f75439afed3b8d6ca1c3f65
|
data/AUTHORS
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Accreditrust Technologies, LLC.
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,199 @@
|
|
1
|
+
This gem is an implementation of the JSON-LD Signatures specification
|
2
|
+
in Ruby that supports the following encryption options:
|
3
|
+
|
4
|
+
* RSA
|
5
|
+
* Ed25519
|
6
|
+
|
7
|
+
Demo
|
8
|
+
----
|
9
|
+
|
10
|
+
See [an example](https://ldsigdemo.herokuapp.com/) of the gem in action. The source code for the demo is [here](https://github.com/johncallahan/ldsigdemo).
|
11
|
+
|
12
|
+
Getting Started
|
13
|
+
---------------
|
14
|
+
|
15
|
+
Add the gem to your Gemfile:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
gem 'ruby-jsonld-signatures'
|
19
|
+
```
|
20
|
+
|
21
|
+
then run `bundle install`
|
22
|
+
|
23
|
+
Development
|
24
|
+
-----------
|
25
|
+
|
26
|
+
Clone this repo, bundle and run the rspec tests:
|
27
|
+
|
28
|
+
```shell
|
29
|
+
git clone https://github.com/johncallahan/ruby-jsonld-signatures.git
|
30
|
+
bundle install
|
31
|
+
rspec
|
32
|
+
```
|
33
|
+
|
34
|
+
Description
|
35
|
+
-----------
|
36
|
+
|
37
|
+
Consider the following JSON-LD document:
|
38
|
+
|
39
|
+
```json
|
40
|
+
{
|
41
|
+
"@context": [ "https://w3id.org/credentials/v1","https://w3id.org/security/v1"],
|
42
|
+
"type" : [ "Credential" ],
|
43
|
+
"claim" : {
|
44
|
+
"id" : "did:v1:test:nym:JApJf12r82Pe6PBJ3gJAAwo8F7uDnae6B4ab9EFQ7XXk",
|
45
|
+
"publicKey" : "did:v1:test:nym:JApJf12r82Pe6PBJ3gJAAwo8F7uDnae6B4ab9EFQ7XXk#authn-key-1"
|
46
|
+
},
|
47
|
+
"issuer" : "did:v1:test:nym:JApJf12r82Pe6PBJ3gJAAwo8F7uDnae6B4ab9EFQ7XXk",
|
48
|
+
"issued" : "2018-03-15T00:00:00Z"
|
49
|
+
}
|
50
|
+
```
|
51
|
+
|
52
|
+
The goal of [Linked Data Signatures](https://w3c-dvcg.github.io/ld-signatures/) is to
|
53
|
+
cryptographically "sign" a JSON-LD document such that the the order of
|
54
|
+
key/pairs within the JSON-LD document does not matter. In other words,
|
55
|
+
the signature value of the JSON content above would be:
|
56
|
+
|
57
|
+
```
|
58
|
+
t/T2Wv335B2guVYW88I9uWKEdrE3HFddrXt14AVo9aD9yr5BAbGJT5eQbVGdG+O0Hn6RU9IYgi1o15/F3x37Ag==
|
59
|
+
```
|
60
|
+
|
61
|
+
The following document is equivalent to the JSON-LD document above even
|
62
|
+
though more whitespace is added and the key/value pairs (even in
|
63
|
+
embedded blocks) are in different order but their values are equal:
|
64
|
+
|
65
|
+
```json
|
66
|
+
{
|
67
|
+
"issued" : "2018-03-15T00:00:00Z",
|
68
|
+
|
69
|
+
"issuer" : "did:v1:test:nym:JApJf12r82Pe6PBJ3gJAAwo8F7uDnae6B4ab9EFQ7XXk",
|
70
|
+
"claim" : {
|
71
|
+
"publicKey" : "did:v1:test:nym:JApJf12r82Pe6PBJ3gJAAwo8F7uDnae6B4ab9EFQ7XXk#authn-key-1",
|
72
|
+
"id" : "did:v1:test:nym:JApJf12r82Pe6PBJ3gJAAwo8F7uDnae6B4ab9EFQ7XXk"
|
73
|
+
},
|
74
|
+
|
75
|
+
"type" : [ "Credential" ],
|
76
|
+
"@context": [ "https://w3id.org/credentials/v1","https://w3id.org/security/v1"]
|
77
|
+
}
|
78
|
+
|
79
|
+
```
|
80
|
+
|
81
|
+
After generating the signature value for the JSON-LD document, the
|
82
|
+
signature value is appended to the document with additional metadata:
|
83
|
+
|
84
|
+
```json
|
85
|
+
{
|
86
|
+
"@context":["https://w3id.org/credentials/v1","https://w3id.org/security/v1"],
|
87
|
+
"type":["Credential"],
|
88
|
+
"claim":{
|
89
|
+
"id":"did:v1:test:nym:JApJf12r82Pe6PBJ3gJAAwo8F7uDnae6B4ab9EFQ7XXk",
|
90
|
+
"publicKey":"did:v1:test:nym:JApJf12r82Pe6PBJ3gJAAwo8F7uDnae6B4ab9EFQ7XXk#authn-key-1"
|
91
|
+
},
|
92
|
+
"issuer":"did:v1:test:nym:JApJf12r82Pe6PBJ3gJAAwo8F7uDnae6B4ab9EFQ7XXk",
|
93
|
+
"issued":"2018-03-15T00:00:00Z",
|
94
|
+
"signature":{
|
95
|
+
"type":"Ed25519Signature2018",
|
96
|
+
"creator":"did:v1:test:nym:JApJf12r82Pe6PBJ3gJAAwo8F7uDnae6B4ab9EFQ7XXk#authn-key-1",
|
97
|
+
"created":"2018-03-15T00:00:00Z",
|
98
|
+
"signatureValue":"t/T2Wv335B2guVYW88I9uWKEdrE3HFddrXt14AVo9aD9yr5BAbGJT5eQbVGdG+O0Hn6RU9IYgi1o15/F3x37Ag=="
|
99
|
+
}
|
100
|
+
}
|
101
|
+
```
|
102
|
+
|
103
|
+
This signed content can be presented to other parties such that any
|
104
|
+
key/value pair change to the JSON content (not the order or
|
105
|
+
whitespace) can be detected. It is useful in [DID Auth](https://github.com/WebOfTrustInfo/rebooting-the-web-of-trust-spring2018/blob/master/final-documents/did-auth.md) where a
|
106
|
+
user (via their browser or mobile device) holds a private key and
|
107
|
+
needs to provide [verifiable credentials](https://github.com/WebOfTrustInfo/rwot7/blob/master/topics-and-advance-readings/verifiable-credentials-primer.md) to a replying party or
|
108
|
+
service provider. In the case of DID Auth, the relying party can
|
109
|
+
verify the signature by resolving the DID (via a [universal
|
110
|
+
resolver](https://github.com/decentralized-identity/universal-resolver)) to obtain the public key from a blockchain ([Veres
|
111
|
+
One](https://github.com/veres-one/veres-one) in this case).
|
112
|
+
|
113
|
+
The process of signing a JSON-LD document includes:
|
114
|
+
|
115
|
+
* resolving the context vocabularies (i.e., fetching them via their URLs in the @context]
|
116
|
+
* normalizing (or sometimes called 'canonicalizing') the document
|
117
|
+
* determining the signature value with a private key (using RSA or Ed25519)
|
118
|
+
* embedding the signature JSON with the metadata and signature value (not part of the JSON-LD document)
|
119
|
+
|
120
|
+
Verifying a signed JSON-LD document includes:
|
121
|
+
|
122
|
+
* extracting the signature block from the JSON-LD document (remove it as well)
|
123
|
+
* normalizing (or sometimes called 'canonicalizing') the remaining JSON-LD document
|
124
|
+
* verifying the signature value with the public key (using RSA or Ed25519)
|
125
|
+
|
126
|
+
The ruby-jsonld-signatures gem relies on other gems to perform signing
|
127
|
+
and verifying including:
|
128
|
+
|
129
|
+
* [json-ld](https://github.com/ruby-rdf/json-ld)
|
130
|
+
* [rdf-normalize](https://github.com/ruby-rdf/rdf-normalize)
|
131
|
+
* [ed25519](https://github.com/crypto-rb/ed25519)
|
132
|
+
|
133
|
+
NOTE: additional keys that are NOT in the context vocabularies will
|
134
|
+
NOT be part of the normalization process. Thus, the following JSON-LD
|
135
|
+
document is equalivalent to the blocks shown above:
|
136
|
+
|
137
|
+
```json
|
138
|
+
{
|
139
|
+
"@context": [ "https://w3id.org/credentials/v1","https://w3id.org/security/v1"],
|
140
|
+
"type" : [ "Credential" ],
|
141
|
+
"claim" : {
|
142
|
+
"id" : "did:v1:test:nym:JApJf12r82Pe6PBJ3gJAAwo8F7uDnae6B4ab9EFQ7XXk",
|
143
|
+
"publicKey" : "did:v1:test:nym:JApJf12r82Pe6PBJ3gJAAwo8F7uDnae6B4ab9EFQ7XXk#authn-key-1"
|
144
|
+
},
|
145
|
+
"foo" : "bar",
|
146
|
+
"issuer" : "did:v1:test:nym:JApJf12r82Pe6PBJ3gJAAwo8F7uDnae6B4ab9EFQ7XXk",
|
147
|
+
"issued" : "2018-03-15T00:00:00Z"
|
148
|
+
}
|
149
|
+
```
|
150
|
+
|
151
|
+
The key "foo" is not found in either the credentials or security
|
152
|
+
vocabularies (in the @context) and therefore *not* included in the
|
153
|
+
normalized content. But the following document is not equivalent (the
|
154
|
+
key "nonce" *is* part of both the credentials and security
|
155
|
+
vocabularies - but it just has to be in one of them):
|
156
|
+
|
157
|
+
```json
|
158
|
+
{
|
159
|
+
"@context": [ "https://w3id.org/credentials/v1","https://w3id.org/security/v1"],
|
160
|
+
"type" : [ "Credential" ],
|
161
|
+
"claim" : {
|
162
|
+
"id" : "did:v1:test:nym:JApJf12r82Pe6PBJ3gJAAwo8F7uDnae6B4ab9EFQ7XXk",
|
163
|
+
"publicKey" : "did:v1:test:nym:JApJf12r82Pe6PBJ3gJAAwo8F7uDnae6B4ab9EFQ7XXk#authn-key-1"
|
164
|
+
},
|
165
|
+
"nonce" : "thisisjustarandomstring",
|
166
|
+
"issuer" : "did:v1:test:nym:JApJf12r82Pe6PBJ3gJAAwo8F7uDnae6B4ab9EFQ7XXk",
|
167
|
+
"issued" : "2018-03-15T00:00:00Z"
|
168
|
+
}
|
169
|
+
```
|
170
|
+
|
171
|
+
By the way, here is the normalized (or "canonicalized") content for
|
172
|
+
all blocks above except the one with the "nonce" key-value pair added:
|
173
|
+
|
174
|
+
```json
|
175
|
+
<did:v1:test:nym:JApJf12r82Pe6PBJ3gJAAwo8F7uDnae6B4ab9EFQ7XXk> <https://w3id.org/security#publicKey> <did:v1:test:nym:JApJf12r82Pe6PBJ3gJAAwo8F7uDnae6B4ab9EFQ7XXk#authn-key-1> .
|
176
|
+
_:c14n0 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <https://w3id.org/credentials#Credential> .
|
177
|
+
_:c14n0 <https://w3id.org/credentials#claim> <did:v1:test:nym:JApJf12r82Pe6PBJ3gJAAwo8F7uDnae6B4ab9EFQ7XXk> .
|
178
|
+
_:c14n0 <https://w3id.org/credentials#issued> "2018-03-15T00:00:00Z"^^<http://www.w3.org/2001/XMLSchema#dateTime> .
|
179
|
+
_:c14n0 <https://w3id.org/credentials#issuer> <did:v1:test:nym:JApJf12r82Pe6PBJ3gJAAwo8F7uDnae6B4ab9EFQ7XXk> .
|
180
|
+
```
|
181
|
+
|
182
|
+
Tests
|
183
|
+
-----
|
184
|
+
|
185
|
+
* Sign a basic string
|
186
|
+
* Sign a basic normalized (i.e., "canonicalized") document
|
187
|
+
* Sign a basic JSON-LD document
|
188
|
+
* Signatures of a second document that contains a non-vocabulary element are equivalent
|
189
|
+
* Signatures of a second document that contains a vocabulary element are NOT equivalent
|
190
|
+
* Signatures of a second document in different order are equivalent
|
191
|
+
* Verify signature of a signed JSON-LD document
|
192
|
+
* Detect when signature of a signed JSON-LD document is invalid
|
193
|
+
|
194
|
+
Todo
|
195
|
+
----
|
196
|
+
|
197
|
+
* The gem currently uses the [ed25519](https://github.com/crypto-rb/ed25519) gem, but I have tested the
|
198
|
+
[rbnacl](https://github.com/crypto-rb/rbnacl) gem and it works. I just need to provide an option hook.
|
199
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.10
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module JSON
|
2
|
+
module LD
|
3
|
+
module SIGNATURE
|
4
|
+
|
5
|
+
require 'base64'
|
6
|
+
require 'json/ld'
|
7
|
+
require 'rdf/normalize'
|
8
|
+
require 'json/ld/signature'
|
9
|
+
require 'json/ld/signature/ed25519Signer'
|
10
|
+
require 'json/ld/signature/ed25519Verifier'
|
11
|
+
require 'json/ld/signature/rsaSigner'
|
12
|
+
require 'json/ld/signature/rsaVerifier'
|
13
|
+
|
14
|
+
autoload :Ed25519Singer, 'json/ld/signature/ed25519Singer'
|
15
|
+
autoload :Ed25519Verifier, 'json/ld/signature/ed25519Verifier'
|
16
|
+
autoload :RsaSinger, 'json/ld/signature/rsaSinger'
|
17
|
+
autoload :RsaVerifier, 'json/ld/signature/rsaVerifier'
|
18
|
+
|
19
|
+
def generateNormalizedGraph(jsonLDDoc, opts)
|
20
|
+
jsonLDDoc.delete 'signature'
|
21
|
+
|
22
|
+
graph = RDF::Graph.new << JSON::LD::API.toRdf(jsonLDDoc)
|
23
|
+
# TODO: Parameterize the normalization
|
24
|
+
normalized = graph.dump(:normalize)
|
25
|
+
|
26
|
+
# digestdoc = ''
|
27
|
+
# digestdoc << opts['nonce'] unless opts['nonce'].nil?
|
28
|
+
# digestdoc << opts['created']
|
29
|
+
# digestdoc << normalized
|
30
|
+
# digestdoc << '@' + opts['domain'] unless opts['domain'].nil?
|
31
|
+
# digestdoc
|
32
|
+
|
33
|
+
normalized
|
34
|
+
end
|
35
|
+
|
36
|
+
module_function :generateNormalizedGraph
|
37
|
+
|
38
|
+
SECURITY_CONTEXT_URL = 'https://w3id.org/security/v1'
|
39
|
+
|
40
|
+
class JsonLdSignatureError < JsonLdError
|
41
|
+
class InvalidJsonLdDocument < JsonLdSignatureError; @code = "invalid JSON-LD document"; end
|
42
|
+
class MissingCreator < JsonLdSignatureError; @code = "missing signature creator"; end
|
43
|
+
class MissingKey < JsonLdSignatureError; @code = "missing private PEM formatted string"; end
|
44
|
+
class InvalidKeyType < JsonLdSignatureError; @code = "invalid PEM key"; end
|
45
|
+
class WrongKeyType < JsonLdSignatureError; @code = "signing requires a private key"; end
|
46
|
+
class UnreachableKey < JsonLdSignatureError; @code = "unable to retrieve public key"; end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module JSON
|
2
|
+
module LD
|
3
|
+
module SIGNATURE
|
4
|
+
|
5
|
+
class Ed25519Signer
|
6
|
+
|
7
|
+
attr_writer :pub
|
8
|
+
attr_writer :priv
|
9
|
+
|
10
|
+
def pub
|
11
|
+
@pub
|
12
|
+
end
|
13
|
+
|
14
|
+
def priv
|
15
|
+
@priv
|
16
|
+
end
|
17
|
+
|
18
|
+
def sign(input, options = {} )
|
19
|
+
|
20
|
+
# We require a creator to identify the signing key
|
21
|
+
|
22
|
+
if options['creator'].nil?
|
23
|
+
raise JsonLdSignatureError::MissingCreator, "the creator of the signature must be identified"
|
24
|
+
end
|
25
|
+
|
26
|
+
creator = options['creator']
|
27
|
+
|
28
|
+
# TODO: Validate the resolvability of the URL?
|
29
|
+
|
30
|
+
# We require a privateKeyPem in the options hash
|
31
|
+
# if options['privateKey'].nil?
|
32
|
+
# raise JsonLdSignatureError::MissingKey, "options parameter must include privateKey"
|
33
|
+
# end
|
34
|
+
|
35
|
+
# The privateKeyPem can be either a String or a parsed RSA key
|
36
|
+
# privateKey = options['privateKey']
|
37
|
+
privateKey = priv
|
38
|
+
|
39
|
+
# unless privateKey.private?
|
40
|
+
# raise JsonLdSignatureError::WrongKeyType, "submitted key is a public key"
|
41
|
+
# end
|
42
|
+
|
43
|
+
# Check the input, it should either be a String or a parsed JSON object
|
44
|
+
|
45
|
+
jsonld = case input
|
46
|
+
when String then
|
47
|
+
begin
|
48
|
+
JSON.parse(input)
|
49
|
+
rescue JSON::ParserError => e
|
50
|
+
raise JsonLdSignatureError::InvalidJsonLdDocument, e.message
|
51
|
+
end
|
52
|
+
when Hash then input
|
53
|
+
else
|
54
|
+
raise JsonLdSignatureError::InvalidJsonLdDocument
|
55
|
+
end
|
56
|
+
|
57
|
+
jsonld.delete 'signature'
|
58
|
+
# created = Time.now.iso8601
|
59
|
+
created = "2018-03-15T00:00:00Z"
|
60
|
+
# nonce = options['nonce']
|
61
|
+
# nonce = "3699b48f-a194-4415-8da3-b76269f63746"
|
62
|
+
nonce = nil
|
63
|
+
# domain = options['domain']
|
64
|
+
domain = nil
|
65
|
+
|
66
|
+
normOpts = {
|
67
|
+
'nonce' => nonce,
|
68
|
+
'domain' => options['domain'],
|
69
|
+
'created' => created,
|
70
|
+
'creator' => creator
|
71
|
+
}
|
72
|
+
|
73
|
+
normalizedGraph = JSON::LD::SIGNATURE::generateNormalizedGraph jsonld, normOpts
|
74
|
+
# puts normalizedGraph
|
75
|
+
signature = privateKey.sign normalizedGraph
|
76
|
+
|
77
|
+
enc = Base64.strict_encode64(signature)
|
78
|
+
|
79
|
+
# "@context" : "https://w3id.org/security/v1",
|
80
|
+
|
81
|
+
sigobj = JSON.parse %({
|
82
|
+
"type" : "Ed25519Signature2018",
|
83
|
+
"creator" : "#{creator}",
|
84
|
+
"created" : "#{created}",
|
85
|
+
"signatureValue" : "#{enc}"
|
86
|
+
})
|
87
|
+
|
88
|
+
sigobj['domain'] = domain unless options['domain'].nil?
|
89
|
+
sigobj['nonce'] = nonce unless nonce.nil?
|
90
|
+
|
91
|
+
jsonld['signature'] = sigobj
|
92
|
+
jsonld.to_json
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module JSON
|
2
|
+
module LD
|
3
|
+
module SIGNATURE
|
4
|
+
|
5
|
+
class Ed25519Verifier
|
6
|
+
|
7
|
+
attr_writer :pub
|
8
|
+
attr_writer :priv
|
9
|
+
|
10
|
+
def pub
|
11
|
+
@pub
|
12
|
+
end
|
13
|
+
|
14
|
+
def priv
|
15
|
+
@priv
|
16
|
+
end
|
17
|
+
|
18
|
+
def verify(input, options = {})
|
19
|
+
|
20
|
+
# We require a publicKeyPem in the options hash
|
21
|
+
# if options['publicKey'].nil?
|
22
|
+
# raise JsonLdSignatureError::MissingKey, "options parameter must include publicKey"
|
23
|
+
# end
|
24
|
+
|
25
|
+
# The publicKeyPem can be either a String or a parsed RSA key
|
26
|
+
# publicKey = options['publicKey']
|
27
|
+
publicKey = pub
|
28
|
+
|
29
|
+
# Check the input, it should either be a String or a parsed JSON object
|
30
|
+
|
31
|
+
jsonld = case input
|
32
|
+
when String then
|
33
|
+
begin
|
34
|
+
JSON.parse(input)
|
35
|
+
rescue JSON::ParserError => e
|
36
|
+
raise JsonLdSignatureError::InvalidJsonLdDocument, e.message
|
37
|
+
end
|
38
|
+
when Hash then input
|
39
|
+
else
|
40
|
+
raise JsonLdSignatureError::InvalidJsonLdDocument
|
41
|
+
end
|
42
|
+
|
43
|
+
signature = jsonld['signature']
|
44
|
+
|
45
|
+
created = signature['created']
|
46
|
+
creator = signature['creator']
|
47
|
+
signatureValue = signature['signatureValue']
|
48
|
+
domain = signature['domain']
|
49
|
+
nonce = signature['nonce']
|
50
|
+
|
51
|
+
uri = URI(creator)
|
52
|
+
|
53
|
+
normOpts = {
|
54
|
+
'nonce' => nonce,
|
55
|
+
'domain' => domain,
|
56
|
+
'created' => created,
|
57
|
+
'creator' => creator
|
58
|
+
}
|
59
|
+
|
60
|
+
normalizedGraph = JSON::LD::SIGNATURE::generateNormalizedGraph jsonld, normOpts
|
61
|
+
|
62
|
+
publicKey.verify Base64.decode64(signatureValue), normalizedGraph
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
@@ -0,0 +1,100 @@
|
|
1
|
+
module JSON
|
2
|
+
module LD
|
3
|
+
module SIGNATURE
|
4
|
+
|
5
|
+
class RsaSigner
|
6
|
+
|
7
|
+
attr_writer :pub
|
8
|
+
attr_writer :priv
|
9
|
+
|
10
|
+
def pub
|
11
|
+
@pub
|
12
|
+
end
|
13
|
+
|
14
|
+
def priv
|
15
|
+
@priv
|
16
|
+
end
|
17
|
+
|
18
|
+
def sign(input, options = {} )
|
19
|
+
|
20
|
+
# We require a creator to identify the signing key
|
21
|
+
|
22
|
+
if options['creator'].nil?
|
23
|
+
raise JsonLdSignatureError::MissingCreator, "the creator of the signature must be identified"
|
24
|
+
end
|
25
|
+
|
26
|
+
creator = options['creator']
|
27
|
+
|
28
|
+
# TODO: Validate the resolvability of the URL?
|
29
|
+
|
30
|
+
# We require a privateKeyPem in the options hash
|
31
|
+
# if options['privateKeyPem'].nil?
|
32
|
+
# raise JsonLdSignatureError::MissingKey, "options parameter must include privateKeyPem"
|
33
|
+
# end
|
34
|
+
|
35
|
+
# The privateKeyPem can be either a String or a parsed RSA key
|
36
|
+
# privateKey = case options['privateKeyPem']
|
37
|
+
# when String then OpenSSL::PKey::RSA.new options['privateKeyPem']
|
38
|
+
# when OpenSSL::PKey::RSA then options['privateKeyPem']
|
39
|
+
# else
|
40
|
+
# raise JsonLdSignatureError::InvalidKeyType, "key must be RSA Key or PEM String"
|
41
|
+
# end
|
42
|
+
privateKey = @priv
|
43
|
+
|
44
|
+
unless privateKey.private?
|
45
|
+
raise JsonLdSignatureError::WrongKeyType, "submitted key is a public key"
|
46
|
+
end
|
47
|
+
|
48
|
+
# Check the input, it should either be a String or a parsed JSON object
|
49
|
+
|
50
|
+
jsonld = case input
|
51
|
+
when String then
|
52
|
+
begin
|
53
|
+
JSON.parse(input)
|
54
|
+
rescue JSON::ParserError => e
|
55
|
+
raise JsonLdSignatureError::InvalidJsonLdDocument, e.message
|
56
|
+
end
|
57
|
+
when Hash then input
|
58
|
+
else
|
59
|
+
raise JsonLdSignatureError::InvalidJsonLdDocument
|
60
|
+
end
|
61
|
+
|
62
|
+
jsonld.delete 'signature'
|
63
|
+
# created = Time.now.iso8601
|
64
|
+
created = "2018-03-15T00:00:00Z"
|
65
|
+
nonce = options['nonce']
|
66
|
+
domain = options['domain']
|
67
|
+
|
68
|
+
normOpts = {
|
69
|
+
'nonce' => nonce,
|
70
|
+
'domain' => options['domain'],
|
71
|
+
'created' => created,
|
72
|
+
'creator' => creator
|
73
|
+
}
|
74
|
+
|
75
|
+
normalizedGraph = JSON::LD::SIGNATURE::generateNormalizedGraph jsonld, normOpts
|
76
|
+
|
77
|
+
digest = OpenSSL::Digest::SHA256.new
|
78
|
+
signature = privateKey.sign digest, normalizedGraph
|
79
|
+
enc = Base64.strict_encode64(signature)
|
80
|
+
|
81
|
+
# "@context" : "https://w3id.org/security/v1",
|
82
|
+
|
83
|
+
sigobj = JSON.parse %({
|
84
|
+
"type" : "RsaSignature2017",
|
85
|
+
"creator" : "#{creator}",
|
86
|
+
"created" : "#{created}",
|
87
|
+
"signatureValue" : "#{enc}"
|
88
|
+
})
|
89
|
+
|
90
|
+
sigobj['domain'] = domain unless options['domain'].nil?
|
91
|
+
sigobj['nonce'] = nonce unless nonce.nil?
|
92
|
+
|
93
|
+
jsonld['signature'] = sigobj
|
94
|
+
jsonld.to_json
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module JSON
|
2
|
+
module LD
|
3
|
+
module SIGNATURE
|
4
|
+
|
5
|
+
class RsaVerifier
|
6
|
+
|
7
|
+
attr_writer :pub
|
8
|
+
attr_writer :priv
|
9
|
+
|
10
|
+
def pub
|
11
|
+
@pub
|
12
|
+
end
|
13
|
+
|
14
|
+
def priv
|
15
|
+
@priv
|
16
|
+
end
|
17
|
+
|
18
|
+
def verify(input, options = {})
|
19
|
+
|
20
|
+
# We require a publicKeyPem in the options hash
|
21
|
+
# if options['publicKeyPem'].nil?
|
22
|
+
# raise JsonLdSignatureError::MissingKey, "options parameter must include publicKeyPem"
|
23
|
+
# end
|
24
|
+
|
25
|
+
# The publicKeyPem can be either a String or a parsed RSA key
|
26
|
+
# publicKey = case options['publicKeyPem']
|
27
|
+
# when String then OpenSSL::PKey::RSA.new options['publicKeyPem']
|
28
|
+
# when OpenSSL::PKey::RSA then options['publicKeyPem']
|
29
|
+
# else
|
30
|
+
# raise JsonLdSignatureError::InvalidKeyType, "key must be RSA Key or PEM String"
|
31
|
+
# end
|
32
|
+
publicKey = @pub
|
33
|
+
|
34
|
+
# Check the input, it should either be a String or a parsed JSON object
|
35
|
+
|
36
|
+
jsonld = case input
|
37
|
+
when String then
|
38
|
+
begin
|
39
|
+
JSON.parse(input)
|
40
|
+
rescue JSON::ParserError => e
|
41
|
+
raise JsonLdSignatureError::InvalidJsonLdDocument, e.message
|
42
|
+
end
|
43
|
+
when Hash then input
|
44
|
+
else
|
45
|
+
raise JsonLdSignatureError::InvalidJsonLdDocument
|
46
|
+
end
|
47
|
+
|
48
|
+
signature = jsonld['signature']
|
49
|
+
|
50
|
+
created = signature['created']
|
51
|
+
creator = signature['creator']
|
52
|
+
signatureValue = signature['signatureValue']
|
53
|
+
domain = signature['domain']
|
54
|
+
nonce = signature['nonce']
|
55
|
+
|
56
|
+
uri = URI(creator)
|
57
|
+
# response = Net::HTTP.get_response(uri)
|
58
|
+
|
59
|
+
# case response.code
|
60
|
+
# when "200"
|
61
|
+
# publicKey = OpenSSL::PKey::RSA.new response.body
|
62
|
+
# else
|
63
|
+
# raise JsonLdSignatureError::UnreachableKey,
|
64
|
+
# "Key #{creator} could not be retrieved. Error: #{response.code}, #{response.message}"
|
65
|
+
# end
|
66
|
+
|
67
|
+
normOpts = {
|
68
|
+
'nonce' => nonce,
|
69
|
+
'domain' => domain,
|
70
|
+
'created' => created,
|
71
|
+
'creator' => creator
|
72
|
+
}
|
73
|
+
|
74
|
+
normalizedGraph = JSON::LD::SIGNATURE::generateNormalizedGraph jsonld, normOpts
|
75
|
+
|
76
|
+
digest = OpenSSL::Digest::SHA256.new
|
77
|
+
publicKey.verify digest, Base64.decode64(signatureValue), normalizedGraph
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
metadata
ADDED
@@ -0,0 +1,186 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ruby-jsonld-signatures
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.10
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Brian Sletten
|
8
|
+
- John Callahan
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2018-09-27 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rdf
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: 3.0.4
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: 3.0.4
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: rdf-normalize
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: rdf-turtle
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
type: :runtime
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: rdf-spec
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: open-uri-cached
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - "~>"
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0.0'
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: 0.0.5
|
80
|
+
type: :development
|
81
|
+
prerelease: false
|
82
|
+
version_requirements: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - "~>"
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '0.0'
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.0.5
|
90
|
+
- !ruby/object:Gem::Dependency
|
91
|
+
name: rspec
|
92
|
+
requirement: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '3.2'
|
97
|
+
type: :development
|
98
|
+
prerelease: false
|
99
|
+
version_requirements: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '3.2'
|
104
|
+
- !ruby/object:Gem::Dependency
|
105
|
+
name: webmock
|
106
|
+
requirement: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 2.3.2
|
111
|
+
type: :development
|
112
|
+
prerelease: false
|
113
|
+
version_requirements: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 2.3.2
|
118
|
+
- !ruby/object:Gem::Dependency
|
119
|
+
name: json-ld
|
120
|
+
requirement: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 3.0.2
|
125
|
+
type: :development
|
126
|
+
prerelease: false
|
127
|
+
version_requirements: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: 3.0.2
|
132
|
+
- !ruby/object:Gem::Dependency
|
133
|
+
name: yard
|
134
|
+
requirement: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0.8'
|
139
|
+
type: :development
|
140
|
+
prerelease: false
|
141
|
+
version_requirements: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0.8'
|
146
|
+
description: RDF::JSON::LD:Signature is an implementation of the JSON-LD Signature
|
147
|
+
specification for the RDF.rb library suite.
|
148
|
+
email: public-rdf-ruby@w3.org
|
149
|
+
executables: []
|
150
|
+
extensions: []
|
151
|
+
extra_rdoc_files: []
|
152
|
+
files:
|
153
|
+
- AUTHORS
|
154
|
+
- LICENSE
|
155
|
+
- README.md
|
156
|
+
- VERSION
|
157
|
+
- lib/json/ld/signature.rb
|
158
|
+
- lib/json/ld/signature/ed25519Signer.rb
|
159
|
+
- lib/json/ld/signature/ed25519Verifier.rb
|
160
|
+
- lib/json/ld/signature/rsaSigner.rb
|
161
|
+
- lib/json/ld/signature/rsaVerifier.rb
|
162
|
+
homepage: http://github.com/bsletten/rdf-jsonld-signature
|
163
|
+
licenses:
|
164
|
+
- MIT
|
165
|
+
metadata: {}
|
166
|
+
post_install_message:
|
167
|
+
rdoc_options: []
|
168
|
+
require_paths:
|
169
|
+
- lib
|
170
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
171
|
+
requirements:
|
172
|
+
- - ">="
|
173
|
+
- !ruby/object:Gem::Version
|
174
|
+
version: 1.9.2
|
175
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
176
|
+
requirements:
|
177
|
+
- - ">="
|
178
|
+
- !ruby/object:Gem::Version
|
179
|
+
version: '0'
|
180
|
+
requirements: []
|
181
|
+
rubyforge_project: rdf-normalize
|
182
|
+
rubygems_version: 2.7.7
|
183
|
+
signing_key:
|
184
|
+
specification_version: 4
|
185
|
+
summary: JSON-LD Signature implementation for Ruby.
|
186
|
+
test_files: []
|