didcomm-rs
Rust implementation of DIDComm v2 spec
#License
Examples of usage
1. Prepare raw message for send and receive
GoTo: full test
// Message construction
let m = new
// setting `from` header (sender) - Optional
.from
// setting `to` header (recepients) - Optional
.to
// populating body with some data - `Vec<bytes>`
.set_body;
// Serialize message into JWM json (SENDER action)
let ready_to_send = m.as_raw_json.unwrap;
//... transport is happening here ...
// On receival deserialize from json into Message (RECEIVER action)
// Error handling recommended here
let received = receive.unwrap;
2. Prepare JWE message for direct send
GoTo: full test
// decide which [Algorithm](crypto::encryptor::CryptoAlgorithm) is used (based on key)
let alg = XC20P;
// key as bytes
let ek =
// creating message
let mut message = new
// packing in some payload (can be anything really)
.set_body
// set JOSE header for XC20P algorithm
.as_jwe
// add some custom app/protocol related headers to didcomm header portion
// these are not included into JOSE header
.add_header_field
.add_header_field;
// set `kid` property
message.jwm_header.kid =
Some;
// encrypt and serialize message with JOSE header included
let ready_to_send = message.seal?;
// use transport of choice to send `ready_to_send` data to the receiver!
//... transport is happening here ...
3. Prepare JWS message -> send -> receive
- Here
Message
is signed but not encrypted. - In such scenarios explicit use of
.sign(...)
andMessage::verify(...)
required.
// Message construction an JWS wrapping
let message = new // creating message
.from // setting from
.to // setting to
.set_body // packing in some payload
.as_jws
.sign.unwrap;
//... transport is happening here ...
// Receiving JWS
let received = verify;
4. Prepare JWE message to be mediated -> mediate -> receive
- Message should be encrypted by destination key first in
.routed_by()
method call using key for the recepient. - Next it should be encrypted by mediator key in
.seal()
method call - this can be done multiple times - once for each mediator in chain but should be strictly sequentual to match mediators sequence in the chain. - Method call
.seal()
MUST be preceeded by.as_jwe(CryptoAlgorithm)
as mediators may use different algorithms and key types than destination and this is not automatically predicted or populated. - Keys used for encryption should be used in reverse order - final destination - last mediator - second to last mediator - etc. Onion style.
GoTo: full test
// Message construction
let message = new
// setting from
.from
// setting to
.to
// packing in some payload
.set_body
// set JOSE header for XC20P algorithm
.as_jwe
// custom header
.add_header_field
// another coustom header
.add_header_field
// set kid header
.kid
// here we use destination key to bob and `to` header of mediator -
//**THISH MUST BE LAST IN THE CHAIN** - after this call you'll get new instance of envelope `Message` destined to the mediator.
// `ek_to_bob` - destination targeted encryption key
.routed_by;
// Message envelope to mediator
let ready_to_send = message
.unwrap // **ERROR HANDLE** here is recommended
.as_jwe // here this method call is crucial as mediator and end receiver may use different algorithms.
// `ek_to_mediator` - mediator targeted encryption key
.seal; // this would've failed without previous method call.
//... transport to mediator is happening here ...
// Received by mediator
// `rk_mediator` - key to decrypt mediated message
let received_mediated = receive;
//... transport to destination is happening here ...
// Received by Bob
// `rk_bob` - key to decrypt final message
let received_bob = receive;
5. Prepare JWS envelope wrapped into JWE -> sign -> pack -> receive
- JWS header is set automatically based on signing algorythm type.
- Message forming and encryption happens in same way as in other JWE examples.
- ED25519-dalek signature is used in this example with keypair for signing and public key for verification.
GoTo: full test
// Message construction
let message = new // creating message
.from // setting from
.to // setting to
.set_body // packing in some payload
.as_jwe // set JOSE header for XC20P algorithm
.add_header_field // custom header
.add_header_field // another coustom header
.kid; // set kid header
// Send as signed and encrypted JWS wrapped into JWE
let ready_to_send = message.seal_signed
.unwrap;
//... transport to destination is happening here ...
//Receive - same method to receive for JWE or JWS wrapped into JWE but with pub verifying key
let received = receive; // and now we parse received
6. Multiple receivers static key wrap per recepient with shared secret
- ! Works with
resolve
feature only - requires resolution of public keys for each recepient for shared secret generation. - Static key generated randomly in the background (
to
field has >1 recepient).
GoTo: full test
// Creating message with multiple recepients.
let m = new
.from
.to
.as_jwe;
let jwe = m.seal;
// Packing was ok?
assert!;
let jwe = jwe.unwrap;
// Each of the recepients receive it in same way as before (direct with single receiver)
let received_first = receive;
let received_second = receive;
// All good without any extra inputs
assert!;
assert!;
7. Working with attachments
7.1 Adding Attachment
use ;
let payload = b"some usefull data";
let mut m = Message:new;
m.append_attachment;
or
use ;
let attachments: ; // instantiate properly
let mut m = Message:new;
for attachment in attachments
7.2 Parsing Attachment
's
// `m` is `receive()`'d instance of a `Message`
let something_im_looking_for = m.get_attachments.filter;
assert!;
for found in something_im_looking_for
8. Threading
By default all new messages are created with random UUID as thid
header value and with empty pthid
value.
To reply to a message in thread with both thid
and pthid
copied use reply_to
method:
let m = new
.reply_to
// - other methods to form a message
;
To set parent thread id (or pthid
header), use with_parent
method:
let m = new
.with_parent
// - other methods to form a message
;
9. Other application-level headers and decorators
In order to satisfy any other header values universal method is present: Message::add_header_field' This method is backed up by a
HashMap` of <String, String>. If the key was present - it's value will be updated.
let m = new
.add_header_field
.add_header_field
// - other methods to form a message
;
To find if specific application level header is present and get it's value get_application_params
method should be used.
let m: Message; // proprely instantiated received message
if let Some = m.get_application_params.filter.first;
Plugable cryptography
In order to use your own implementation[s] of message crypto and/or signature algorythms implement these trait[s]:
Dont use default
feature - might change in future.
When implemented - use them instead of CrptoAlgorithm
and SignatureAlgorithm
from examples above.
Strongly typed Message payload (body)
GoTo: full test
In most cases apllication implementation would prefer to have strongly typed body of the message instead of raw Vec<u8>
.
For this scenario Shape
trait should be implemented for target type.
- First, let's define our target type. JSON in this example.
- Next, implement
Shape
trait for it
- Now we can call
shape()
on ourMessage
and shape in in. - In this example we expect JSON payload and use it's Deserializer to get our data, but your implementation can work with any serialization.
let received_typed_body = shape.unwrap; // Where m = Message
Disclaimer
This is a sample implementation of the DIDComm V2 spec. The DIDComm V2 spec is still actively being developed by the DIDComm WG in the DIF and therefore subject to change.