diff --git a/crates/database/README.md b/crates/database/README.md new file mode 100644 index 00000000..0779dc40 --- /dev/null +++ b/crates/database/README.md @@ -0,0 +1,64 @@ +# Database crate + +A lightweight library for managing MongoDB operations. This library provides an interface, the `Repository` trait with default implementations for interacting with MongoDB collections. It is used by the plugins in the workspace that require database access. +This crate is part of the [DIDComm mediator](https://github.com/adorsys/didcomm-mediator-rs) project. + +## Usage + +### Requirements + +* [MongoDB](https://www.mongodb.com) server instance +* Environment variables: + * `MONGO_URI`: MongoDB connection string + * `MONGO_DBN`: Database name + +### Example + +* Define an entity + +```rust +use database::Repository; + +#[derive(Debug, Clone, Serialize, Deserialize)] +struct MyEntity { + #[serde(rename = "_id", skip_serializing_if = "Option::is_none")] + id: Option, + name: String, +} + +impl Identifiable for MyEntity { + fn id(&self) -> Option { + self.id.clone() + } + + fn set_id(&mut self, id: ObjectId) { + self.id = Some(id); + } +} +``` + +* Implement the `Repository` trait(the only required method is `get_collection`) + +```rust +struct MyEntityRepository { + collection: Arc>>, +} + +#[async_trait] +impl Repository for MyEntityRepository { + fn get_collection(&self) -> Arc>> { + self.collection.clone() + } +} +``` + +* Use the repository + +```rust +let db = get_or_init_database(); +let repo = MyEntityRepository { + collection: Arc::new(RwLock::new(db.read().await.collection("my_entities"))), +}; +let entity = MyEntity { id: None, name: "example".to_string() }; +repo.store(entity).await?; +``` diff --git a/crates/database/src/lib.rs b/crates/database/src/lib.rs index 98d415de..079f558a 100644 --- a/crates/database/src/lib.rs +++ b/crates/database/src/lib.rs @@ -68,8 +68,10 @@ where Entity: Identifiable + Unpin, Entity: Serialize + for<'de> Deserialize<'de>, { + /// Get a handle to a collection. fn get_collection(&self) -> Arc>>; + /// Retrieve all entities from the database. async fn find_all(&self) -> Result, RepositoryError> { let mut entities = Vec::new(); let collection = self.get_collection(); @@ -83,7 +85,7 @@ where Ok(entities) } - /// Counts all entities by filter. + /// Gets the number of documents matching `filter`. async fn count_by(&self, filter: BsonDocument) -> Result { let collection = self.get_collection(); // Lock the Mutex and get the Collection @@ -95,10 +97,12 @@ where .map_err(|_| RepositoryError::Generic("count overflow".to_owned()))?) } + /// Find an entity by `id`. async fn find_one(&self, id: ObjectId) -> Result, RepositoryError> { self.find_one_by(doc! {"_id": id}).await } + /// Find an entity matching `filter`. async fn find_one_by(&self, filter: BsonDocument) -> Result, RepositoryError> { let collection = self.get_collection(); @@ -125,6 +129,8 @@ where Ok(entity) } + /// Find all entities matching `filter`. + /// If `limit` is set, only the first `limit` entities are returned. async fn find_all_by( &self, filter: BsonDocument, @@ -146,6 +152,7 @@ where Ok(entities) } + /// Deletes an entity by `id`. async fn delete_one(&self, id: ObjectId) -> Result<(), RepositoryError> { let collection = self.get_collection(); @@ -158,6 +165,7 @@ where Ok(()) } + /// Updates an entity. async fn update(&self, entity: Entity) -> Result { if entity.id().is_none() { return Err(RepositoryError::MissingIdentifier); diff --git a/crates/keystore/README.md b/crates/keystore/README.md index e69de29b..a3ca23df 100644 --- a/crates/keystore/README.md +++ b/crates/keystore/README.md @@ -0,0 +1,40 @@ +# Keystore Crate + +The `keystore` crate is a utility library for managing cryptographic secrets. It is used in the [Didcomm Mediator](https://github.com/adorsys/didcomm-mediator-rs/) to store and retrieve cryptographic keys for DIDcomm interactions. + +## Usage + +This crate is internal to the [Didcomm Mediator](https://github.com/adorsys/didcomm-mediator-rs/). Below is an example of interacting with the keystore: + +```rust +use keystore::{KeyStore, Secrets}; +use mongodb::bson::{doc, Bson, Document}; +use did_utils::jwk::Jwk; + +// Initialize the keystore +let keystore = KeyStore::get(); + +let jwk: Jwk = serde_json::from_str( + r#"{ + "kty": "OKP", + "crv": "X25519", + "x": "SHSUZ6V3x355FqCzIUfgoPzrZB0BQs0JKyag4UfMqHQ", + "d": "0A8SSFkGHg3N9gmVDRnl63ih5fcwtEvnQu9912SVplY" + }"#, +) +.unwrap(); + +// Store a secret +let secret = Secrets { + id: Some(ObjectId::new()), + kid: "key-1".to_string(), + secret_material: jwk, +}; +keystore.store(secret).await?; + +// Retrieve a secret by ID +let secret = keystore.find_one(doc! {"kid": "key-1"}).await?; + +// Delete a secret by ID +keystore.delete_one(secret.id.unwrap()).await?; +``` diff --git a/crates/keystore/src/lib.rs b/crates/keystore/src/lib.rs index 5fe88c7c..c20804cd 100644 --- a/crates/keystore/src/lib.rs +++ b/crates/keystore/src/lib.rs @@ -9,6 +9,7 @@ use tokio::sync::RwLock; static SECRETS_COLLECTION: OnceCell> = OnceCell::new(); +/// Represents a cryptographic secret stored in the keystore. #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] pub struct Secrets { #[serde(rename = "_id")] @@ -30,6 +31,7 @@ impl Identifiable for Secrets { } } +/// A keystore for managing cryptographic secrets. #[derive(Debug, Clone)] pub struct KeyStore where diff --git a/crates/plugin-api/README.md b/crates/plugin-api/README.md index cccf5475..1547e600 100644 --- a/crates/plugin-api/README.md +++ b/crates/plugin-api/README.md @@ -1,6 +1,6 @@ # Server plugin API -The server used for this project provides a system for building versatile applications by integrating functionalities offered by configurable plugins via the plugin API. +An interface that can be used by a generic server to build versatile applications by integrating functionalities offered by configurable plugins via the plugin API. It is part of the [Didcomm Mediator](https://github.com/adorsys/didcomm-mediator-rs/) project. ## Features @@ -51,9 +51,9 @@ impl Plugin for MyPlugin { Ok(()) } - fn routes(&self) -> Router { + fn routes(&self) -> Result { // Define and return routes here - Router::new().route("/myplugin", get(my_plugin_handler)) + Router::new().route("/myplugin", get(my_plugin_handler))? } } diff --git a/crates/web-plugins/did-endpoint/README.md b/crates/web-plugins/did-endpoint/README.md index 86032002..7a53e1b4 100644 --- a/crates/web-plugins/did-endpoint/README.md +++ b/crates/web-plugins/did-endpoint/README.md @@ -1,32 +1,35 @@ # did-endpoint -The `did-endpoint` crate provides tools and functionalities for generating and managing Decentralized Identifiers (DIDs) and web-based interactions. +The `did-endpoint` plugin crate provides a set of tools for generating and validating a DID document. It is a part of the [Didcomm Mediator](https://github.com/adorsys/didcomm-mediator-rs/) project. ## Features -- **Generates keys and forward them for DID generation:** - **Builds and persists DID document:** - **Validates the integrity of the persisted DID document** -## Usage - -To use `did-endpoint` in your project, add the following to your **Cargo.toml**: - -```toml -did-endpoint = "0.1.0" -``` - ### Example Here’s a simple example of how you can generate and validate a DID document: ```rust use did_endpoint::{didgen, validate_diddoc}; +use filesystem::{FileSystem, StdFileSystem}; +use keystore::KeyStore; + +let storage_dirpath = std::env::var("STORAGE_DIRPATH").unwrap(), +let server_public_domain = std::env::var("SERVER_PUBLIC_DOMAIN").unwrap(); + +let mut filesystem = filesystem::StdFileSystem; +let keystore = keystore::KeyStore::get(); -let (storage_dirpath, server_public_domain) = ("target/storage", "https://example.com"); +// Generate and persist a new DID document +didgen::didgen( + storage_dirpath, + server_public_domain, + &keystore, + &mut filesystem, +)?; -// generate and persist a did document -didgen(&storage_dirpath, &server_public_domain)?; -// validate the generated did document -assert!(validate_diddoc(&storage_dirpath).is_ok()); +// Validate the integrity of the persisted DID document +didgen::validate_diddoc(storage_dirpath, &keystore, &mut filesystem)?; ``` diff --git a/crates/web-plugins/didcomm-messaging/README.md b/crates/web-plugins/didcomm-messaging/README.md new file mode 100644 index 00000000..29c193ef --- /dev/null +++ b/crates/web-plugins/didcomm-messaging/README.md @@ -0,0 +1,97 @@ +# didcomm-messaging + +The `didcomm-messaging` plugin is a web plugin of the [DIDComm mediator](https://github.com/adorsys/didcomm-mediator-rs/) project. It provides implementations of various DIDComm messaging protocols as plugins and features, so protocols can be added and deleted with minimal effort as well as being dynamically loaded. + +See the repository [README](https://github.com/adorsys/didcomm-mediator-rs/blob/main/README.md) for the list of currently supported protocols. + +## Usage + +**Implementing a new protocol:** + +* Define handler(s) for the protocol + +```rust +use async_trait::async_trait; +use axum::response::{IntoResponse, Response}; +use didcomm::Message; +use message_api::{MessageHandler, MessagePlugin, MessageRouter}; +use shared::state::AppState; +use std::sync::Arc; + +pub struct MockProtocol; + +struct ExampleProtocolHandler1; +struct ExampleProtocolHandler2; + +#[async_trait] +impl MessageHandler for ExampleProtocolHandler1 { + async fn handle( + &self, + _state: Arc, + message: Message, + ) -> Result, Response> { + // do something with the message + Ok(None) + } +} + +#[async_trait] +impl MessageHandler for ExampleProtocolHandler2 { + async fn handle( + &self, + _state: Arc, + message: Message, + ) -> Result, Response> { + // do something with the message + Ok(None) + } +} + +impl MessagePlugin for MockProtocol { + fn name(&self) -> &'static str { + "mock_protocol" + } + + fn didcomm_routes(&self) -> MessageRouter { + MessageRouter::new() + .register("message-type-1", ExampleProtocolHandler1) + .register("message-type-2", ExampleProtocolHandler2) + } +} +``` + +* Add the protocol to the `DIDCOMM_PLUGINS` array in `crates/web-plugins/didcomm-messaging/src/protocols.rs`: + +```rust +pub(crate) static DIDCOMM_PLUGINS: Lazy>> = Lazy::new(|| { + vec![ + #[cfg(feature = "mock-protocol")] + Arc::new(MockProtocol), + // other plugins + ] +}); +``` + +* Add the plugin as a feature in `didcomm-messaging` `Cargo.toml`: + +```toml +[dependencies] +mock-protocol = { workspace = true, optional = true } + +[features] +mock-protocol = ["mock-protocol", ...] + +default = ["mock-protocol", ...] +``` + +* Adjust the workspace `Cargo.toml`: + +```toml +[workspace.dependencies] +mock-protocol = { path = "./crates/web-plugins/didcomm-messaging/protocols/mock-protocol", version = "0.1.0" } + +[features] +mock-protocol = ["plugin-didcomm_messaging", "didcomm-messaging/mock-protocol"] +``` + +The plugin manager will automatically handle routing based on the added protocol's routes. diff --git a/crates/web-plugins/didcomm-messaging/did-utils/README.md b/crates/web-plugins/didcomm-messaging/did-utils/README.md index 1c82e6f9..2545f96b 100644 --- a/crates/web-plugins/didcomm-messaging/did-utils/README.md +++ b/crates/web-plugins/didcomm-messaging/did-utils/README.md @@ -10,8 +10,11 @@ A Rust library for implementing reusable utility code for DID-based applications ## Installation -```rust -cargo install did-utils +Add this to your `Cargo.toml`: + +```toml +[dependencies] +did-utils = "0.1" ``` ## Usage @@ -39,20 +42,9 @@ let encrypted_did_document = did_document.encrypt(&my_public_key); let decrypted_did_document = encrypted_did_document.decrypt(&my_private_key); ``` -## Dependencies - -* serde -* sha2 -* x25519-dalek - ## Documentation -The documentation for the library is available here: https://docs.rs/did-utils/ - -## Contributors - -* Bard -* [Your name] +The documentation for the library is available [here](https://docs.rs/did-utils) ## License diff --git a/crates/web-plugins/didcomm-messaging/did-utils/src/crypto/alg.rs b/crates/web-plugins/didcomm-messaging/did-utils/src/crypto/alg.rs index 3ac78bb0..5284a8b3 100644 --- a/crates/web-plugins/didcomm-messaging/did-utils/src/crypto/alg.rs +++ b/crates/web-plugins/didcomm-messaging/did-utils/src/crypto/alg.rs @@ -31,9 +31,13 @@ use Algorithm::*; impl Algorithm { /// Returns the multicodec prefix associated with the algorithm. /// - /// # Returns + /// # Example /// - /// A two-byte array representing the multicodec prefix. + /// ```rust + /// use did_utils::crypto::Algorithm; + /// + /// let prefix = Algorithm::Ed25519.muticodec_prefix(); + /// ``` pub fn muticodec_prefix(&self) -> [u8; 2] { match self { Ed25519 => [0xed, 0x01], @@ -47,15 +51,20 @@ impl Algorithm { } } - /// Creates an `Algorithm` enum variant from the given multicodec prefix. + /// Returns the corresponding `Algorithm` variant from a multicodec prefix. /// - /// # Parameters + /// # Example /// - /// - `prefix`: A two-byte array representing the multicodec prefix. + /// ```rust + /// use did_utils::crypto::Algorithm; /// - /// # Returns + /// # fn example() -> Option { + /// let alg = Algorithm::from_muticodec_prefix(&[0xed, 0x01])?; + /// assert_eq!(alg, Algorithm::Ed25519); /// - /// An `Option` containing the corresponding `Algorithm` variant. + /// # Some(alg) + /// # } + /// ``` pub fn from_muticodec_prefix(prefix: &[u8; 2]) -> Option { match prefix { [0xed, 0x01] => Some(Ed25519), @@ -72,9 +81,18 @@ impl Algorithm { /// Returns the length of the public key for the algorithm, if known. /// - /// # Returns + /// # Example + /// + /// ``` + /// use did_utils::crypto::Algorithm; /// - /// An `Option` containing the length of the public key in bytes. + /// # fn example() -> Option { + /// let length = Algorithm::Ed25519.public_key_length()?; + /// assert_eq!(length, 32); + /// + /// # Some(length) + /// # } + /// ``` pub fn public_key_length(&self) -> Option { match self { Ed25519 => Some(32), @@ -90,13 +108,18 @@ impl Algorithm { /// Builds a JSON Web Key from raw public key bytes. /// - /// # Parameters - /// - /// - `raw_public_key_bytes`: The raw public key bytes. + /// # Example /// - /// # Returns + /// ``` + /// use did_utils::crypto::Algorithm; + /// # use did_utils::crypto::Error; + /// # + /// # fn example() -> Result<(), Error> { + /// let jwk = Algorithm::Ed25519.build_jwk(&[1u8; 32])?; /// - /// A `Result` containing the constructed `Jwk` or a `CryptoError`. + /// # Ok(()) + /// # } + /// ``` pub fn build_jwk(&self, raw_public_key_bytes: &[u8]) -> Result { match self { Ed25519 => Ok(Jwk { @@ -144,15 +167,7 @@ impl Algorithm { } } - /// Uncompresses a compressed public key. - /// - /// # Parameters - /// - /// - `compressed_key_bytes`: The compressed public key bytes. - /// - /// # Returns - /// - /// The bytes representing the uncompressed key or a `CryptoError`. + // Uncompresses a compressed public key. pub fn uncompress_public_key(&self, compressed_key_bytes: &[u8]) -> Result, CryptoError> { if let Some(required_length) = self.public_key_length() { if required_length != compressed_key_bytes.len() { diff --git a/crates/web-plugins/didcomm-messaging/did-utils/src/crypto/ed25519.rs b/crates/web-plugins/didcomm-messaging/did-utils/src/crypto/ed25519.rs index e228baa5..7d2e46b4 100644 --- a/crates/web-plugins/didcomm-messaging/did-utils/src/crypto/ed25519.rs +++ b/crates/web-plugins/didcomm-messaging/did-utils/src/crypto/ed25519.rs @@ -12,6 +12,7 @@ use super::{ AsymmetricKey, }; +/// A wrapper struct for an Ed25519 asymmetric key pair. pub type Ed25519KeyPair = AsymmetricKey; impl std::fmt::Debug for Ed25519KeyPair { @@ -24,20 +25,10 @@ impl std::fmt::Debug for Ed25519KeyPair { } impl KeyMaterial for Ed25519KeyPair { - /// Returns the bytes of the public key. - /// - /// # Returns - /// - /// A `Result` containing the bytes of the public key or an `Error`. fn public_key_bytes(&self) -> Result<[u8; BYTES_LENGTH_32], Error> { Ok(clone_slice_to_array(self.public_key.as_bytes())) } - /// Returns the bytes of the private key. - /// - /// # Returns - /// - /// A `Result` containing the bytes of the private key or an `Error`. fn private_key_bytes(&self) -> Result<[u8; BYTES_LENGTH_32], Error> { match &self.secret_key { Some(sk) => Ok(clone_slice_to_array(&sk.to_bytes())), @@ -47,28 +38,10 @@ impl KeyMaterial for Ed25519KeyPair { } impl Generate for Ed25519KeyPair { - /// Generates a new Ed25519 key pair. - /// - /// If the initial seed is empty or invalid, a random seed will be generated. - /// - /// # Returns - /// - /// A new `Ed25519KeyPair` instance or an `Error`. fn new() -> Result { Self::new_with_seed(vec![].as_slice()) } - /// Generates a new Ed25519 key pair with a seed. - /// - /// If the seed is empty or invalid, generates a new seed. - /// - /// # Arguments - /// - /// * `seed` - The initial seed to use. - /// - /// # Returns - /// - /// A new `Ed25519KeyPair` instance. fn new_with_seed(seed: &[u8]) -> Result { match generate_seed(seed) { Ok(secret_seed) => { @@ -82,15 +55,6 @@ impl Generate for Ed25519KeyPair { } } - /// Creates a new `Ed25519KeyPair` from a public key. - /// - /// # Arguments - /// - /// * `public_key` - The bytes of the public key. - /// - /// # Returns - /// - /// A new `Ed25519KeyPair` instance. fn from_public_key(public_key: &[u8; BYTES_LENGTH_32]) -> Result { match public_key.len() { BYTES_LENGTH_32 => Ok(Ed25519KeyPair { @@ -104,17 +68,6 @@ impl Generate for Ed25519KeyPair { } } - /// Creates a new `Ed25519KeyPair` from a secret key. - /// - /// A public key will be derived from the secret key. - /// - /// # Arguments - /// - /// * `secret_key` - The bytes of the secret key. - /// - /// # Returns - /// - /// A new `Ed25519KeyPair` instance. fn from_secret_key(secret_key: &[u8; BYTES_LENGTH_32]) -> Result { match secret_key.len() { BYTES_LENGTH_32 => { @@ -130,15 +83,18 @@ impl Generate for Ed25519KeyPair { } impl CoreSign for Ed25519KeyPair { - /// Signs a payload using the secret key of the `Ed25519KeyPair`. + /// Signs the given payload and returns the signature. /// - /// # Arguments + /// The signature is generated using the private key of the `Ed25519KeyPair`. /// - /// * `payload` - The payload to sign. + /// # Example /// - /// # Returns + /// ``` + /// use did_utils::crypto::{Ed25519KeyPair, CoreSign}; /// - /// A `Result` containing the signature as bytes or an `Error`. + /// let kp = Ed25519KeyPair::new()?; + /// let signature = kp.sign(b"Hello, World!")?; + /// ``` fn sign(&self, payload: &[u8]) -> Result, Error> { // Check if the secret key is present match &self.secret_key { @@ -156,16 +112,19 @@ impl CoreSign for Ed25519KeyPair { } } - /// Verifies a payload using the public key of the `Ed25519KeyPair`. + /// Verifies the signature of the given payload. /// - /// # Arguments + /// # Example /// - /// * `payload` - The payload to verify. - /// * `signature` - The signature to verify against the payload. + /// ``` + /// use did_utils::crypto::{Ed25519KeyPair, CoreSign}; /// - /// # Returns + /// let kp = Ed25519KeyPair::new()?; + /// let signature = kp.sign(b"Hello, World!")?; + /// let result = kp.verify(b"Hello, World!", &signature)?; /// - /// A `Result` containing `()`, or an `Error` if the verification fails. + /// assert!(result.is_ok()); + /// ``` fn verify(&self, payload: &[u8], signature: &[u8]) -> Result<(), Error> { // Try to convert the signature to a `Signature` instance // This conversion is necessary because the `signature` argument is represented as bytes @@ -188,11 +147,19 @@ impl ToMultikey for Ed25519KeyPair { } impl Ed25519KeyPair { - /// Returns the X25519 key pair corresponding to the Ed25519 key pair. + /// Converts an Ed25519 key pair to an X25519 key pair. + /// + /// If the secret key is present, it derives the X25519 key pair from the secret key. + /// If the secret key is not present, it derives the X25519 key pair from the public key. + /// + /// # Example /// - /// # Returns + /// ``` + /// use did_utils::crypto::{Ed25519KeyPair, Generate, X25519KeyPair}; /// - /// A `Result` containing the X25519 key pair as `X25519KeyPair` or an `Error`. + /// let kp = Ed25519KeyPair::new()?; + /// let xkp = kp.to_x25519()?; + /// ``` pub fn get_x25519(&self) -> Result { // Check if the secret key is present match &self.secret_key { diff --git a/crates/web-plugins/didcomm-messaging/did-utils/src/crypto/format.rs b/crates/web-plugins/didcomm-messaging/did-utils/src/crypto/format.rs index 7382ad78..11f1b820 100644 --- a/crates/web-plugins/didcomm-messaging/did-utils/src/crypto/format.rs +++ b/crates/web-plugins/didcomm-messaging/did-utils/src/crypto/format.rs @@ -19,19 +19,10 @@ pub enum PublicKeyFormat { Jwk, } -/// Converts an `Ed25519KeyPair` to a `Jwk`. impl TryFrom for Jwk { type Error = CryptoError; - /// Converts an `Ed25519KeyPair` to a `Jwk`. - /// - /// # Arguments - /// - /// * `keypair` - The `Ed25519KeyPair` to convert. - /// - /// # Returns - /// - /// A `Result` containing the converted `Jwk` or an `Error`. + // Converts an Ed25519KeyPair to a Jwk. fn try_from(keypair: Ed25519KeyPair) -> Result { Ok(Jwk { key: Key::Okp(Okp { @@ -48,15 +39,7 @@ impl TryFrom for Jwk { impl TryFrom for Ed25519KeyPair { type Error = CryptoError; - /// Converts a `Jwk` to an `Ed25519KeyPair`. - /// - /// # Arguments - /// - /// * `jwk` - The `Jwk` to convert. - /// - /// # Returns - /// - /// A `Result` containing the converted `Ed25519KeyPair` or an `Error`. + // Converts a Jwk to an Ed25519KeyPair. fn try_from(jwk: Jwk) -> Result { match jwk.key { Key::Okp(okp) => { @@ -84,19 +67,10 @@ impl TryFrom for Ed25519KeyPair { } } -/// Converts an `X25519KeyPair` to a `Jwk`. impl TryFrom for Jwk { type Error = CryptoError; - /// Converts an `X25519KeyPair` to a `Jwk`. - /// - /// # Arguments - /// - /// * `keypair` - The `X25519KeyPair` to convert. - /// - /// # Returns - /// - /// A `Result` containing the converted `Jwk` or an `Error`. + // Converts an `X25519KeyPair` to a `Jwk`. fn try_from(keypair: X25519KeyPair) -> Result { Ok(Jwk { key: Key::Okp(Okp { @@ -109,19 +83,10 @@ impl TryFrom for Jwk { } } -/// Converts a `Jwk` to an `X25519KeyPair`. impl TryFrom for X25519KeyPair { type Error = CryptoError; - /// Converts a `Jwk` to an `X25519KeyPair`. - /// - /// # Arguments - /// - /// * `jwk` - The `Jwk` to convert. - /// - /// # Returns - /// - /// A `Result` containing the converted `X25519KeyPair` or an `Error`. + // Converts a `Jwk` to an `X25519KeyPair`. fn try_from(jwk: Jwk) -> Result { match jwk.key { Key::Okp(okp) => { @@ -149,15 +114,7 @@ impl TryFrom for X25519KeyPair { } } -/// Decodes a base64url encoded key string to bytes. -/// -/// # Arguments -/// -/// * `key` - The base64url encoded key string. -/// -/// # Returns -/// -/// A `Result` containing the decoded key bytes. +// Decodes a base64url encoded key string to bytes. #[allow(dead_code)] fn base64url_to_bytes(key: &str) -> Result<[u8; BYTES_LENGTH_32], ()> { let key: Vec = Base64Url.decode(key).map_err(|_| ())?; diff --git a/crates/web-plugins/didcomm-messaging/did-utils/src/crypto/mod.rs b/crates/web-plugins/didcomm-messaging/did-utils/src/crypto/mod.rs index 89c175fa..7788b003 100644 --- a/crates/web-plugins/didcomm-messaging/did-utils/src/crypto/mod.rs +++ b/crates/web-plugins/didcomm-messaging/did-utils/src/crypto/mod.rs @@ -1,42 +1,12 @@ -//! This module provides cryptographic utilities and key pair structures -//! for various cryptographic algorithms, including Ed25519, X25519, and SHA-256 hashing. +//! This module contains cryptographic utilities. //! -//! The module includes the following submodules: -//! - [`ed25519`]: Provides Ed25519 key pair generation and signature functionality. -//! - [`x25519`]: Provides X25519 key pair generation and Diffie-Hellman key exchange functionality. -//! - [`traits`]: Defines common traits for cryptographic operations. -//! - [`mod@sha256_hash`]: Provides functionality for SHA-256 hashing. +//! Provides interfaces and implementations for cryptographic key management, +//! including key generation, signing, verification, and key exchange operations. +//! It supports multiple curve algorithms, including [Ed25519], [X25519], and [SHA-256] hashing. //! -//! The module also re-exports key types and utilities for easier access. -//! -//! # Example -//! -//! ```no run -//! # use did_utils::crypto::{Ed25519KeyPair, X25519KeyPair, -//! # sha256_hash, -//! # Generate, CoreSign, ECDH}; -//! -//! // Example usage of Ed25519 key pair -//! let keypair = Ed25519KeyPair::new()?; -//! let json_file = "test_resources/crypto_ed25519_test_sign_verify.json"; -//! let json_data = std::fs::read_to_string(json_file)?; -//! let signature = keypair.sign(json_data.as_bytes()); -//! // Verify the signature -//! let verified = keypair.verify(json_data.as_bytes(), &signature?); -//! -//! // Example usage of X25519 key pair -//! let alice_seed = b"TMwLj2p2qhcuVhaFAj3QkkJGhK6pdyKx"; -//! let bob_seed = b"NWB6DbnIlewWVp5jIJOSgyX8msXNPPAL"; -//! let alice = X25519KeyPair::new_with_seed(alice_seed)?; -//! let bob = X25519KeyPair::new_with_seed(bob_seed)?; -//! -//! let alice_shared_secret = alice.key_exchange(&bob); -//! let bob_shared_secret = bob.key_exchange(&alice); -//! assert_eq!(alice_shared_secret, bob_shared_secret); -//! -//! // Example usage of SHA-256 hashing -//! let hash = sha256_hash(json_file.as_bytes()); -//!``` +//! [Ed25519]: https://en.wikipedia.org/wiki/EdDSA +//! [X25519]: https://en.wikipedia.org/wiki/X25519 +//! [SHA-256]: https://en.wikipedia.org/wiki/SHA-2 pub(crate) mod alg; mod ed25519; @@ -56,16 +26,7 @@ pub use traits::{CoreSign, Generate, KeyMaterial, ToMultikey, ToPublic, BYTES_LE pub use x25519::X25519KeyPair; /// A wrapper struct for an asymmetric key pair. -/// -/// # Fields -/// -/// - `public_key`: the public key of the key pair. -/// - `secret_key`: the optional private key of the key pair. -/// -/// # Type Parameters -/// -/// - `P`: The type of the public key. -/// - `S`: The type of the secret key. +/// This struct holds a public key and an optional secret key. pub struct AsymmetricKey { pub public_key: P, pub secret_key: Option, diff --git a/crates/web-plugins/didcomm-messaging/did-utils/src/crypto/sha256_hash.rs b/crates/web-plugins/didcomm-messaging/did-utils/src/crypto/sha256_hash.rs index 3ccc14a8..184acfac 100644 --- a/crates/web-plugins/didcomm-messaging/did-utils/src/crypto/sha256_hash.rs +++ b/crates/web-plugins/didcomm-messaging/did-utils/src/crypto/sha256_hash.rs @@ -3,13 +3,14 @@ use sha2::{Digest, Sha256}; /// Compute the SHA256 hash of a given input. /// -/// # Arguments -/// -/// * `bytes` - The input to compute the hash from. +/// # Example /// -/// # Returns +/// ```rust +/// use did_utils::crypto::sha256_hash; /// -/// The SHA256 hash as a byte array of length 32. +/// let bytes = b"Hello, world!"; +/// let hash = sha256_hash(bytes); +/// ``` pub fn sha256_hash(bytes: &[u8]) -> [u8; 32] { // Create a new Sha256 hasher let mut hasher = Sha256::new(); @@ -21,13 +22,13 @@ pub fn sha256_hash(bytes: &[u8]) -> [u8; 32] { hash.as_slice()[..32].try_into().unwrap() } -/// Compute SHA256 hash of a given input and return it as a multibase-encoded string. -/// +/// Compute SHA256 hash of a given input and return it as a multihash Base58 encoded string. +/// /// # Example -/// +/// /// ```rust /// use did_utils::crypto::sha256_multihash; -/// +/// /// let bytes = b"Hello, world!"; /// let hash = sha256_multihash(bytes); /// ``` diff --git a/crates/web-plugins/didcomm-messaging/did-utils/src/crypto/traits.rs b/crates/web-plugins/didcomm-messaging/did-utils/src/crypto/traits.rs index a35a9394..6a7878a7 100644 --- a/crates/web-plugins/didcomm-messaging/did-utils/src/crypto/traits.rs +++ b/crates/web-plugins/didcomm-messaging/did-utils/src/crypto/traits.rs @@ -8,7 +8,7 @@ use super::errors::Error; /// The length of a 32-byte key material. pub const BYTES_LENGTH_32: usize = 32; -#[allow(unused)] +/// A trait for types that can be converted to a multikey string. pub trait ToMultikey { /// Converts keypair into its multikey string fn to_multikey(&self) -> String; @@ -16,66 +16,50 @@ pub trait ToMultikey { /// A trait for types that hold key material bytes. pub trait KeyMaterial { - /// Returns the public key bytes as a slice. - /// - /// Returns a `Result` containing the public key bytes, or an `Error` if the operation fails. - + /// Returns the bytes of the public key. fn public_key_bytes(&self) -> Result<[u8; BYTES_LENGTH_32], Error>; - /// Returns the secret key bytes as a slice. - /// - /// Returns a `Result` containing the secret key bytes, or an `Error` if the operation fails. + + /// Returns the bytes of the private key. fn private_key_bytes(&self) -> Result<[u8; BYTES_LENGTH_32], Error>; } /// A trait for types that support deterministic key generation. pub trait Generate: KeyMaterial { - /// Generates a new random key. - /// - /// Returns a `Result` containing the new key, or an `Error` if the operation fails. + /// Generates a new random key pair. fn new() -> Result where Self: Sized; - /// Generates a new key deterministically using the given seed. - /// - /// Returns a `Result` containing the new key, or an `Error` if the operation fails. + /// Generates a new key pair with a given seed. + /// If the seed is empty or invalid, a random seed will be generated. fn new_with_seed(seed: &[u8]) -> Result where Self: Sized; - /// Generates a new instance from an existing public key. - /// - /// Returns a `Result` containing the new instance, or an `Error` if the operation fails. + /// Generates a new key pair from a public key. fn from_public_key(public_key: &[u8; BYTES_LENGTH_32]) -> Result where Self: Sized; - /// Generates a new instance from an existing secret key. - /// - /// Returns a `Result` containing the new instance, or an `Error` if the operation fails. + /// Generates a new key pair from a secret key. + /// A public key will be generated from the secret key. fn from_secret_key(private_key: &[u8; BYTES_LENGTH_32]) -> Result where Self: Sized; } -/// A trait for types that support creating and verifying digital signatures. +/// A trait for types that support signing and verification operations. pub trait CoreSign { - /// Performs a sign operation. - /// - /// Returns a `Result` containing the signature, or an `Error` if the operation fails. + /// Signs the payload with the key pair. fn sign(&self, payload: &[u8]) -> Result, Error>; - /// Performs a verify operation. - /// - /// Returns a `Result` containing `()`, or an `Error` if the operation fails. + /// Verifies the signature of the payload with the key pair. fn verify(&self, payload: &[u8], signature: &[u8]) -> Result<(), Error>; } /// A trait for types that support ECDH key exchange operations. pub trait ECDH { - /// Performs a key exchange operation. - /// - /// Returns an `Option` containing the shared secret, or `None` if the operation fails. + /// Performs ECDH key exchange with the given public key. fn key_exchange(&self, their_public: &Self) -> Option>; } diff --git a/crates/web-plugins/didcomm-messaging/did-utils/src/crypto/utils.rs b/crates/web-plugins/didcomm-messaging/did-utils/src/crypto/utils.rs index 427de550..9517bd37 100644 --- a/crates/web-plugins/didcomm-messaging/did-utils/src/crypto/utils.rs +++ b/crates/web-plugins/didcomm-messaging/did-utils/src/crypto/utils.rs @@ -1,20 +1,7 @@ use super::traits::BYTES_LENGTH_32; -/// Generates a seed for the `ed25519` key pair. -/// -/// If the initial seed is empty or invalid, generates a new seed. -/// -/// # Arguments -/// -/// * `initial_seed` - The initial seed to use, or empty if none. -/// -/// # Returns -/// -/// A `Vec` of bytes of length `BYTES_LENGTH_32`, containing the generated seed. -/// -/// # Errors -/// -/// Returns an error if the initial seed is invalid. +// Generate a seed from an optional initial seed. +// If the initial seed is empty or invalid, generate a random seed. pub(super) fn generate_seed(initial_seed: &[u8]) -> Result<[u8; BYTES_LENGTH_32], &str> { let mut seed = [0u8; BYTES_LENGTH_32]; if initial_seed.is_empty() || initial_seed.len() != BYTES_LENGTH_32 { @@ -28,21 +15,7 @@ pub(super) fn generate_seed(initial_seed: &[u8]) -> Result<[u8; BYTES_LENGTH_32] Ok(seed) } -/// Clones the content of the slice into a new array. -/// -/// It is important to clone the data, as we don't want key material to be hazardously modified. -/// -/// # Arguments -/// -/// * `slice` - The slice to clone. -/// -/// # Returns -/// -/// A new array containing the cloned data. -/// -/// # Panics -/// -/// Panics if the length of the slice is not equal to `BYTES_LENGTH_32`. +// Clone a slice into an array. pub(super) fn clone_slice_to_array(slice: &[u8; BYTES_LENGTH_32]) -> [u8; BYTES_LENGTH_32] { let mut array = [0u8; BYTES_LENGTH_32]; diff --git a/crates/web-plugins/didcomm-messaging/did-utils/src/crypto/x25519.rs b/crates/web-plugins/didcomm-messaging/did-utils/src/crypto/x25519.rs index 0f258039..7b101f26 100644 --- a/crates/web-plugins/didcomm-messaging/did-utils/src/crypto/x25519.rs +++ b/crates/web-plugins/didcomm-messaging/did-utils/src/crypto/x25519.rs @@ -9,6 +9,7 @@ use super::{ AsymmetricKey, }; +/// A wrapper struct for an X25519 asymmetric key pair. pub type X25519KeyPair = AsymmetricKey; impl std::fmt::Debug for X25519KeyPair { @@ -21,20 +22,10 @@ impl std::fmt::Debug for X25519KeyPair { } impl KeyMaterial for X25519KeyPair { - /// Returns the bytes of the public key. - /// - /// # Returns - /// - /// A `Result` containing the bytes of the public key or an `Error`. fn public_key_bytes(&self) -> Result<[u8; BYTES_LENGTH_32], Error> { Ok(clone_slice_to_array(self.public_key.as_bytes())) } - /// Returns the bytes of the private key. - /// - /// # Returns - /// - /// A `Result` containing the bytes of the private key or an `Error`. fn private_key_bytes(&self) -> Result<[u8; BYTES_LENGTH_32], Error> { match &self.secret_key { Some(sk) => Ok(clone_slice_to_array(sk.as_bytes())), @@ -44,32 +35,10 @@ impl KeyMaterial for X25519KeyPair { } impl Generate for X25519KeyPair { - /// Generates a new X25519 key pair. - /// - /// If the initial seed is empty or invalid, a random seed will be generated. - /// - /// # Arguments - /// - /// * `seed` - The initial seed to use, or empty if none. - /// - /// # Returns - /// - /// A new `X25519KeyPair` instance. fn new() -> Result { Self::new_with_seed(vec![].as_slice()) } - /// Generates a new X25519 key pair with a seed. - /// - /// If the seed is empty or invalid, generates a new seed. - /// - /// # Arguments - /// - /// * `seed` - The initial seed to use. - /// - /// # Returns - /// - /// A new `X25519KeyPair` instance. fn new_with_seed(seed: &[u8]) -> Result { match generate_seed(seed) { Ok(secret_seed) => { @@ -83,15 +52,6 @@ impl Generate for X25519KeyPair { } } - /// Creates a new `X25519KeyPair` from a public key. - /// - /// # Arguments - /// - /// * `public_key` - The bytes of the public key. - /// - /// # Returns - /// - /// A new `X25519KeyPair` instance. fn from_public_key(public_key: &[u8; BYTES_LENGTH_32]) -> Result { match public_key.len() { BYTES_LENGTH_32 => { @@ -105,15 +65,6 @@ impl Generate for X25519KeyPair { } } - /// Creates a new `X25519KeyPair` from a secret key. - /// - /// # Arguments - /// - /// * `secret_key` - The bytes of the secret key. - /// - /// # Returns - /// - /// A new `X25519KeyPair` instance. fn from_secret_key(secret_key: &[u8; BYTES_LENGTH_32]) -> Result { match secret_key.len() { BYTES_LENGTH_32 => { @@ -130,16 +81,25 @@ impl Generate for X25519KeyPair { } impl ECDH for X25519KeyPair { - /// Performs a key exchange using the Diffie-Hellman algorithm. + /// Performs an Elliptic Curve Diffie-Hellman (ECDH) key exchange. + /// + /// This method computes a shared secret using the given public key and the private key + /// of this key pair. The shared secret can be used for cryptographic purposes such as + /// deriving encryption keys. + /// + /// # Example /// - /// # Arguments + /// ``` + /// use did_utils::crypto::{X25519KeyPair, ECDH}; /// - /// * `key` - The public key of the other party. + /// let keypair = X25519KeyPair::new()?; + /// let other_keypair = X25519KeyPair::new()?; /// - /// # Returns + /// let shared_secret = keypair.key_exchange(&other_keypair)?; + /// let other_shared_secret = other_keypair.key_exchange(&keypair)?; /// - /// An optional vector of bytes representing the shared secret. - /// If the secret key is not available, returns `None`. + /// assert_eq!(shared_secret, other_shared_secret); + /// ``` fn key_exchange(&self, key: &Self) -> Option> { (self.secret_key).as_ref().map(|x| x.diffie_hellman(&key.public_key).as_bytes().to_vec()) } diff --git a/crates/web-plugins/didcomm-messaging/did-utils/src/didcore.rs b/crates/web-plugins/didcomm-messaging/did-utils/src/didcore.rs index 06b8c09c..bedd3be5 100644 --- a/crates/web-plugins/didcomm-messaging/did-utils/src/didcore.rs +++ b/crates/web-plugins/didcomm-messaging/did-utils/src/didcore.rs @@ -21,59 +21,70 @@ use crate::{jwk::Jwk, ldmodel::Context, proof::Proof}; #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] #[serde(rename_all = "camelCase")] pub struct Document { - // The @context property defines the vocabulary used in the JSON-LD document. - // It provides a way to map the keys in the JSON structure to specific terms, - // properties, and classes from external vocabularies. In the context of a - // DID Document, the @context property is used to define the vocabulary for - // the various properties within the document, such as id, publicKey, service, and others. + /// The @context property defines the vocabulary used in the JSON-LD document. + /// It provides a way to map the keys in the JSON structure to specific terms, + /// properties, and classes from external vocabularies. In the context of a + /// DID Document, the @context property is used to define the vocabulary for + /// the various properties within the document, such as id, publicKey, service, and others. #[serde(rename = "@context")] pub context: Context, // === Identifier === - - // Identifier property is mandatory in a did document. + /// Identifier property is mandatory in a did document. // see https://www.w3.org/TR/did-core/#dfn-id #[serde(default = "String::new")] pub id: String, - // See https://www.w3.org/TR/did-core/#dfn-controller + /// The controller of the DID Document. #[serde(skip_serializing_if = "Option::is_none")] pub controller: Option, - // See https://www.w3.org/TR/did-core/#dfn-alsoknownas + /// Other identifiers of the DID Document. #[serde(skip_serializing_if = "Option::is_none")] pub also_known_as: Option>, // === Verification Methods === #[serde(skip_serializing_if = "Option::is_none")] + /// Express verification methods than can be used to authenticate + /// or authorize interactions with the DID subject. pub verification_method: Option>, // === Verification Relationships === #[serde(skip_serializing_if = "Option::is_none")] + /// Used to specify how DID subject is expected to be authenticated. pub authentication: Option>, #[serde(skip_serializing_if = "Option::is_none")] + /// Used to specify how the DID subject is expected to express claims. pub assertion_method: Option>, #[serde(skip_serializing_if = "Option::is_none")] + /// used to specify a mechanism that might be used by the DID subject + /// to delegate a cryptographic capability to another party. pub capability_delegation: Option>, #[serde(skip_serializing_if = "Option::is_none")] + /// Used to specify a verification method that might be used by + /// the DID subject to invoke a cryptographic capability. pub capability_invocation: Option>, #[serde(skip_serializing_if = "Option::is_none")] + /// Used to specify how an entity can generate encryption material in order + /// to transmit confidential information intended for the DID subject. pub key_agreement: Option>, // === Services === #[serde(skip_serializing_if = "Option::is_none")] + /// Express ways of communicating with the DID subject. pub service: Option>, // === Dynamic Properties === #[serde(skip_serializing_if = "Option::is_none")] #[serde(flatten)] + /// Dynamic properties pub additional_properties: Option>, - // === Proof === + /// The proof of the DID Document. #[serde(skip_serializing_if = "Option::is_none")] pub proof: Option, } diff --git a/crates/web-plugins/didcomm-messaging/did-utils/src/didkit.rs b/crates/web-plugins/didcomm-messaging/did-utils/src/didkit.rs index a6dabe1f..95d8e7d5 100644 --- a/crates/web-plugins/didcomm-messaging/did-utils/src/didkit.rs +++ b/crates/web-plugins/didcomm-messaging/did-utils/src/didkit.rs @@ -6,17 +6,7 @@ use crate::{ }; impl VerificationMethod { - /// Creates a new `VerificationMethod` instance. - /// - /// # Arguments - /// - /// * `id` - A unique identifier for the verification method. - /// * `key_type` - The type of key used in the verification method. - /// * `controller` - The identifier of the controller of the verification method. - /// - /// # Returns - /// - /// * A new instance of `VerificationMethod`. + /// Creates a new `VerificationMethod` instance with only the required members. pub fn new(id: String, key_type: String, controller: String) -> Self { Self { id, @@ -31,17 +21,7 @@ impl VerificationMethod { } impl Service { - /// Creates a new `Service` instance. - /// - /// # Arguments - /// - /// * `id` - A unique identifier for the service. - /// * `service_type` - The type of service. - /// * `service_endpoint` - The endpoint where the service can be accessed. - /// - /// # Returns - /// - /// * A new instance of `Service`. + /// Creates a new `Service` instance with only the required members. pub fn new(id: String, service_type: String, service_endpoint: Value) -> Self { Self { id, @@ -53,16 +33,7 @@ impl Service { } impl Document { - /// Creates a new `Document` instance with only the required members. - /// - /// # Arguments - /// - /// * `context` - The context defining the vocabulary for the document. - /// * `id` - A unique identifier for the DID document. - /// - /// # Returns - /// - /// * A new instance of `Document`. + /// Creates a new `Document` instance with frequent members. pub fn new(context: Context, id: String) -> Self { Self { id, @@ -82,20 +53,6 @@ impl Document { } /// Creates a new `Document` instance with frequent members. - /// - /// # Arguments - /// - /// * `context` - The context defining the vocabulary for the document. - /// * `id` - A unique identifier for the DID document. - /// * `authentication` - An optional vector of `Authentication` methods. - /// * `assertion_method` - An optional vector of `AssertionMethod` methods. - /// * `key_agreement` - An optional vector of `KeyAgreement` methods. - /// * `verification_method` - An optional vector of `VerificationMethod` methods. - /// * `service` - An optional vector of `Service` instances. - /// - /// # Returns - /// - /// * A new instance of `Document`. pub fn new_full( context: Context, id: String, diff --git a/crates/web-plugins/didcomm-messaging/did-utils/src/jwk/mod.rs b/crates/web-plugins/didcomm-messaging/did-utils/src/jwk/mod.rs index 0f8e2481..ca861223 100644 --- a/crates/web-plugins/didcomm-messaging/did-utils/src/jwk/mod.rs +++ b/crates/web-plugins/didcomm-messaging/did-utils/src/jwk/mod.rs @@ -1,35 +1,6 @@ //! This module provides types and utilities for handling JSON Web Keys (JWKs). //! //! It includes support for various key types, secure serialization and deserialization, and encoding schemes. -//! -//! ## Submodules -//! -//! - [`bytes`]: Contains utilities for handling byte sequences, including secure serialization and deserialization using Base64 encoding. -//! - [`ec`]: Provides support for working with elliptic-curve keys. -//! - [`jwk`]: Contains types and utilities for working with JSON Web Keys (JWKs). -//! - [`key`]: Provides generic key types and associated utilities used in JSON Web Keys (JWKs). -//! - [`oct`]: Provides support for working with octet sequence keys. -//! - [`okp`]: Provides support for working with Octet Key Pairs (OKP). -//! - [`prm`]: Defines parameter-related types for keys and cryptographic operations. -//! - [`rsa`]: Provides support for working with RSA keys. -//! - [`secret`]: Provides utilities for working with secrets securely. -//! -//! //! # Examples -//! -//! ```no run -//! # use did_utils::key_jwk::Bytes; -//! # use base64ct::Base64UrlUnpadded; -//! -//! // Creating a Bytes instance from a vector -//! let data = vec![1, 2, 3, 4]; -//! let bytes: Bytes, Base64UrlUnpadded> = Bytes::from(data); -//! -//! // Serializing to a base64 string -//! let serialized = serde_json::to_string(&bytes)?; -//! -//! // Deserializing from a base64 string -//! let deserialized: Bytes, Base64UrlUnpadded> = serde_json::from_str(&serialized)?; -//! ``` mod bytes; mod ec; diff --git a/crates/web-plugins/didcomm-messaging/did-utils/src/ldmodel.rs b/crates/web-plugins/didcomm-messaging/did-utils/src/ldmodel.rs index 025881e7..60849e29 100644 --- a/crates/web-plugins/didcomm-messaging/did-utils/src/ldmodel.rs +++ b/crates/web-plugins/didcomm-messaging/did-utils/src/ldmodel.rs @@ -1,3 +1,5 @@ +//! Provides Linked Data models for representing DIDs and related data. + use serde::{Deserialize, Serialize}; use serde_json::Value; @@ -8,7 +10,10 @@ use serde_json::Value; // It provides a way to map the keys in the JSON structure to specific terms, // properties, and classes from external vocabularies. pub enum Context { + /// A single string value. SingleString(String), + /// A set of string values. SetOfString(Vec), + /// A JSON object. JsonObject(Value), } diff --git a/crates/web-plugins/didcomm-messaging/did-utils/src/lib.rs b/crates/web-plugins/didcomm-messaging/did-utils/src/lib.rs index 06a167ae..5eded9d5 100644 --- a/crates/web-plugins/didcomm-messaging/did-utils/src/lib.rs +++ b/crates/web-plugins/didcomm-messaging/did-utils/src/lib.rs @@ -1,35 +1,16 @@ -//! # did-utils -//! -//! This crate provides a set of utilities for working with Decentralized Identifiers (DIDs). -//! It includes support for cryptographic operations, DID core functionality, key management, proof handling, -//! verifiable credentials, linked data models, and various DID methods. -//! -//! ## Modules -//! -//! - [`crypto`]: Contains cryptographic utilities for key generation, encryption, -//! decryption, signing, and verification. -//! - [`didcore`]: Provides core functionality for DIDs, including parsing and manipulation. -//! - [`didkit`]: Provides high-level functionality for creating and managing DIDs. -//! - [`key_jwk`]: Provides support for JSON Web Key (JWK) representations of keys. -//! - [`proof`]: Handles proof creation and verification. -//! - [`vc`]: Manages Verifiable Credentials, including their creation, signing, and verification. -//! - [`ldmodel`]: Defines Linked Data models for representing DIDs and related data. -//! - [`methods`]: Implements various DID methods. -//! -//! ## Example Usage -//! -//! Below is a simple example of how to create a DID Document: -//! -//! ```rust -//! # use did_utils::didcore::Document; -//! # use did_utils::ldmodel::Context; -//! -//! # fn main() { -//! let context = Context::SetOfString(vec!["https://www.w3.org/ns/did/v1".to_string()]); -//! let did_document = Document::new(context, "did:example:123456".to_string()); -//! println!("{:?}", did_document); -//! # } -//! ``` +/*! # did-utils + +This library provides a set of utilities for working with Decentralized Identifiers (DIDs). +It includes support for cryptographic operations, DID core functionality, key management, proof handling, +verifiable credentials, linked data models, and various DID methods. + +## Features + +- **Cryptographic Operations**: Comprehensive support for cryptographic operations, including key management and digital signatures. +- **DID Support**: Comprehensive support for various DID methods, enabling decentralized identity management. +- **Verifiable Credentials**: Tools for creating, managing, and verifying verifiable credentials. + +*/ pub mod crypto; pub mod didcore; pub mod didkit; diff --git a/crates/web-plugins/didcomm-messaging/did-utils/src/methods/errors.rs b/crates/web-plugins/didcomm-messaging/did-utils/src/methods/errors.rs index a616aa00..daba60b6 100644 --- a/crates/web-plugins/didcomm-messaging/did-utils/src/methods/errors.rs +++ b/crates/web-plugins/didcomm-messaging/did-utils/src/methods/errors.rs @@ -53,6 +53,7 @@ pub enum DIDResolutionError { NonSuccessResponse, } +/// Error type for DID Web method #[derive(Error, Debug)] #[non_exhaustive] pub enum DidWebError { @@ -72,6 +73,7 @@ pub enum DidWebError { Other(#[from] Box), } +/// Parsing error source #[derive(Error, Debug)] pub enum ParsingErrorSource { #[error("JSON parsing error: {0}")] diff --git a/crates/web-plugins/didcomm-messaging/did-utils/src/methods/key/method.rs b/crates/web-plugins/didcomm-messaging/did-utils/src/methods/key/method.rs index 7a4fce5e..2fa1f14f 100644 --- a/crates/web-plugins/didcomm-messaging/did-utils/src/methods/key/method.rs +++ b/crates/web-plugins/didcomm-messaging/did-utils/src/methods/key/method.rs @@ -10,6 +10,7 @@ use crate::{ methods::{errors::DIDResolutionError, traits::DIDMethod}, }; +/// A struct for resolving DID Key documents. #[derive(Default)] pub struct DidKey { /// Key format to consider during DID expansion into a DID document @@ -39,13 +40,41 @@ impl DidKey { } } - /// Generates did:key address ex nihilo, off self-generated Ed25519 key pair + /// Generates a new DID key from an Ed25519 key pair. + /// + /// This function creates a new Ed25519 key pair and converts it into a DID key. + /// + /// # Example + /// + /// ```rust + /// use did_utils::methods::DidKey; + /// + /// # fn example() -> Result<(), did_utils::crypto::Error> { + /// let did_key = DidKey::generate()?; + /// # Ok(()) + /// # } + /// ``` pub fn generate() -> Result { let keypair = Ed25519KeyPair::new()?; Self::from_ed25519_keypair(&keypair) } - /// Computes did:key address corresponding to Ed25519 key pair + /// Converts an Ed25519 key pair into a DID key. + /// + /// This function takes an existing Ed25519 key pair and returns the corresponding DID key. + /// + /// # Example + /// + /// ```rust + /// use did_utils::crypto::{Ed25519KeyPair, Generate}; + /// use did_utils::methods::DidKey; + /// + /// # fn example() -> Result<(), did_utils::crypto::Error> { + /// let keypair = Ed25519KeyPair::new()?; + /// let did_key = DidKey::from_ed25519_keypair(&keypair)?; + /// # Ok(()) + /// # } + /// ``` pub fn from_ed25519_keypair(keypair: &Ed25519KeyPair) -> Result { let multibase_value = multibase::encode( Base58Btc, @@ -55,7 +84,26 @@ impl DidKey { Ok(format!("did:key:{}", multibase_value)) } - /// Computes did:key address corresponding to raw public key bytes + /// Converts a raw public key into a DID key. + /// + /// This function takes a raw public key and an algorithm type, and returns the corresponding DID key. + /// + /// # Example + /// + /// ```rust + /// use did_utils::methods::DidKey; + /// use did_utils::methods::Algorithm; + /// + /// # fn example() -> Result<(), did_utils::crypto::Error> { + /// let bytes = [0u8; 32]; + /// let did_key = DidKey::from_raw_public_key(Algorithm::Ed25519, &bytes)?; + /// # Ok(()) + /// # } + /// ``` + /// + /// # Errors + /// + /// This function returns an error if the length of the raw public key does not match the algorithm's expected length. pub fn from_raw_public_key(alg: Algorithm, bytes: &[u8]) -> Result { if let Some(required_length) = alg.public_key_length() { if required_length != bytes.len() { @@ -68,7 +116,7 @@ impl DidKey { Ok(format!("did:key:{}", multibase_value)) } - /// Expands did:key address into DID document + /// Expands `did:key` address into DID document /// /// See [Create a did key](https://w3c-ccg.github.io/did-method-key/#create) pub fn expand(&self, did: &str) -> Result { diff --git a/crates/web-plugins/didcomm-messaging/did-utils/src/methods/key/resolver.rs b/crates/web-plugins/didcomm-messaging/did-utils/src/methods/key/resolver.rs index f19ba8ab..6242ea1e 100644 --- a/crates/web-plugins/didcomm-messaging/did-utils/src/methods/key/resolver.rs +++ b/crates/web-plugins/didcomm-messaging/did-utils/src/methods/key/resolver.rs @@ -1,25 +1,34 @@ -use async_trait::async_trait; - use crate::{ ldmodel::Context, - methods::{errors::DIDResolutionError, traits::DIDResolver}, + methods::{ + errors::DIDResolutionError, + resolution::{DIDResolutionMetadata, DIDResolutionOptions, MediaType, ResolutionOutput}, + traits::DIDResolver, + DidKey, + }, }; - -use crate::methods::resolution::{DIDResolutionMetadata, DIDResolutionOptions, MediaType, ResolutionOutput}; - -use crate::methods::DidKey; +use async_trait::async_trait; #[async_trait] impl DIDResolver for DidKey { - /// Resolves a DID using the did:key method. - /// - /// # Arguments + /// Resolves a `did:key` address to a DID document. /// - /// * `did` - The DID address to resolve. + /// # Example /// - /// # Returns + /// ``` + /// use did_utils::methods::{DIDResolver, DidKey, DIDResolutionOptions}; + /// # use did_utils::crypto::Error; /// - /// A `ResolutionOutput` struct containing the resolved DID document and metadata. + /// # async fn example_resolve_did_key() -> Result<(), Error> { + /// // create new key did resolver + /// let did_key_resolver = DidKey::new(); + /// // generate a new did:key + /// let did = DidKey::generate()?; + /// // resolve the did + /// let output = did_key_resolver.resolve(&did, &DIDResolutionOptions::default()).await; + /// # Ok(()) + /// # } + /// ``` async fn resolve(&self, did: &str, _options: &DIDResolutionOptions) -> ResolutionOutput { let context = Context::SingleString(String::from("https://w3id.org/did-resolution/v1")); diff --git a/crates/web-plugins/didcomm-messaging/did-utils/src/methods/mod.rs b/crates/web-plugins/didcomm-messaging/did-utils/src/methods/mod.rs index 50f92875..95435599 100644 --- a/crates/web-plugins/didcomm-messaging/did-utils/src/methods/mod.rs +++ b/crates/web-plugins/didcomm-messaging/did-utils/src/methods/mod.rs @@ -1,20 +1,39 @@ //! A collection of methods for DID resolution and related utilities. //! -//! This module provides functionality for resolving Decentralized Identifiers (DIDs) -//! using different DID methods including [`did:key`], [`did:web`], and [`did:peer`]. +//! This module provides functionality for creating and resolving Decentralized Identifiers (DIDs) +//! using different DID methods including [did:key], [did:web], and [did:peer]. //! -//! [`did:key`]: https://w3c-ccg.github.io/did-method-key/ -//! [`did:web`]: https://w3c-ccg.github.io/did-method-web/ -//! [`did:peer`]: https://identity.foundation/peer-did-method-spec/ +//! # DID Methods //! -//! # Examples +//! The following DID methods are currently supported: //! -//! ### Basic did:key resolution example. +//! ## did:key //! -//! ``` -//! use did_utils::methods::{DIDResolver, DidKey}; -//! use did_utils::methods::DIDResolutionOptions; +//! The [did:key] method is the simplest possible implementation of a DID Method that is able to achieve many, +//! but not all, of the benefits of utilizing DIDs. +//! +//! ## did:web +//! +//! The [did:web] is a DID method that uses the web domain's existing reputation to create and manage DIDs. +//! +//! ## did:peer +//! +//! The [did:peer] DID method is designed to be used independent of any central source of truth, +//! and is intended to be cheap, fast, scalable, and secure. It is suitable for most private +//! relationships between people, organizations, and things. +//! +//! [did:key]: https://w3c-ccg.github.io/did-method-key/ +//! [did:web]: https://w3c-ccg.github.io/did-method-web/ +//! [did:peer]: https://identity.foundation/peer-did-method-spec/ //! +//! # Basic Usage +//! +//! Basic `did:key` resolution example +//! +//! ``` +//! # use did_utils::methods::{DIDResolver, DidKey}; +//! # use did_utils::methods::DIDResolutionOptions; +//! # //! # async fn test_did_key() { //! let did_key_resolver = DidKey::new(); //! let did = "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"; @@ -22,12 +41,12 @@ //! # } //! ``` //! -//! ### An example demonstrating a basic usage of did:web +//! An example demonstrating a basic usage of `did:web` //! //! ``` -//! use did_utils::methods::{DIDResolver, DidWeb}; -//! use did_utils::methods::DIDResolutionOptions; -//! +//! # use did_utils::methods::{DIDResolver, DidWeb}; +//! # use did_utils::methods::DIDResolutionOptions; +//! # //! # async fn resolves_did_web_document() { //! let port = 3000; //! let host = "localhost"; @@ -36,27 +55,21 @@ //! let did: &str = &formatted_string; //! //! let did_web_resolver = DidWeb::new(); -//! let output = did_web_resolver.resolve( -//! did, -//! &DIDResolutionOptions::default() -//! ).await; +//! let output = did_web_resolver.resolve(did, &DIDResolutionOptions::default()).await; //! # } //! ``` //! -//! ### An example demonstrating a basic usage of did:peer +//! An example demonstrating a basic usage of `did:peer` //! //! ``` -//! use did_utils::methods::{DIDResolver, DidPeer}; -//! use did_utils::methods::DIDResolutionOptions; -//! +//! # use did_utils::methods::{DIDResolver, DidPeer}; +//! # use did_utils::methods::DIDResolutionOptions; +//! # //! # async fn resolves_did_peer_document() { //! let did = "did:peer:0z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"; //! //! let did_peer_resolver = DidPeer::new(); -//! let output = did_peer_resolver.resolve( -//! did, -//! &DIDResolutionOptions::default() -//! ).await; +//! let output = did_peer_resolver.resolve(did, &DIDResolutionOptions::default()).await; //! # } //! ``` diff --git a/crates/web-plugins/didcomm-messaging/did-utils/src/methods/peer/method.rs b/crates/web-plugins/didcomm-messaging/did-utils/src/methods/peer/method.rs index a6200075..c9c80c5e 100644 --- a/crates/web-plugins/didcomm-messaging/did-utils/src/methods/peer/method.rs +++ b/crates/web-plugins/didcomm-messaging/did-utils/src/methods/peer/method.rs @@ -24,12 +24,14 @@ lazy_static::lazy_static!( const MULTICODEC_JSON: [u8; 2] = [0x80, 0x04]; +/// A struct representing the `did:peer` method #[derive(Default)] pub struct DidPeer { /// Key format to consider during DID expansion into a DID document key_format: PublicKeyFormat, } +/// The purpose of a key in the deed:peer:4 creation #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub enum Purpose { @@ -41,6 +43,7 @@ pub enum Purpose { Service, } +/// A public key with associated purpose in the deed:peer:4 creation #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct PurposedKey { @@ -96,27 +99,82 @@ impl DidPeer { // Generating did:peer addresses // --------------------------------------------------------------------------- - /// Method 0: Generates did:peer address from ed25519 inception key without doc + /// Generates `did:peer:0` address from ed25519 [inception key without doc][ikwd]. /// - /// See https://identity.foundation/peer-did-method-spec/#method-0-inception-key-without-doc + /// [ikwd]: https://identity.foundation/peer-did-method-spec/#method-0-inception-key-without-doc + /// + /// # Example + /// + /// ``` + /// use did_utils::methods::DidPeer; + /// use did_utils::crypto::{Ed25519KeyPair, Generate}; + /// # use did_utils::methods::DIDResolutionError; + /// + /// # async fn test_did_peer() -> Result { + /// let keypair = Ed25519KeyPair::new().unwrap(); + /// let did = DidPeer::create_did_peer_0_from_ed25519_keypair(&keypair)?; + /// # Ok(did) + /// # } + /// ``` pub fn create_did_peer_0_from_ed25519_keypair(keypair: &Ed25519KeyPair) -> Result { let did_key = DidKey::from_ed25519_keypair(keypair)?; Ok(did_key.replace("did:key:", "did:peer:0")) } - /// Method 0: Generates did:peer address from inception key without doc + /// Generates `did:peer:0` address from [inception key without doc][ik]. + /// + /// [ik]: https://identity.foundation/peer-did-method-spec/#method-0-inception-key-without-doc + /// + /// # Example + /// + /// ``` + /// use did_utils::methods::DidPeer; + /// use did_utils::methods::Algorithm; + /// # use did_utils::methods::DIDResolutionError; /// - /// See https://identity.foundation/peer-did-method-spec/#method-0-inception-key-without-doc + /// # fn example() -> Result { + /// // used just to show the example + /// let public_key = [0u8; 32]; + /// // create did:peer:0 address + /// let did = DidPeer::create_did_peer_0_from_raw_public_key(Algorithm::Ed25519, &public_key)?; + /// # Ok(did) + /// # } pub fn create_did_peer_0_from_raw_public_key(alg: Algorithm, bytes: &[u8]) -> Result { let did_key = DidKey::from_raw_public_key(alg, bytes)?; Ok(did_key.replace("did:key:", "did:peer:0")) } - /// Method 1: Generates did:peer address from DID document + /// Generates `did:peer:1` address from a [genesis DID document][gd]. /// - /// See https://identity.foundation/peer-did-method-spec/#method-1-genesis-doc + /// [gd]: https://identity.foundation/peer-did-method-spec/#method-1-genesis-doc + /// + /// # Example + /// + /// ``` + /// use did_utils::methods::DidPeer; + /// # use did_utils::methods::DIDResolutionError; + /// # use did_utils::didcore::Document as DIDDocument; + /// # use did_utils::didcore::VerificationMethod; + /// # use did_utils::didcore::Authentication; + /// + /// # fn example() -> Result { + /// // example DID document + /// let store_variant = DIDDocument { + /// verification_method: Some(vec![ + /// VerificationMethod { id: "#key-0".to_string(), ..Default::default() }, + /// ]), + /// authentication: Some(vec![ + /// Authentication::Reference("#key-0".to_string()), + /// ]), + /// ..Default::default() + /// }; + /// // create did:peer:1 address + /// let did = DidPeer::create_did_peer_1_from_stored_variant(&store_variant)?; + /// # Ok(did) + /// # } + /// ``` pub fn create_did_peer_1_from_stored_variant(diddoc: &DIDDocument) -> Result { if !diddoc.id.is_empty() { return Err(DIDPeerMethodError::InvalidStoredVariant); @@ -128,9 +186,31 @@ impl DidPeer { Ok(format!("did:peer:1{multihash}")) } - /// Method 2: Generates did:peer address from multiple inception key + /// Generates `did:peer:2` address from [multiple inception key][mik] + /// + /// [mik]: https://identity.foundation/peer-did-method-spec/#method-2-multiple-inception-key-without-doc + /// + /// # Example + /// + /// ``` + /// use did_utils::methods::{DidPeer, Purpose, PurposedKey}; + /// # use did_utils::methods::DIDResolutionError; /// - /// See https://identity.foundation/peer-did-method-spec/#method-2-multiple-inception-key-without-doc + /// # fn example() -> Result { + /// let keys = vec![ + /// PurposedKey { + /// purpose: Purpose::Verification, + /// public_key_multibase: String::from("z6Mkj3PUd1WjvaDhNZhhhXQdz5UnZXmS7ehtx8bsPpD47kKc"), + /// }, + /// PurposedKey { + /// purpose: Purpose::Encryption, + /// public_key_multibase: String::from("z6LSg8zQom395jKLrGiBNruB9MM6V8PWuf2FpEy4uRFiqQBR"), + /// }, + /// ]; + /// // Create the did:peer:2 address + /// let did = DidPeer::create_did_peer_2(&keys, &[])?; + /// # Ok(did) + /// # } pub fn create_did_peer_2(keys: &[PurposedKey], services: &[Service]) -> Result { if keys.is_empty() && services.is_empty() { return Err(DIDPeerMethodError::EmptyArguments); @@ -159,9 +239,23 @@ impl DidPeer { Ok(format!("did:peer:2{}", chain.join(""))) } - /// Method 3: DID Shortening with SHA-256 Hash + /// Generates `did:peer:3` address by [shortening did:peer:2][dsh] with SHA-256 Hash /// - /// See https://identity.foundation/peer-did-method-spec/#method-3-did-shortening-with-sha-256-hash + /// [dsh]: https://identity.foundation/peer-did-method-spec/#method-3-did-shortening-with-sha-256-hash + /// + /// # Example + /// + /// ``` + /// use did_utils::methods::DidPeer; + /// # use did_utils::methods::DIDResolutionError; + /// + /// # fn example() -> Result { + /// let did_peer_2 = "did:peer:2.Vz6Mkj3PUd1WjvaDhNZhhhXQdz5UnZXmS7ehtx8bsPpD47kKc"; + /// // Create the did:peer:3 address + /// let did = DidPeer::create_did_peer_3(did_peer_2)?; + /// # Ok(did) + /// # } + /// ``` pub fn create_did_peer_3(did: &str) -> Result { let stripped = match did.strip_prefix("did:peer:2") { Some(stripped) => stripped, @@ -174,9 +268,35 @@ impl DidPeer { Ok(format!("did:peer:3{multihash}")) } - /// Method 4: Generates did:peer address from DID document (embedding long form) + /// Generates `did:peer:4` address from DID document ([embedding long form][elf]) + /// + /// [elf]: https://identity.foundation/peer-did-method-spec/#method-4-short-form-and-long-form + /// + /// # Example + /// + /// ``` + /// use did_utils::methods::DidPeer; + /// # use did_utils::methods::DIDResolutionError; + /// # use did_utils::didcore::Document as DIDDocument; + /// # use did_utils::didcore::VerificationMethod; + /// # use did_utils::didcore::Authentication; /// - /// See https://identity.foundation/peer-did-method-spec/#method-4-short-form-and-long-form + /// # fn example() -> Result { + /// // example DID document + /// let store_variant = DIDDocument { + /// verification_method: Some(vec![ + /// VerificationMethod { id: "#key-0".to_string(), ..Default::default() }, + /// ]), + /// authentication: Some(vec![ + /// Authentication::Reference("#key-0".to_string()), + /// ]), + /// ..Default::default() + /// }; + /// // create did:peer:4 address + /// let did = DidPeer::create_did_peer_4_from_stored_variant(&store_variant)?; + /// # Ok(did) + /// # } + /// ``` pub fn create_did_peer_4_from_stored_variant(diddoc: &DIDDocument) -> Result { // Validate input documment validate_input_document(diddoc)?; @@ -191,9 +311,32 @@ impl DidPeer { Ok(format!("did:peer:4{hash}:{encoded}")) } - /// Method 4: DID shortening for did:peer:4 addresses + /// [DID shortening][dsh] for `did:peer:4` addresses /// - /// See https://identity.foundation/peer-did-method-spec/#method-4-short-form-and-long-form + /// [dsh]: https://identity.foundation/peer-did-method-spec/#method-4-short-form-and-long-form + /// + /// # Example + /// + /// ``` + /// use did_utils::methods::DidPeer; + /// # use did_utils::methods::DIDResolutionError; + /// + /// # fn example() -> Result { + /// let did_peer_4_long_form = concat!( + /// "did:peer:4zQmePYVawceZsPSxpLRp54z4Q5DCZXeyyGKwoDMc2NqgZXZ:z2yS424R5nAoSu", + /// "CezPTvBHybrvByZRD9g8L4oMe4ctq9UwPksVskxJFiars33RRyKz3z7RbwwQRAo9ByoXmBhg", + /// "7UCMkvmSHBeXWF44tQJfLjiXieCtXgxASzPJ5UsgPLAWX2vdjNFfmiLVh1WLe3RdBPvQoMuM", + /// "EiPLFGiKhbzX66dT21qDwZusRC4uDzQa7XpsLBS7rBjZZ9sLMRzjpG4rYpjgLUmUF2D1ixeW", + /// "ZFMqy7fVfPUUGyt4N6R4aLAjMLgcJzAQKb1uFiBYe2ZCTmsjtazWkHypgJetLysv7AwasYDV", + /// "4MMNPY5AbM4p3TGtdpJZaxaXzSKRZexuQ4tWsfGuHXEDiaABj5YtjbNjWh4f5M4sn7D9AAAS", + /// "StG593VkLFaPxG4VnFR4tKPiWeN9AJXRWPQ2XRnsD7U3mCHpRSb2f1HT5KeSHTU8zNAn6vFc", + /// "4fstgf2j71Uo8tngcUBkxdqkHKmpvZ1Fs27sWh7JvWAeiehsW3aBe4CbU4WGjzmusaKVb2HS", + /// "7iY5hbYngYrpwcZ5Sse"); + /// // create short-form did:peer:4 address + /// let did = DidPeer::shorten_did_peer_4(did_peer_4_long_form)?; + /// # Ok(did) + /// # } + /// ``` pub fn shorten_did_peer_4(did: &str) -> Result { let stripped = match did.strip_prefix("did:peer:4") { Some(stripped) => stripped, @@ -234,9 +377,9 @@ impl DidPeer { } } - /// Expands did:peer:0 address - /// - /// See https://identity.foundation/peer-did-method-spec/#method-0-inception-key-without-doc + // Expands did:peer:0 address + // + // See https://identity.foundation/peer-did-method-spec/#method-0-inception-key-without-doc pub fn expand_did_peer_0(&self, did: &str) -> Result { if !DID_PEER_0_REGEX.is_match(did) { return Err(DIDPeerMethodError::RegexMismatch); @@ -333,9 +476,9 @@ impl DidPeer { self.derive_verification_method(did, &enc_multikey, alg, enc_key) } - /// Expands did:peer:2 address - /// - /// See https://identity.foundation/peer-did-method-spec/#resolving-a-didpeer2 + // Expands did:peer:2 address + // + // See https://identity.foundation/peer-did-method-spec/#resolving-a-didpeer2 pub fn expand_did_peer_2(&self, did: &str) -> Result { if !DID_PEER_2_REGEX.is_match(did) { return Err(DIDPeerMethodError::RegexMismatch); @@ -465,9 +608,9 @@ impl DidPeer { Ok(diddoc) } - /// Expands did:peer:4 address - /// - /// See https://identity.foundation/peer-did-method-spec/#resolving-a-did + // Expands did:peer:4 address + // + // See https://identity.foundation/peer-did-method-spec/#resolving-a-did pub fn expand_did_peer_4(&self, did: &str) -> Result { // Ensure long format by computing did:peer:4 short form alias // This also ensures that the hash is valid before shortening the did. diff --git a/crates/web-plugins/didcomm-messaging/did-utils/src/methods/peer/resolver.rs b/crates/web-plugins/didcomm-messaging/did-utils/src/methods/peer/resolver.rs index 1a525c00..c7347bf4 100644 --- a/crates/web-plugins/didcomm-messaging/did-utils/src/methods/peer/resolver.rs +++ b/crates/web-plugins/didcomm-messaging/did-utils/src/methods/peer/resolver.rs @@ -12,7 +12,21 @@ use crate::{ #[async_trait] impl DIDResolver for DidPeer { - /// Resolves a DID address into its corresponding DID document. + /// Resolves a `did:peer` address to a DID document. + /// + /// # Example + /// + /// ``` + /// use did_utils::methods::{DIDResolver, DidPeer, DIDResolutionOptions}; + /// + /// # async fn example_resolve_did_peer() { + /// // create new peer did resolver + /// let did_peer_resolver = DidPeer::new(); + /// let did = "did:peer:2.Vz6Mkj3PUd1WjvaDhNZhhhXQdz5UnZXmS7ehtx8bsPpD47kKc"; + /// // resolve the did + /// let output = did_peer_resolver.resolve(did, &DIDResolutionOptions::default()).await; + /// # } + /// ``` async fn resolve(&self, did: &str, _options: &DIDResolutionOptions) -> ResolutionOutput { let context = Context::SingleString(String::from("https://w3id.org/did-resolution/v1")); diff --git a/crates/web-plugins/didcomm-messaging/did-utils/src/methods/resolution.rs b/crates/web-plugins/didcomm-messaging/did-utils/src/methods/resolution.rs index 8ffe7afb..f89ddbd8 100644 --- a/crates/web-plugins/didcomm-messaging/did-utils/src/methods/resolution.rs +++ b/crates/web-plugins/didcomm-messaging/did-utils/src/methods/resolution.rs @@ -14,19 +14,23 @@ use crate::{didcore::Document as DIDDocument, ldmodel::Context, methods::errors: /// Formerly known as "DID resolution input metadata", they provide /// additional configuration for the DID resolution process. /// -/// See `` +/// See [did resolution options][dro] +/// +/// [dro]: https://www.w3.org/TR/did-core/#did-resolution-options #[derive(Debug, Serialize, Deserialize, Clone, Default)] #[serde(rename_all = "camelCase")] pub struct DIDResolutionOptions { // See https://www.w3.org/TR/did-spec-registries/#accept #[serde(skip_serializing_if = "Option::is_none")] + /// Accept header pub accept: Option, // See https://w3c.github.io/did-resolution/#caching #[serde(skip_serializing_if = "Option::is_none")] + /// Caching control pub no_cache: Option, - // Dynamic properties #[serde(skip_serializing_if = "Option::is_none")] #[serde(flatten)] + /// Dynamic properties pub additional_properties: Option>, } @@ -36,35 +40,47 @@ pub struct DIDResolutionOptions { #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(rename_all = "camelCase")] pub struct ResolutionOutput { - // The @context property defines the vocabulary used in the JSON-LD document. - // It provides a way to map the keys in the JSON structure to specific terms, - // properties, and classes from external vocabularies. + /// The @context property defines the vocabulary used in the JSON-LD document. + /// It provides a way to map the keys in the JSON structure to specific terms, + /// properties, and classes from external vocabularies. #[serde(rename = "@context")] pub context: Context, - // See https://www.w3.org/TR/did-core/#dfn-diddocument + /// See [did document] + /// + /// [did document]: https://www.w3.org/TR/did-core/#dfn-diddocument pub did_document: Option, - // See https://www.w3.org/TR/did-core/#dfn-didresolutionmetadata + /// See [did resolution metadata][drm] + /// + /// [drm]: https://www.w3.org/TR/did-core/#dfn-didresolutionmetadata pub did_resolution_metadata: Option, - // See https://www.w3.org/TR/did-core/#dfn-diddocumentmetadata + /// See [did document metadata][ddm] + /// + /// [ddm]: https://www.w3.org/TR/did-core/#dfn-diddocumentmetadata pub did_document_metadata: Option, - // Dynamic properties + /// Dynamic properties #[serde(flatten)] pub additional_properties: Option>, } /// DID Resolution Metadata. /// -/// See `` +/// See [did document metadata][ddm] +/// +/// [ddm]: https://www.w3.org/TR/did-core/#dfn-diddocumentmetadata #[derive(Debug, Serialize, Deserialize, Clone, Default)] #[serde(rename_all = "camelCase")] pub struct DIDResolutionMetadata { - // See https://www.w3.org/TR/did-spec-registries/#error + /// See [did resolution error][dre] + /// + /// [dre]: https://www.w3.org/TR/did-spec-registries/#error #[serde(skip_serializing_if = "Option::is_none")] pub error: Option, #[serde(skip_serializing_if = "Option::is_none")] - // See https://www.w3.org/TR/did-spec-registries/#contenttype + /// See [content type][ct] + /// + /// [ct]: https://www.w3.org/TR/did-spec-registries/#contenttype pub content_type: Option, - // Dynamic properties + /// Dynamic properties #[serde(skip_serializing_if = "Option::is_none")] #[serde(flatten)] pub additional_properties: Option>, @@ -72,73 +88,83 @@ pub struct DIDResolutionMetadata { /// DID Document Metadata. /// -/// See `` +/// See [did document metadata][ddm] +/// +/// [ddm]: https://www.w3.org/TR/did-core/#dfn-diddocumentmetadata #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)] #[serde(rename_all = "camelCase")] pub struct DIDDocumentMetadata { - // See https://www.w3.org/TR/did-spec-registries/#created + /// See [created] section in the DID specification registry + /// + /// [created]: https://www.w3.org/TR/did-spec-registries/#created #[serde(skip_serializing_if = "Option::is_none")] pub created: Option>, - // See https://www.w3.org/TR/did-spec-registries/#updated + /// See [updated] section in the DID specification registry + /// + /// [updated]: https://www.w3.org/TR/did-spec-registries/#updated #[serde(skip_serializing_if = "Option::is_none")] pub updated: Option>, - // See https://www.w3.org/TR/did-spec-registries/#deactivated + /// See [deactivated] section in the DID specification registry + /// + /// [deactivated]: https://www.w3.org/TR/did-spec-registries/#deactivated #[serde(skip_serializing_if = "Option::is_none")] pub deactivated: Option, - // See https://www.w3.org/TR/did-spec-registries/#next_update + /// See [next_update] section in the DID specification registry + /// + /// [next_update]: https://www.w3.org/TR/did-spec-registries/#next_update #[serde(skip_serializing_if = "Option::is_none")] pub next_update: Option>, - // See https://www.w3.org/TR/did-spec-registries/#version_id + /// See [version_id] section in the DID specification registry + /// + /// [version_id]: https://www.w3.org/TR/did-spec-registries/#version_id #[serde(skip_serializing_if = "Option::is_none")] pub version_id: Option, - // See https://www.w3.org/TR/did-spec-registries/#next_version_id + /// See [next_version_id] section in the DID specification registry + /// + /// [next_version_id]: https://www.w3.org/TR/did-spec-registries/#next_version_id #[serde(skip_serializing_if = "Option::is_none")] pub next_version_id: Option, - // See https://www.w3.org/TR/did-spec-registries/#equivalent_id + /// See [equivalent_id] section in the DID specification registry + /// + /// [equivalent_id]: https://www.w3.org/TR/did-spec-registries/#equivalent_id #[serde(skip_serializing_if = "Vec::is_empty")] pub equivalent_id: Vec, - // See https://www.w3.org/TR/did-spec-registries/#canonical_id + /// See [canonical_id] section in the DID specification registry + /// + /// [canonical_id]: https://www.w3.org/TR/did-spec-registries/#canonical_id #[serde(skip_serializing_if = "Option::is_none")] pub canonical_id: Option, - // Dynamic properties + /// Dynamic properties #[serde(skip_serializing_if = "Option::is_none")] #[serde(flatten)] pub additional_properties: Option>, } /// DID URL Dereferencing Options. -/// -/// See `` pub type DereferencingOptions = DIDResolutionOptions; /// DID URL Dereferencing Metadata. -/// -/// See `` pub type DereferencingMetadata = DIDResolutionMetadata; /// Content Metadata. -/// -/// See `` pub type ContentMetadata = DIDDocumentMetadata; /// Dereferencing Output. -/// -/// See `` #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(rename_all = "camelCase")] pub struct DereferencingOutput { - // The @context property defines the vocabulary used in the JSON-LD document. - // It provides a way to map the keys in the JSON structure to specific terms, - // properties, and classes from external vocabularies. + /// The @context property defines the vocabulary used in the JSON-LD document. + /// It provides a way to map the keys in the JSON structure to specific terms, + /// properties, and classes from external vocabularies. #[serde(rename = "@context")] pub context: Context, - // See https://www.w3.org/TR/did-core/#dfn-diddocument + /// The content of the dereferenced document pub content: Option, - // See https://www.w3.org/TR/did-core/#did-url-dereferencing-metadata + /// The dereferencing metadata pub dereferencing_metadata: Option, - // See https://www.w3.org/TR/did-core/#dfn-diddocumentmetadata + /// The content metadata pub content_metadata: Option, - // Dynamic properties + /// Dynamic properties #[serde(flatten)] pub additional_properties: Option>, } @@ -159,8 +185,11 @@ pub enum Content { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[non_exhaustive] pub enum MediaType { + /// JSON Json, + /// DID JSON DidJson, + /// DID LD JSON DidLdJson, } diff --git a/crates/web-plugins/didcomm-messaging/did-utils/src/methods/traits.rs b/crates/web-plugins/didcomm-messaging/did-utils/src/methods/traits.rs index b8e1c87a..5f8e7c73 100644 --- a/crates/web-plugins/didcomm-messaging/did-utils/src/methods/traits.rs +++ b/crates/web-plugins/didcomm-messaging/did-utils/src/methods/traits.rs @@ -27,7 +27,7 @@ pub trait DIDMethod: DIDResolver { /// Abstract contract for DID resolution. /// -/// [See DID Resolution Specification](https://w3c.github.io/did-resolution) +/// See [DID Resolution Specification](https://w3c.github.io/did-resolution) #[async_trait] pub trait DIDResolver { /// Resolves a DID address into its corresponding DID document. diff --git a/crates/web-plugins/didcomm-messaging/did-utils/src/methods/web/resolver.rs b/crates/web-plugins/didcomm-messaging/did-utils/src/methods/web/resolver.rs index d96397cb..c33fe21d 100644 --- a/crates/web-plugins/didcomm-messaging/did-utils/src/methods/web/resolver.rs +++ b/crates/web-plugins/didcomm-messaging/did-utils/src/methods/web/resolver.rs @@ -49,18 +49,7 @@ impl DidWeb where C: Connect + Send + Sync + Clone + 'static, { - /// Fetches a DID document from the given URL. - /// - /// This method performs an HTTP GET request to the provided URL - /// and attempts to returns the response body as a string. - /// - /// # Arguments - /// - /// * `url` - The URL to fetch the DID document from. - /// - /// # Returns - /// - /// A `Result` containing the DID document as a string or a `DidWebError`. + /// Fetches a DID document from the given URL async fn fetch_did_document(&self, url: Uri) -> Result { let res = self.client.get(url).await?; @@ -74,18 +63,6 @@ where } /// Fetches and parses a DID document for the given DID. - /// - /// This method first parses the DID Web URL format from the given DID and then constructs - /// an URI based on the scheme, domain name, and path. It then fetches the DID document and - /// parses the response body. - /// - /// # Arguments - /// - /// * `did` - The DID to resolve. - /// - /// # Returns - /// - /// A `Result` containing the resolved `DIDDocument` or a `DidWebError`. async fn resolver_fetcher(&self, did: &str) -> Result { let (path, domain_name) = parse_did_web_url(did).map_err(|err| DidWebError::RepresentationNotSupported(err.to_string()))?; @@ -112,14 +89,6 @@ where } /// Parses a DID Web URL and returns the path and domain name. -/// -/// # Arguments -/// -/// * `did` - The DID to parse. -/// -/// # Returns -/// -/// A `Result` containing the path and domain name or a `DidWebError`. fn parse_did_web_url(did: &str) -> Result<(String, String), DidWebError> { let mut parts = did.split(':').peekable(); let domain_name = match (parts.next(), parts.next(), parts.next()) { @@ -144,16 +113,21 @@ impl DIDResolver for DidWeb where C: Connect + Send + Sync + Clone + 'static, { - /// Resolves a DID to a DID document. - /// - /// # Arguments + /// Resolves a `did:web` address to a DID document. /// - /// * `did` - The DID to resolve. - /// * `_options` - The options for DID resolution. + /// # Example /// - /// # Returns + /// ``` + /// use did_utils::methods::{DIDResolver, DidWeb, DIDResolutionOptions}; /// - /// A `ResolutionOutput` containing the resolved DID document and metadata. + /// # async fn example_resolve_did_web() { + /// // create new web did resolver + /// let did_web_resolver = DidWeb::new(); + /// let did = "did:web:example.com"; + /// // resolve the did + /// let output = did_web_resolver.resolve(did, &DIDResolutionOptions::default()).await; + /// # } + /// ``` async fn resolve(&self, did: &str, _options: &DIDResolutionOptions) -> ResolutionOutput { let context = Context::SingleString(String::from("https://w3id.org/did-resolution/v1")); diff --git a/crates/web-plugins/didcomm-messaging/did-utils/src/proof/eddsa_jcs_2022.rs b/crates/web-plugins/didcomm-messaging/did-utils/src/proof/eddsa_jcs_2022.rs index 67bd1f07..c70a24cc 100644 --- a/crates/web-plugins/didcomm-messaging/did-utils/src/proof/eddsa_jcs_2022.rs +++ b/crates/web-plugins/didcomm-messaging/did-utils/src/proof/eddsa_jcs_2022.rs @@ -4,9 +4,12 @@ use crate::crypto::{sha256_hash, CoreSign, Ed25519KeyPair, Error}; use super::{model::Proof, traits::CryptoProof}; +/// The crypto suite for EdDSA-JCS-2022 pub const CRYPRO_SUITE_EDDSA_JCS_2022: &str = "eddsa-jcs-2022"; +/// The proof type for DataIntegrityProof pub const PROOF_TYPE_DATA_INTEGRITY_PROOF: &str = "DataIntegrityProof"; +/// Proof object for EdDSA-JCS-2022 pub struct EdDsaJcs2022 { /// The proof object /// diff --git a/crates/web-plugins/didcomm-messaging/did-utils/src/proof/mod.rs b/crates/web-plugins/didcomm-messaging/did-utils/src/proof/mod.rs index d0b08f23..db8070b6 100644 --- a/crates/web-plugins/didcomm-messaging/did-utils/src/proof/mod.rs +++ b/crates/web-plugins/didcomm-messaging/did-utils/src/proof/mod.rs @@ -1,21 +1,20 @@ -//! This module provides utilities for creating and verifying proofs. +//! This module contains various components for implementing cryptographic proofs. //! -//! See [RFC 7807](https://tools.ietf.org/html/rfc7807) for details. +//! # Basic usage //! -//! # Examples -//! -//! ```no run +//! ``` +//! use did_utils::proof::{EdDsaJcs2022, Proof, Domain, UnsecuredDocument, Proofs, PreviousProofs, CryptoProof}; //! # use chrono::TimeZone; //! # use serde_json::json; +//! # use multibase::Base; +//! # use serde_json::Value; +//! # use did_utils::crypto::{Ed25519KeyPair, Generate, KeyMaterial}; //! -//! # use crate::crypto::Ed25519KeyPair; -//! -//! # use crate::*; -//! +//! # fn example() -> Result<(), did_utils::crypto::Error> { //! let my_string = String::from("Sample seed bytes of thirtytwo!b"); //! let seed: &[u8] = my_string.as_bytes(); //! let key_pair = Ed25519KeyPair::new_with_seed(seed)?; -//! let public_key = &key_pair.public_key.clone(); +//! let public_key = &key_pair.public_key_bytes()?; //! //! let proof = Proof { //! id: None, @@ -25,7 +24,7 @@ //! verification_method: "https://di.example/issuer#z6MkjLrk3gKS2nnkeWcmcxiZPGskmesDpuwRBorgHxUXfxnG".to_string(), //! created: Some(chrono::Utc.with_ymd_and_hms(2023, 3, 5, 19, 23, 24).unwrap()), //! expires: None, -//! domain: Some(crate::proof::model::Domain::SingleString("vc-demo.adorsys.com".to_string())), +//! domain: Some(Domain::SingleString("vc-demo.adorsys.com".to_string())), //! challenge: Some("523452345234asfdasdfasdfa".to_string()), //! proof_value: None, //! previous_proof: None, @@ -49,20 +48,22 @@ //! //! let secure_doc = UnsecuredDocument { //! content: payload, -//! proof: crate::proof::model::Proofs::SingleProof(Box::new(secured_proof.clone())), +//! proof: Proofs::SingleProof(Box::new(secured_proof.clone())), //! }; //! //! // Serialize the struct into a serde_json::Value -//! let secure_doc_json_value: Value = serde_json::to_value(&secure_doc)?; +//! let secure_doc_json_value: Value = serde_json::to_value(&secure_doc).unwrap(); //! //! let ed_dsa_jcs_2022_verifier = EdDsaJcs2022 { //! proof: secured_proof, -//! key_pair: Ed25519KeyPair::from_public_key(public_key.as_bytes())?, +//! key_pair: Ed25519KeyPair::from_public_key(public_key)?, //! proof_value_codec: None, //! }; //! //! // Verify the proof //! ed_dsa_jcs_2022_verifier.verify(secure_doc_json_value)?; +//! # Ok(()) +//! # } //!``` mod eddsa_jcs_2022; mod model; diff --git a/crates/web-plugins/didcomm-messaging/did-utils/src/proof/model.rs b/crates/web-plugins/didcomm-messaging/did-utils/src/proof/model.rs index 9029bc2c..cfd0b26f 100644 --- a/crates/web-plugins/didcomm-messaging/did-utils/src/proof/model.rs +++ b/crates/web-plugins/didcomm-messaging/did-utils/src/proof/model.rs @@ -4,64 +4,74 @@ use serde_json::Value; #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] #[serde(rename_all = "camelCase")] +/// Represents the cryptographic proof of a verifiable credential pub struct Proof { - // An optional identifier for the proof. + /// An optional identifier for the proof. #[serde(skip_serializing_if = "Option::is_none")] pub id: Option, - // A specified set of cryptographic primitives bundled together into a cryptographic suite - // See https://www.w3.org/TR/vc-data-integrity/#dfn-proof-type + /// A specified set of cryptographic primitives bundled together into a cryptographic suite. + /// See [proof type] + /// + /// [proof type]: https://www.w3.org/TR/vc-data-integrity/#dfn-proof-type #[serde(rename = "type")] pub proof_type: String, - // A string value that identifies the cryptographic suite used to create the proof - // Only required when type=DataIntegrityProof + /// A string value that identifies the cryptographic suite used to create the proof. + /// Only required when type=DataIntegrityProof pub cryptosuite: Option, - // See https://www.w3.org/TR/vc-data-integrity/#dfn-proof-purpose + /// The [purpose] of the proof. + /// + /// [purpose]: https://www.w3.org/TR/vc-data-integrity/#dfn-proof-purpose pub proof_purpose: String, - // See https://www.w3.org/TR/vc-data-integrity/#dfn-verification-method + /// A set of parameters that can be used together with a process to independently verify a proof. + /// See [verification method][vm] + /// + /// [vm]: https://www.w3.org/TR/vc-data-integrity/#dfn-verification-method pub verification_method: String, - // The date and time the proof was created + /// The date and time the proof was created #[serde(skip_serializing_if = "Option::is_none")] pub created: Option>, - // The date and time that the proof expires + /// The date and time that the proof expires #[serde(skip_serializing_if = "Option::is_none")] pub expires: Option>, - // One or more security domains in which the proof is meant to be used + /// One or more security domains in which the proof is meant to be used #[serde(skip_serializing_if = "Option::is_none")] pub domain: Option, - // A string value that SHOULD be included in a proof if a domain is specified - // The value is used once for a particular domain and window of time - // This value is used to mitigate replay attacks. + /// A string value that SHOULD be included in a proof if a domain is specified. + /// The value is used once for a particular domain and window of time. + /// This value is used to mitigate replay attacks. #[serde(skip_serializing_if = "Option::is_none")] pub challenge: Option, - // Data necessary to verify the digital proof using the verificationMethod specified - // The contents of the value MUST be a [MULTIBASE]-encoded binary value + /// Data necessary to verify the digital proof using the verificationMethod specified + /// The contents of the value MUST be a multibase-encoded binary value #[serde(skip_serializing_if = "Option::is_none")] pub proof_value: Option, - // Each value identifies another data integrity proof that - // MUST verify before the current proof is processed + /// Each value identifies another data integrity proof that + /// MUST verify before the current proof is processed // See https://www.w3.org/TR/vc-data-integrity/#proof-chains #[serde(skip_serializing_if = "Option::is_none")] pub previous_proof: Option, - // A string value supplied by the proof creator that is unique to the proof - // One use of this field is to increase privacy by decreasing linkability - // that is the result of deterministically generated signatures + /// A string value supplied by the proof creator that is unique to the proof. + /// One use of this field is to increase privacy by decreasing linkability + /// that is the result of deterministically generated signatures #[serde(skip_serializing_if = "Option::is_none")] pub nonce: Option, } #[derive(Serialize, Debug, Clone, PartialEq, Deserialize)] #[serde(untagged)] +#[allow(missing_docs)] +/// The domain in which the proof is meant to be used pub enum Domain { SingleString(String), SetOfString(Vec), @@ -69,6 +79,8 @@ pub enum Domain { #[derive(Serialize, Debug, Clone, PartialEq, Deserialize)] #[serde(untagged)] +#[allow(missing_docs)] +/// The previous proofs in the proof chain pub enum PreviousProofs { SingleString(String), SetOfString(Vec), @@ -76,6 +88,8 @@ pub enum PreviousProofs { #[derive(Serialize, Debug, Clone, PartialEq, Deserialize)] #[serde(untagged)] +#[allow(missing_docs)] +/// The set of proofs pub enum Proofs { SingleProof(Box), SetOfProofs(Box>), @@ -83,12 +97,13 @@ pub enum Proofs { #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] #[serde(rename_all = "camelCase")] +/// The unsecured document pub struct UnsecuredDocument { - // The document to be secured + /// The document to be secured #[serde(flatten)] pub content: Value, - // Set of proofs + /// Set of proofs pub proof: Proofs, } diff --git a/crates/web-plugins/didcomm-messaging/did-utils/src/proof/traits.rs b/crates/web-plugins/didcomm-messaging/did-utils/src/proof/traits.rs index cb5cc48b..e0b93bda 100644 --- a/crates/web-plugins/didcomm-messaging/did-utils/src/proof/traits.rs +++ b/crates/web-plugins/didcomm-messaging/did-utils/src/proof/traits.rs @@ -1,8 +1,6 @@ -use serde_json::Value; - -use crate::crypto::Error; - use super::model::Proof; +use crate::crypto::Error; +use serde_json::Value; /// A trait to be implemented by every crypto suite pub trait CryptoProof { diff --git a/crates/web-plugins/didcomm-messaging/did-utils/src/vc/mod.rs b/crates/web-plugins/didcomm-messaging/did-utils/src/vc/mod.rs index 8cb19788..78bc88be 100644 --- a/crates/web-plugins/didcomm-messaging/did-utils/src/vc/mod.rs +++ b/crates/web-plugins/didcomm-messaging/did-utils/src/vc/mod.rs @@ -1,6 +1,6 @@ //! This module provides utilities for working with [Verifiable Credentials (VCs)][vc]. //! -//! See [vc]: (https://www.w3.org/TR/vc-data-model-2.0/) for details. +//! [vc]: https://www.w3.org/TR/vc-data-model-2.0/ mod model; diff --git a/crates/web-plugins/didcomm-messaging/did-utils/src/vc/model.rs b/crates/web-plugins/didcomm-messaging/did-utils/src/vc/model.rs index 17131492..b81eaad5 100644 --- a/crates/web-plugins/didcomm-messaging/did-utils/src/vc/model.rs +++ b/crates/web-plugins/didcomm-messaging/did-utils/src/vc/model.rs @@ -11,48 +11,50 @@ use crate::{didcore::Proofs, ldmodel::Context}; #[serde(rename_all = "camelCase")] pub struct VerifiableCredential { #[serde(rename = "@context")] + /// The @context property defines the vocabulary used in the JSON-LD document. pub context: Context, - // Identifier of this credential. - // WARNING: This is not the identifier of the subject of the credential. - // WARNING: This is not the identifier of the holder of the credential. + /// Identifier of this credential. + /// WARNING: This is not the identifier of the subject of the credential. // Optional globally unique identifiers enable // others to express statements about the same thing // https://www.w3.org/TR/vc-data-model-2.0/#identifiers #[serde(skip_serializing_if = "Option::is_none")] pub id: Option, - // see https://www.w3.org/TR/vc-data-model-2.0/#types + /// The type of this credential. #[serde(rename = "type")] pub cred_type: Vec, - // see https://www.w3.org/TR/vc-data-model-2.0/#issuer + /// The issuer of this credential. pub issuer: Issuers, - // The date and time the proof was created + /// The date and time the proof was created #[serde(skip_serializing_if = "Option::is_none")] pub valid_from: Option>, #[serde(skip_serializing_if = "Option::is_none")] + /// The date and time the proof expires pub valid_until: Option>, - // See https://www.w3.org/TR/vc-data-model-2.0/#credential-subject + /// The credential subject pub credential_subject: CredentialSubject, - // laguage tag + /// laguage tag #[serde(skip_serializing_if = "Option::is_none")] pub name: Option, - // text direction string + /// text direction string #[serde(skip_serializing_if = "Option::is_none")] pub description: Option, // === Properties Map=== #[serde(skip_serializing_if = "Option::is_none")] #[serde(flatten)] + /// Dynamic properties of the credential pub additional_properties: Option>, - // Set of proofs + /// Set of proofs // We allow a vc to created without the proof block. // Event though it is required. As we want to produce // the unsecured vesion before proof production or proof @@ -61,39 +63,49 @@ pub struct VerifiableCredential { pub proof: Option, #[serde(skip_serializing_if = "Option::is_none")] + /// The credential status pub credential_status: Option, #[serde(skip_serializing_if = "Option::is_none")] + /// The credential schema pub credential_schemas: Option, #[serde(skip_serializing_if = "Option::is_none")] + /// The related resource pub related_resource: Option>, #[serde(skip_serializing_if = "Option::is_none")] + /// The refresh service pub refresh_service: Option, } #[derive(Serialize, Debug, Clone, PartialEq, Deserialize)] #[serde(untagged)] +#[allow(missing_docs)] +/// The issuers of the credential. pub enum Issuers { Single(Box), - SetOf(Vec), + SetOf(Box>), } #[derive(Serialize, Debug, Clone, PartialEq, Deserialize)] #[serde(untagged)] +#[allow(missing_docs)] +/// The issuer of the credential. pub enum Issuer { SingleString(String), IssuerObject, } #[derive(Serialize, Debug, Clone, PartialEq, Deserialize)] +/// The issuer object pub struct IssuerObject { + /// The identifier of the issuer pub id: String, - // laguage tag + /// The laguage tag #[serde(skip_serializing_if = "Option::is_none")] pub name: Option, - // text direction string + /// The text direction string #[serde(skip_serializing_if = "Option::is_none")] pub description: Option, } @@ -107,10 +119,11 @@ pub enum CredentialSubjects { #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] #[serde(rename_all = "camelCase")] +/// The credential subject pub struct CredentialSubject { - // Identifies the subject of the verifiable credential - // (the thing the claims are about) and - // uses a decentralized identifier, also known as a DID + /// Identifies the subject of the verifiable credential + /// (the thing the claims are about) and + /// uses a decentralized identifier, also known as a DID // see https://www.w3.org/TR/vc-data-model-2.0/#identifiers #[serde(skip_serializing_if = "Option::is_none")] pub id: Option, @@ -118,6 +131,7 @@ pub struct CredentialSubject { // === Properties Map=== #[serde(skip_serializing_if = "Option::is_none")] #[serde(flatten)] + /// Dynamic properties pub additional_properties: Option>, } @@ -136,13 +150,15 @@ pub enum Name { } #[derive(Serialize, Debug, Clone, PartialEq, Deserialize)] +/// The name object pub struct NameObject { + /// The name pub value: String, - // laguage tag + /// The laguage tag // see https://www.rfc-editor.org/rfc/rfc5646 #[serde(skip_serializing_if = "Option::is_none")] pub lang: Option, - // text direction string + /// The text direction string #[serde(skip_serializing_if = "Option::is_none")] pub dir: Option, } @@ -162,33 +178,40 @@ pub enum Description { } #[derive(Serialize, Debug, Clone, PartialEq, Deserialize)] +/// The description object pub struct DescriptionObject { + /// The description pub value: String, - // laguage tag + /// The laguage tag // see https://www.rfc-editor.org/rfc/rfc5646 #[serde(skip_serializing_if = "Option::is_none")] pub lang: Option, - // text direction string + /// The text direction string #[serde(skip_serializing_if = "Option::is_none")] pub dir: Option, } #[derive(Serialize, Debug, Clone, PartialEq, Deserialize)] #[serde(rename_all = "camelCase")] +/// The credential status pub struct CredentialStatus { + /// The identifier of the credential status pub id: String, - // see https://www.w3.org/TR/vc-data-model-2.0/#types + /// The type of the credential status #[serde(rename = "type")] pub status_type: Vec, #[serde(skip_serializing_if = "Option::is_none")] + /// The purpose of the credential pub status_purpose: Option, #[serde(skip_serializing_if = "Option::is_none")] + /// The index of the status pub status_list_index: Option, #[serde(skip_serializing_if = "Option::is_none")] + /// The index of the credential status pub status_list_credential: Option, } @@ -202,14 +225,16 @@ pub enum CredentialSchemas { #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] #[serde(rename_all = "camelCase")] +/// The credential schema pub struct CredentialSchema { #[serde(rename = "@context")] + /// The @context property defines the vocabulary used in the JSON-LD document. pub context: Context, - // https://www.w3.org/TR/vc-data-model-2.0/#identifiers + /// See pub id: String, - // see https://www.w3.org/TR/vc-data-model-2.0/#types + /// see #[serde(rename = "type")] pub schema_type: String, } @@ -217,56 +242,68 @@ pub struct CredentialSchema { // see https://www.w3.org/TR/vc-data-model-2.0/#integrity-of-related-resources #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] #[serde(rename_all = "camelCase")] +/// Represents the integrity of related resources pub struct RelatedResource { #[serde(skip_serializing_if = "Option::is_none")] #[serde(rename = "@context")] + /// The @context property defines the vocabulary used in the JSON-LD document. pub context: Option, - // https://www.w3.org/TR/vc-data-model-2.0/#identifiers + /// The identifier for the resource pub id: String, #[serde(rename = "digestSRI")] + /// Cryptographic digest pub digest_sri: Option, + /// Cryptographic digest pub digest_multibase: Option, + /// The media type of the resource pub media_type: Option, } #[derive(Serialize, Debug, Clone, PartialEq, Deserialize)] #[serde(rename_all = "camelCase")] +/// Represents the refresh service pub struct RefreshService { + /// The identifier pub id: String, - // see https://www.w3.org/TR/vc-data-model-2.0/#types + /// The type of the service #[serde(rename = "type")] pub rs_type: String, } #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] #[serde(rename_all = "camelCase")] +/// Represents a verifiable presentation as defined [here]. +/// +/// [here]: https://www.w3.org/TR/vc-data-model-2.0/#verifiable-presentations pub struct VerifiablePresentation { #[serde(rename = "@context")] + /// The @context property defines the vocabulary used in the JSON-LD document. pub context: Context, - // Optional globally unique identifiers enable - // others to express statements about the same thing + /// Optional globally unique identifiers enable + /// others to express statements about the same thing. // https://www.w3.org/TR/vc-data-model-2.0/#identifiers #[serde(skip_serializing_if = "Option::is_none")] pub id: Option, + /// The list of verifiable credentials pub verifiable_credential: Vec, - // see https://www.w3.org/TR/vc-data-model-2.0/#types + /// Used to express the type of verifiable presentation #[serde(rename = "type")] pub pres_type: Vec, - // Identifies the presenter + /// Identifies the presenter // https://www.w3.org/TR/vc-data-model-2.0/#identifiers #[serde(skip_serializing_if = "Option::is_none")] pub holder: Option, - // Set of proofs + /// Set of proofs // We allow a VP to created without the proof block. // Event though it is required. As we want to produce // the unsecured vesion before proof production or proof diff --git a/crates/web-plugins/didcomm-messaging/message-api/src/lib.rs b/crates/web-plugins/didcomm-messaging/message-api/src/lib.rs index c758d9e5..74b97ecd 100644 --- a/crates/web-plugins/didcomm-messaging/message-api/src/lib.rs +++ b/crates/web-plugins/didcomm-messaging/message-api/src/lib.rs @@ -4,24 +4,31 @@ use didcomm::Message; use shared::state::AppState; use std::{collections::HashMap, sync::Arc}; +/// Defines a handler that processes `DIDComm` messages. #[async_trait] pub trait MessageHandler: Send + Sync { + /// Process a `DIDComm` message. + /// + /// Returns an optional message to be sent back to the sender or an error response. async fn handle(&self, state: Arc, msg: Message) -> Result, Response>; } +/// A router that maps `DIDComm` message types to their corresponding handlers. #[derive(Default, Clone)] pub struct MessageRouter { handlers: HashMap>, } impl MessageRouter { + /// Creates a new [`MessageRouter`]. pub fn new() -> Self { Self { handlers: HashMap::new(), } } + /// Registers a handler for a specific message type. pub fn register(mut self, msg: &str, f: F) -> Self where F: MessageHandler + 'static, @@ -30,15 +37,18 @@ impl MessageRouter { self } + /// Merges another [`MessageRouter`] into this one. pub fn merge(mut self, other: Self) -> Self { self.handlers.extend(other.handlers); self } + /// Returns the handler for a specific message type if it is registered. pub fn get_handler(&self, msg: &str) -> Option<&Arc> { self.handlers.get(msg) } + /// Returns a list of all registered message types. pub fn messages_types(&self) -> Vec { self.handlers.keys().cloned().collect() } diff --git a/crates/web-plugins/didcomm-messaging/protocols/discover-features/src/plugin.rs b/crates/web-plugins/didcomm-messaging/protocols/discover-features/src/plugin.rs index d77795ec..b8164a70 100644 --- a/crates/web-plugins/didcomm-messaging/protocols/discover-features/src/plugin.rs +++ b/crates/web-plugins/didcomm-messaging/protocols/discover-features/src/plugin.rs @@ -6,6 +6,7 @@ use message_api::{MessageHandler, MessagePlugin, MessageRouter}; use shared::state::AppState; use std::sync::Arc; +/// Represents the discover-features protocol plugin pub struct DiscoverFeaturesProtocol; struct DiscoverFeaturesHandler; diff --git a/crates/web-plugins/didcomm-messaging/protocols/forward/src/plugin.rs b/crates/web-plugins/didcomm-messaging/protocols/forward/src/plugin.rs index 75744505..6f860a59 100644 --- a/crates/web-plugins/didcomm-messaging/protocols/forward/src/plugin.rs +++ b/crates/web-plugins/didcomm-messaging/protocols/forward/src/plugin.rs @@ -6,6 +6,7 @@ use message_api::{MessageHandler, MessagePlugin, MessageRouter}; use shared::state::AppState; use std::sync::Arc; +/// Represents the routing protocol plugin. pub struct RoutingProtocol; struct ForwardHandler; diff --git a/crates/web-plugins/didcomm-messaging/protocols/mediator-coordination/src/plugin.rs b/crates/web-plugins/didcomm-messaging/protocols/mediator-coordination/src/plugin.rs index 3aab4d69..b0693bc1 100644 --- a/crates/web-plugins/didcomm-messaging/protocols/mediator-coordination/src/plugin.rs +++ b/crates/web-plugins/didcomm-messaging/protocols/mediator-coordination/src/plugin.rs @@ -6,6 +6,7 @@ use message_api::{MessageHandler, MessagePlugin, MessageRouter}; use shared::state::AppState; use std::sync::Arc; +/// Represents the routing protocol plugin. pub struct MediatorCoordinationProtocol; struct MediateRequestHandler; diff --git a/crates/web-plugins/didcomm-messaging/protocols/pickup/src/plugin.rs b/crates/web-plugins/didcomm-messaging/protocols/pickup/src/plugin.rs index c40df56e..b91353b9 100644 --- a/crates/web-plugins/didcomm-messaging/protocols/pickup/src/plugin.rs +++ b/crates/web-plugins/didcomm-messaging/protocols/pickup/src/plugin.rs @@ -8,6 +8,7 @@ use message_api::{MessageHandler, MessagePlugin, MessageRouter}; use shared::state::AppState; use std::sync::Arc; +/// Represents the pickup protocol plugin. pub struct PickupProtocol; struct StatusRequestHandler; diff --git a/crates/web-plugins/didcomm-messaging/protocols/trust-ping/src/plugin.rs b/crates/web-plugins/didcomm-messaging/protocols/trust-ping/src/plugin.rs index 59982eed..8695632c 100644 --- a/crates/web-plugins/didcomm-messaging/protocols/trust-ping/src/plugin.rs +++ b/crates/web-plugins/didcomm-messaging/protocols/trust-ping/src/plugin.rs @@ -6,6 +6,7 @@ use message_api::{MessageHandler, MessagePlugin, MessageRouter}; use shared::state::AppState; use std::sync::Arc; +/// Represents the trust-ping protocol plugin pub struct TrustPingProtocol; struct TrustPingHandler; diff --git a/crates/web-plugins/didcomm-messaging/shared/README.md b/crates/web-plugins/didcomm-messaging/shared/README.md new file mode 100644 index 00000000..29583629 --- /dev/null +++ b/crates/web-plugins/didcomm-messaging/shared/README.md @@ -0,0 +1,3 @@ +# shared crate + +A set of shared utilities for the `didcomm-messaging` plugin used in the [Didcomm mediator](https://github.com/adorsys/didcomm-mediator-rs) project. diff --git a/crates/web-plugins/oob-messages/README.md b/crates/web-plugins/oob-messages/README.md index 41eb3ae6..6e414b12 100644 --- a/crates/web-plugins/oob-messages/README.md +++ b/crates/web-plugins/oob-messages/README.md @@ -1,43 +1,5 @@ -## oob-messages +# oob-messages -A Rust library for implementing out of band messages for DID-based applications. +Implementation of the [Out of Band](https://didcomm.org/out-of-band/2.0/invitation) protocol for the [Didcomm Mediator](https://github.com/adorsys/didcomm-mediator-rs/) server. -Out of band messages (OOB) messages are the initiators of a didcomm communication where by sender provides his identifier in an unencrypted messages (QR-code or Invitation link) which the other party can scan with his/her edge device, hence no private information should be send in this way. The protocol used here is the version 2 https://didcomm.org/out-of-band/2.0/invitation - -## Features -- Creates out of band invitation URL and QR codes. - -## Installation - -```rust -cargo install oob-messages -``` - -## USAGE -To be able to generate oob invitations, first begin by setting the following local environment variables -**SERVER_PUBLIC_DOMAIN** -**SERVER_LOCAL_PORT** -**STORAGE_DIRPATH** -```rust - -// start by creating a new OOBMessages with default name oob_messages -let oobmessagesplugin = OOBMessages; - -// initialize mounting of the oob invitation and qr code in the storage directory set in STORAGE_DIRPATH. -// Or returns an error if the plugins are not valid PluginError -oobmessagesplugin.mount(); - -// Then navigate to the following endpoints on your browser /, /oob_url and /oob_qr,where you will find the server's response, out of band url and out of band qr code respectively. -// Now both parties can start a communication using didcomm protocol. -oobmessagesplugin.routes(); - -// to revert the mounting initialization, call unmount function on the created oobmessagesplugin -oobmessagesplugin.unmount(); - -``` -## DEBUGGING - -```rust -// After setting the environment variables you can try checking if the were well set using calls to get_environment_variables function. -get_environment_variables(oobmessagesplugin); -``` +Out of band messages (OOB) messages are the initiators of a didcomm communication where by sender provides his identifier in an unencrypted messages (QR-code or Invitation link) which the other party can scan with his/her edge device, hence no private information should be send in this way. The [protocol](https://didcomm.org/out-of-band/2.0/invitation) used here is the version 2.