Crate ssi

Source
Expand description

The SSI library provides a simple and modular API to sign and verify claims exchanged between applications using Decentralized Identifiers (DIDs). SSI is embedded in the cross-platform didkit library as a core dependency.

This library supports the two main families of verifiable claims:

§Basic Usage

SSI provides various functions to parse, verify, create and sign various kind of claims. This section shows how to use these functions in combination with JSON Web Signatures (or Tokens) and Verifiable Credentials.

§Verification

The simplest type of claim to load and verify is probably JSON Web Signatures (JWSs), often use to encode JSON Web Tokens (JWTs). To represent such claims SSI provides the JwsBuf type representing a JWS in compact textual form. One can load a JWS using new and verify it using verify.

use ssi::prelude::*;

// Load a JWT from the file system.
let jwt = JwsBuf::new(
  std::fs::read_to_string("examples/files/claims.jwt")
  .expect("unable to load JWT")
).expect("invalid JWS");

// Setup a verification method resolver, in charge of retrieving the
// public key used to sign the JWT.
// Here we use the example `ExampleDIDResolver` resolver, enabled with the
// `example` feature.
let vm_resolver = ExampleDIDResolver::default().into_vm_resolver::<AnyJwkMethod>();

// Setup the verification parameters.
let params = VerificationParameters::from_resolver(vm_resolver);

// Verify the JWT.
assert!(jwt.verify(&params).await.expect("verification failed").is_ok())

§Verifiable Credentials

Verifiable Credential are much more complex as they require interpreting the input claims and proofs, such as Data-Integrity proofs as Linked-Data using JSON-LD. This operation is highly configurable. SSI provide functions exposing various levels of implementation details that you can tweak as needed. The simplest of them is any_credential_from_json_str that will simply load a VC from a string, assuming it is signed using any Data-Integrity proof supported by SSI.

use ssi::prelude::*;

let vc = ssi::claims::vc::v1::data_integrity::any_credential_from_json_str(
  &std::fs::read_to_string("examples/files/vc.jsonld")
  .expect("unable to load VC")
).expect("invalid VC");

// Setup a verification method resolver, in charge of retrieving the
// public key used to sign the JWT.
let vm_resolver = ExampleDIDResolver::default().into_vm_resolver();

// Setup the verification parameters.
let params = VerificationParameters::from_resolver(vm_resolver);

assert!(vc.verify(&params).await.expect("verification failed").is_ok());

§Signature & Custom Claims

In the previous section we have seen how to load and verify arbitrary claims. This section shows how to create and sign custom claims. With SSI, any Rust type can serve as claims as long as it complies to certain conditions such as implementing serialization/deserialization functions using serde. Don’t forget to enable the derive feature for serde.

In the following example, we create a custom type MyClaims and sign it as a JWT.

use serde::{Serialize, Deserialize};
use ssi::prelude::*;

// Defines the shape of our custom claims.
#[derive(Serialize, Deserialize)]
pub struct MyClaims {
  name: String,
  email: String
}

// Create JWT claims from our custom ("private") claims.
let claims = JWTClaims::from_private_claims(MyClaims {
  name: "John Smith".to_owned(),
  email: "john.smith@example.org".to_owned()
});

// Create a random signing key, and turn its public part into a DID URL.
let mut key = JWK::generate_p256(); // requires the `p256` feature.
let did = DIDJWK::generate_url(&key.to_public());
key.key_id = Some(did.into());

// Sign the claims.
let jwt = claims.sign(&key).await.expect("signature failed");

// Create a verification method resolver, which will be in charge of
// decoding the DID back into a public key.
let vm_resolver = DIDJWK.into_vm_resolver::<AnyJwkMethod>();

// Setup the verification parameters.
let params = VerificationParameters::from_resolver(vm_resolver);

// Verify the JWT.
assert!(jwt.verify(&params).await.expect("verification failed").is_ok());

// Print the JWT.
println!("{jwt}")

§Verifiable Credential

We can use a similar technique to sign a VC with custom claims. The SpecializedJsonCredential type provides a customizable implementation of the VC data-model 1.1 where you can set the credential type yourself.

use static_iref::uri;
use serde::{Serialize, Deserialize};
use ssi::claims::vc::syntax::NonEmptyVec;
use ssi::prelude::*;

// Defines the shape of our custom claims.
#[derive(Serialize, Deserialize)]
pub struct MyCredentialSubject {
  #[serde(rename = "https://example.org/#name")]
  name: String,

  #[serde(rename = "https://example.org/#email")]
  email: String
}

let credential = ssi::claims::vc::v1::JsonCredential::<MyCredentialSubject>::new(
  Some(uri!("https://example.org/#CredentialId").to_owned()), // id
  uri!("https://example.org/#Issuer").to_owned().into(), // issuer
  DateTime::now(), // issuance date
  NonEmptyVec::new(MyCredentialSubject {
    name: "John Smith".to_owned(),
    email: "john.smith@example.org".to_owned()
  })
);

// Create a random signing key, and turn its public part into a DID URL.
let key = JWK::generate_p256(); // requires the `p256` feature.
let did = DIDJWK::generate_url(&key.to_public());

// Create a verification method resolver, which will be in charge of
// decoding the DID back into a public key.
let vm_resolver = DIDJWK.into_vm_resolver();

// Create a signer from the secret key.
// Here we use the simple `SingleSecretSigner` signer type which always uses
// the same provided secret key to sign messages.
let signer = SingleSecretSigner::new(key.clone()).into_local();

// Turn the DID URL into a verification method reference.
let verification_method = did.into_iri().into();

// Automatically pick a suitable Data-Integrity signature suite for our key.
let cryptosuite = AnySuite::pick(&key, Some(&verification_method))
  .expect("could not find appropriate cryptosuite");

let vc = cryptosuite.sign(
  credential,
  &vm_resolver,
  &signer,
  ProofOptions::from_method(verification_method)
).await.expect("signature failed");

It is critical that custom claims can be interpreted as Linked-Data. In the above example this is done by specifying a serialization URL for each field of MyCredentialSubject. This can also be done by creating a custom JSON-LD context and embed it to credential using either SpecializedJsonCredential’s context field or leveraging its context type parameter.

§Data-Models

The examples above are using the VC data-model 1.1, but you ssi also has support for:

§Features

  • w3c (enabled by default) — Signature suites specified by the W3C.

    This will enable the following Data-Integrity suites:

    • JsonWebSignature2020
    • RsaSignature2018 (requires rsa)
    • Ed25519Signature2018 (requires ed25519)
    • Ed25519Signature2020 (requires ed25519)
    • EdDsa2022 (requires ed25519)
    • EcdsaSecp256k1Signature2019 (requires secp256k1)
    • EcdsaSecp256r1Signature2019 (requires secp256r1)
    • EthereumEip712Signature2021 (requires eip712)
  • dif — Signature suites specified by the Decentralized Identify Foundation (DIF).

    This includes:

    • EcdsaSecp256k1RecoverySignature2020 (requires secp256k1)
  • ed25519 (enabled by default) — Signature suites based on ED25519 (EdDSA).

    This includes:

    • Ed25519Signature2018 (requires w3c)
    • Ed25519Signature2020 (requires w3c)
    • EdDsa2022 (requires w3c)
  • secp256k1 (enabled by default) — Signature suites based on secp256k1.

    This includes:

    • EcdsaSecp256k1Signature2019 (requires w3c)
    • EcdsaSecp256k1RecoverySignature2020 (requires dif)
  • secp256r1 (enabled by default) — Signature suites based on secp256r1.

    This includes:

    • EcdsaSecp256r1Signature2019 (requires w3c)
    • EcdsaRdfc2019 (requires w3c)
  • secp384r1 — Enable secp384r1 keys and signature suites based on secp384r1.

    This includes:

    • EcdsaRdfc2019 (requires w3c)
  • rsa (enabled by default) — Signature suites based on RSA.

    This includes:

    • RsaSignature2018 (requires w3c)
  • tezos — Tezos signatures suites.

    This includes:

    • TezosJcsSignature2021
    • TezosSignature2021
    • Ed25519BLAKE2BDigestSize20Base58CheckEncodedSignature2021 (requires ed25519)
    • P256BLAKE2BDigestSize20Base58CheckEncodedSignature2021 (requires secp256r1)
  • aleo — Enables AleoSignature2021.

  • eip712 (enabled by default) — Signature suites based on Ethereum EIP-712.

    This includes:

    • Eip712Signature2021 (requires ethereum).
    • EthereumEip712Signature2021 (requires w3c)
  • ethereum — Ethereum signature suites.

    This includes:

    • EthereumPersonalSignature2021
    • Eip712Signature2021 (requires eip712)
  • ripemd-160 (enabled by default) — Enable ripemd hashes.

  • bbs — Enable bbs.

  • ring — Use the Ring crate for crypto operations

  • http-did — Enable DID resolution tests using hyper and tokio.

  • example — Enable example DIDs.

Modules§

bbsbbs
BBS cryptoscheme.
caips
Chain Agnostic Improvement Proposals (CAIPs).
claims
Verifiable Claims.
crypto
Cryptography.
dids
Decentralized Identifiers (DIDs).
eip712
Ethereum Typed Structured Data Hashing and Signing (EIP-712).
json_ld
JSON-LD utilities. Linked-Data types.
jwk
JSON Web Key (JWK).
multicodec
Multicodec.
prelude
Collection of common names defined by SSI.
rdf
RDF utilities. Resource Description Framework (RDF) utilities, including the URDNA2015 canonicalization algorithm.
security
W3C’s Security Vocabulary.
ssh
Secure Shell utilities.
status
Claims status.
ucan
User Controlled Authorization Network (UCAN).
verification_methods
Verification Methods. This library provides the implementation of common Data Integrity verification methods such as Multikey or JsonWebKey2020.
xsd
XSD types. This crate aims at providing safe representations of XSD built-in data types.
zcap_ld
Authorization Capabilities for Linked Data (ZCAP-LD).

Structs§

JWK
JSON Web Key (JWK).

Type Aliases§

DefaultVerificationParameters
Default verification parameters type.