spruceid / ssi Goto Github PK
View Code? Open in Web Editor NEWCore library for decentralized identity.
Home Page: https://spruceid.dev
License: Apache License 2.0
Core library for decentralized identity.
Home Page: https://spruceid.dev
License: Apache License 2.0
The required context in did-core is now https://www.w3.org/ns/did/v1
. Check if there are other changes in the data model that src/did.rs
should be updated to reflect.
By putting DID methods into their own repos but implementing the DID traits, we can ensure maximum modularity and extensibility. We should consider how this affects SSI and DIDKit, and if it would be worthwhile. What are the considerations and major changes?
@clehner, phenomenal work on the test suite. I was running the test suite and found that only a single test was pending under the optional section. Is this intentional or is there a different mode I should run the test suite in?
#47 introduces a dependency on async_std, as json-ld's JSON-LD expansion implementation is asynchronous. We keep the existing synchronous API by using block_on internally. But for WASM, block_on
does not a return a value, and requires the future to be static, since it uses wasm_bindgen_futures::spawn_local. This is a problem for using #47 with WASM (#52) while keeping ssi
's blocking API.
I see the following options to proceed:
block_on
/spawn_local
work somehow, maybe using global static storage and/or message passing.json-ld
support synchronous expansion.json-ld
.The last option might be the most sensible. I think we want to support asynchronous usage anyway, e.g. to be able to make HTTP requests during expansion and verification.
wasm-bindgen-futures
can convert a Rust Future to a JS Promise. So if ssi
uses async
functions, DIDKit's WASM package should be able to wrap those with Promises.
Non-WASM use of ssi
could continue to operate synchronously by using block_on
in the application code. I think that would work for the DIDKit CLI and FFIs.
When we start to do actual I/O then we can update the FFIs to integrate with native event loops.
As per #98 (comment)
To release ssi
publicly, we should use thiserror
to easily derive std::error::Error
and make the API even more easy to use.
The main benefit of std::error::Error
is the chain of errors, makes debugging easier.
Re: spruceid/didkit#16, spruceid/didkit#16 (comment)
Performance: an OS may provide access to cryptographic hardware that is faster than using our own crypto dependencies on the CPU.
Security and interop: an OS may provide access to manage and use keys where the private key material is not exposed to the application (including ssi
). This could be considered a special case of #53.
Using OS-provided crypto libraries could be done with feature flags as in #52, and/or or by detecting the library at runtime (dynamic linking), with fallback to our compiled code.
We should have the answer to this following riddle to ensure that our libraries are correct in all cases, not just the common ones. This is currently hard-coded in places like did-key's resolution fn.
When an arbitrary bytearray of length
n
is encoded as base58check, what is the length of bytearray output?
#93 included a fixup commit a804e56 which should have been squashed before merging. I would like to rebase main
to squash the fixup commit with its parent commit.
We should be forthcoming about which cryptographic libs are used for what functions, such as Ring for ed25519 keygen, or openssl, etc.
We need to register the tz1/tz2/tz3 verification methods so we could refer to them satisfying LD-Proofs? E.g., Ed25519PublicKeyBLAKE2BDigestSize20Base58Encoded2020
.
It looks to me like the way to register a proof type is to create a specification for it, like https://w3c-ccg.github.io/lds-ed25519-2020/, and then make a PR adding it to security vocab, e.g. w3c-ccg/security-vocab#66
Related to #49
Related to spruceid/didkit#16, spruceid/didkit#16 (comment)
Allow issuing/signing where we don't have direct access to the private key, e.g. because it is in a hardware authentication device. Support some key management system protocols.
As per #98 (comment)
If we selected dependencies carefully here, it may be possible to remove libsodium
and slim the cryptographic profile of our dependency tree.
Currently the ssi
package depends on these:
Cargo book says crates.io does not allow packages to be published with git dependencies. So for publishing to crates.io
, I think we need to either:
json-ld
and https://github.com/maciejhirsz/json-rust to be published to crates.io
json-ld
and json
to publish to crates.io under different namesjson-ld
and json
Edit: #139 means json
no longer needs to be vendored. Vendoring json-ld
is implemented in #138.
As per #98 (comment)
A credential's credentialSchema
may specify a URI for a JSON Schema file for validating the credential. (https://w3c.github.io/vc-data-model/#proofs-signatures)
A Rust library for JSON Schema is jsonschema.
jsonschema
uses HTTP client library reqwest which pulls in a lot of dependencies (#18). Also, the requests are blocking and responses are not cached (Stranger6667/jsonschema-rs#75). But it looks like the resolver is not used internally to validation but only if the caller calls it. So this might not be a problem us, especially if reqwest
is made optional (Stranger6667/jsonschema-rs#137), as we can do our own HTTP resolution (edit: maybe just using reqwest
as well), e.g. alongside resolution for JSON-LD Context documents.
Another library is schemafy (mentioned in #21 (comment)) which generates types from schemas at compile time. But it doesn't have full support for the latest JSON Schema drafts. Its use may be limited here since I think we want to be able to validate credentials against arbitrary schemas at runtime.
The http-jni-docs
branch fails to build on MacOS Catalina with the following rustc
version:
% rustc --version
rustc 1.50.0-nightly (da3846948 2020-11-21)
git clone [email protected]:spruceid/ssi.git
git checkout http-jni-docs
cargo build
didkit % cargo build
warning: unused import: `Statement`
--> /Users/wayne/work/ssi/src/jsonld.rs:6:5
|
6 | Statement,
| ^^^^^^^^^
|
= note: `#[warn(unused_imports)]` on by default
warning: unused variable: `identifier`
--> /Users/wayne/work/ssi/src/jsonld.rs:69:9
|
69 | let identifier = match identifier {
| ^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_identifier`
|
= note: `#[warn(unused_variables)]` on by default
warning: unused variable: `node_map`
--> /Users/wayne/work/ssi/src/jsonld.rs:329:5
|
329 | node_map: &NodeMap,
| ^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_node_map`
warning: unused variable: `dataset`
--> /Users/wayne/work/ssi/src/jsonld.rs:330:5
|
330 | dataset: &mut DataSet,
| ^^^^^^^ help: if this is intentional, prefix it with an underscore: `_dataset`
warning: unused variable: `options`
--> /Users/wayne/work/ssi/src/jsonld.rs:331:5
|
331 | options: Option<&JsonLdOptions>,
| ^^^^^^^ help: if this is intentional, prefix it with an underscore: `_options`
warning: field is never read: `produce_generalized_rdf`
--> /Users/wayne/work/ssi/src/jsonld.rs:23:5
|
23 | produce_generalized_rdf: Option<bool>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(dead_code)]` on by default
warning: field is never read: `rdf_direction`
--> /Users/wayne/work/ssi/src/jsonld.rs:24:5
|
24 | rdf_direction: Option<RdfDirection>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: 7 warnings emitted
Compiling didkit_cbindings v0.1.0 (/Users/wayne/work/didkit/lib/cbindings)
Compiling didkit-http v0.0.1 (/Users/wayne/work/didkit/http)
error: future cannot be sent between threads safely
--> http/src/lib.rs:164:9
|
164 | / Box::pin(async move {
165 | | let body_reader = hyper::body::aggregate(req).await?.reader();
166 | | let issue_req: IssueCredentialRequest = match serde_json::from_reader(body_reader) {
167 | | Ok(reader) => reader,
... |
188 | | .map_err(|err| err.into())
189 | | })
| |__________^ future created by async block is not `Send`
|
= help: the trait `Send` is not implemented for `(dyn StdError + 'static)`
note: future is not `Send` as this value is used across an await
--> http/src/lib.rs:178:28
|
177 | Err(err) => {
| --- has type `didkit::Error` which is not `Send`
178 | return Self::response(StatusCode::INTERNAL_SERVER_ERROR, err.to_string())
| ____________________________^
179 | | .await;
| |______________________________^ await occurs here, with `err` maybe used later
180 | }
| - `err` is later dropped here
= note: required for the cast to the object type `dyn Future<Output = std::result::Result<Response<Body>, error::Error>> + Send`
error: future cannot be sent between threads safely
--> http/src/lib.rs:164:9
|
164 | / Box::pin(async move {
165 | | let body_reader = hyper::body::aggregate(req).await?.reader();
166 | | let issue_req: IssueCredentialRequest = match serde_json::from_reader(body_reader) {
167 | | Ok(reader) => reader,
... |
188 | | .map_err(|err| err.into())
189 | | })
| |__________^ future created by async block is not `Send`
|
= help: the trait `Sync` is not implemented for `(dyn StdError + 'static)`
note: future is not `Send` as this value is used across an await
--> http/src/lib.rs:178:28
|
178 | return Self::response(StatusCode::INTERNAL_SERVER_ERROR, err.to_string())
| ____________________________^
179 | | .await;
| |______________________________^ first, await occurs here, with `err` maybe used later...
note: `err` is later dropped here
--> http/src/lib.rs:179:31
|
178 | return Self::response(StatusCode::INTERNAL_SERVER_ERROR, err.to_string())
| --- has type `&didkit::Error` which is not `Send`
179 | .await;
| ^
help: consider moving this into a `let` binding to create a shorter lived borrow
--> http/src/lib.rs:178:78
|
178 | return Self::response(StatusCode::INTERNAL_SERVER_ERROR, err.to_string())
| ^^^^^^^^^^^^^^^
= note: required for the cast to the object type `dyn Future<Output = std::result::Result<Response<Body>, error::Error>> + Send`
error: future cannot be sent between threads safely
--> http/src/lib.rs:246:9
|
246 | / Box::pin(async move {
247 | | let body_reader = hyper::body::aggregate(req).await?.reader();
248 | | let issue_req: ProvePresentationRequest = match serde_json::from_reader(body_reader) {
249 | | Ok(reader) => reader,
... |
270 | | .map_err(|err| err.into())
271 | | })
| |__________^ future created by async block is not `Send`
|
= help: the trait `Send` is not implemented for `(dyn StdError + 'static)`
note: future is not `Send` as this value is used across an await
--> http/src/lib.rs:260:28
|
259 | Err(err) => {
| --- has type `didkit::Error` which is not `Send`
260 | return Self::response(StatusCode::INTERNAL_SERVER_ERROR, err.to_string())
| ____________________________^
261 | | .await;
| |______________________________^ await occurs here, with `err` maybe used later
262 | }
| - `err` is later dropped here
= note: required for the cast to the object type `dyn Future<Output = std::result::Result<Response<Body>, error::Error>> + Send`
error: future cannot be sent between threads safely
--> http/src/lib.rs:246:9
|
246 | / Box::pin(async move {
247 | | let body_reader = hyper::body::aggregate(req).await?.reader();
248 | | let issue_req: ProvePresentationRequest = match serde_json::from_reader(body_reader) {
249 | | Ok(reader) => reader,
... |
270 | | .map_err(|err| err.into())
271 | | })
| |__________^ future created by async block is not `Send`
|
= help: the trait `Sync` is not implemented for `(dyn StdError + 'static)`
note: future is not `Send` as this value is used across an await
--> http/src/lib.rs:260:28
|
260 | return Self::response(StatusCode::INTERNAL_SERVER_ERROR, err.to_string())
| ____________________________^
261 | | .await;
| |______________________________^ first, await occurs here, with `err` maybe used later...
note: `err` is later dropped here
--> http/src/lib.rs:261:31
|
260 | return Self::response(StatusCode::INTERNAL_SERVER_ERROR, err.to_string())
| --- has type `&didkit::Error` which is not `Send`
261 | .await;
| ^
help: consider moving this into a `let` binding to create a shorter lived borrow
--> http/src/lib.rs:260:78
|
260 | return Self::response(StatusCode::INTERNAL_SERVER_ERROR, err.to_string())
| ^^^^^^^^^^^^^^^
= note: required for the cast to the object type `dyn Future<Output = std::result::Result<Response<Body>, error::Error>> + Send`
error: aborting due to 4 previous errors
error: could not compile `didkit-http`
To learn more, run the command again with --verbose.
didkit % rustc --version
rustc 1.50.0-nightly (da3846948 2020-11-21)
Ref: #98 (comment)
Example test suite we can borrow from:
https://github.com/jwt-dotnet/jwt/tree/main/tests/JWT.Tests.Common
As per the spec, implement serde-infused data structures supporting the MAY, SHOULD, and MUST criteria for DIDs and DID Documents. We must decide on nom, pest, or other existing URI libraries for the parsing of DID URLs.
We should minimize external dependencies where possible. Towards this end, we should do a cost-benefit analysis of our dependencies and their dependencies (and so on) to see if they warrant being in the branch. Things we want to avoid:
Todo:
This library is AGPL, which may be problematic for an Apache 2.0 release.
As per https://w3c-ccg.github.io/ld-proofs/, we should support LD-Proofs for signing and verification of VCs
This would let us to use DID URL dereferencing operations to "resolve" DID Documents to include verifiable service endpoints as parameter payloads, avoiding the problem of disclosing more-than-necessary information in the DID Document.
@clehner the JSON-LD spec works in IRIs instead of URIs, should we update our implementation and variable naming to accommodate for this? I realize that a lot of our data structures are not JSON-LD specific, but perhaps it would be cleaner to adopt IRI wholesale. I believe this would affect only variable naming in most instances, as Rust strings support UTF-8.
Do you have thoughts on this?
References:
As defined in the spec, we should implement serde-infused data structures that adhere to the specification's MAY, SHOULD, and MUST criteria, including tests.
We believe that many users would like to use this to test out the library. Fortunately, it looks like we have much of the implementation in did-resolver
as per the HTTP resolver!
For spruceid/didkit#15 and spruceid/didkit#16
Enable using libraries other than jsonwebtoken
/ring
that support WASM targets. Possbly make it a feature or features for the library user to choose which underlying crypto library to use.
A great future work item might be to allow interpretation of signing_input
data through OOB indicators to potentially allow a client to display a neutral interface to the user of what is to be signed exactly, and any further implications according to trust frameworks.
As per https://github.com/spruceid/ssi/pull/94/files#r582078898
For example, the recent additions with spruceid/did-tezos#15 have added ~20 MB to the debug binary, but fortunately the release binary still sits at a slim 16 MB.
We are encountering users who want to utilize traditional CA infrastructure in conjunction with DIDs/VCs. A DID method may be an appropriate way to ensure this interoperability.
I think an ultimate demo of this would be X.509-based DIDs talking to did:onion
-based DIDs over TorGap as per spruceid/didkit#68
Possible examples of DIDs based on X.509:
did:x509:canonical:ccadb:example.com
did:x509:canonical:fcpca:website.gov
did:x509:md5:444bcb3a3fcf8389296c49467f27e1d6:server.corpinternal.com
did:x509:sha1:99b4251e2eee05d8292e8397a90165293d116028:server.corpinternal.com
did:x509:sha2:2689367b205c16ce32ed4200942b8b8b1e262dfc70d9bc9fbc77c49699a4f1df:server.corpinternal.com
The finger/thumbprints (md5/sha1/sha2) can be defined as per:
Uncurated and undirected dump of prior and related work:
https://github.com/WebOfTrustInfo/rwot9-prague/blob/master/topics-and-advance-readings/X.509-DID-Method.md
https://www.researchgate.net/publication/342027346_Distributed-Ledger-based_Authentication_with_Decentralized_Identifiers_and_Verifiable_Credentials
https://hyperledger-fabric.readthedocs.io/en/release-2.2/identity/identity.html
https://arxiv.org/pdf/2003.05106.pdf
https://www.ndss-symposium.org/wp-content/uploads/diss2019_05_Lagutin_paper.pdf
https://github.com/WebOfTrustInfo/rwot1-sf/blob/master/draft-documents/Decentralized-Public-Key-Infrastructure-CURRENT.md
https://arxiv.org/pdf/2004.07063.pdf
This would be a good candidate specification for a CCG work item.
Proof type | Verification method type | Used with |
---|---|---|
Ed25519BLAKE2BDigestSize20Base58CheckEncodedSignature2021 |
Ed25519PublicKeyBLAKE2BDigestSize20Base58CheckEncoded2021 |
did:tz:tz1 |
P256BLAKE2BDigestSize20Base58CheckEncodedSignature2021 |
P256PublicKeyBLAKE2BDigestSize20Base58CheckEncoded2021 |
did:tz:tz3 |
Eip712Signature2021 |
Eip712Method2021 |
did:ethr (experimental) |
SolanaSignature2021 |
SolanaMethod2021 |
did:sol (experimental) or did:pkh:sol (did:pkh:solana ) |
AleoSignature2021 |
AleoMethod2021 or BlockchainVerificationMethod2021 |
did:pkh:aleo |
TezosJcsSignature2021 |
TezosMethod2021 |
did:tz or did:pkh:tz |
TezosSignature2021 |
TezosMethod2021 |
did:tz or did:pkh:tz |
EthereumPersonalSignature2021 |
EcdsaSecp256k1VerificationKey2019 or EcdsaSecp256k1RecoveryMethod2020 |
If we were to build a JSON-LD library, I wonder how tenable would be the idea of resolving and expanding contexts into code via metaprogramming prior to compilation. We could assume the JSON files are in hand--I wonder if there would be any benefits from a safety and performance perspective, and how much binary bloat such a flag might add. Just a thought--this is likely one of those "someday maybe" features that we revisit when the need actually arises.
cc @clehner
Could we design an LD-Proof to remove the requirement of including the public key in the authentication section? As per #98 (comment)
A CONTRIBUTING.md
file could:
ID2020 is an organization committed to solving important human rights and privacy issues globally, especially in the global south. They have technical and other requirements to certify software to their standards. We should use these to inform our roadmap, implementation, and products, then attaining certification if appropriate.
w3c/did-core#424 renamed resolveStream
to resolveRepresentation
.
Following w3c/did-resolution#57 (comment), I think we could simplify the API slightly by returning a byte slice instead of a stream.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.