#header #recipient #key #message #did #messaging #identity

didcomm-rs

DIDComm 消息 v2 规范实现:https://identity.foundation/didcomm-messaging/spec/

16 个不稳定版本 (5 个破坏性更新)

0.7.2 2022年3月23日
0.6.7 2022年1月26日
0.6.3 2021年12月3日
0.6.2 2021年11月19日
0.1.0 2020年11月9日

#584 in 异步


3 个 crate 中使用 (2 个直接使用)

Apache-2.0

170KB
3K SLoC

didcomm-rs

Rust 实现 DIDComm v2 规范

tests

许可证

Apache-2.0

使用示例

1. 准备发送和接收的原始消息

前往: 完整测试

// Message construction
let m = Message::new()
    // setting `from` header (sender) - Optional
    .from("did:xyz:ulapcuhsatnpuhza930hpu34n_")
    // setting `to` header (recipients) - Optional
    .to(&[
        "did::xyz:34r3cu403hnth03r49g03",
        "did:xyz:30489jnutnjqhiu0uh540u8hunoe",
    ])
    // populating body with some data - `Vec<bytes>`
    .body(TEST_DID);

// Serialize message into JWM json (SENDER action)
let ready_to_send = m.clone().as_raw_json().unwrap();

// ... transport is happening here ...

// On receival deserialize from json into Message (RECEIVER action)
// Error handling recommended here

let received = Message::receive(&ready_to_send, None, None, None);

2. 准备直接发送的 JWE 消息

前往: 完整测试

// sender key as bytes
let ek = [130, 110, 93, 113, 105, 127, 4, 210, 65, 234, 112, 90, 150, 120, 189, 252, 212, 165, 30, 209, 194, 213, 81, 38, 250, 187, 216, 14, 246, 250, 166, 92];

// Message construction
let message = Message::new()
    .from("did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp")
    .to(&[
        "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp",
        "did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG",
    ])
    // packing in some payload (can be anything really)
    .body(TEST_DID)
    // decide which [Algorithm](crypto::encryptor::CryptoAlgorithm) is used (based on key)
    .as_jwe(
        &CryptoAlgorithm::XC20P,
        Some(&bobs_public),
    )
    // add some custom app/protocol related headers to didcomm header portion
    // these are not included into JOSE header
    .add_header_field("my_custom_key".into(), "my_custom_value".into())
    .add_header_field("another_key".into(), "another_value".into())
    // set `kid` property
    .kid(r#"#z6LShs9GGnqk85isEBzzshkuVWrVKsRp24GnDuHk8QWkARMW"#);

// recipient public key is automatically resolved
let ready_to_send = message.seal(
    &ek,
    Some(vec![Some(&bobs_public), Some(&carol_public)]),
).unwrap();

//... transport is happening here ...

3. 准备 JWS 消息 -> 发送 -> 接收

  • 在这里 Message 已签名但未加密。
  • 在这种情况下,需要显式使用 .sign(...)Message::verify(...)
// Message construction an JWS wrapping
let message = Message::new() // creating message
    .from("did:xyz:ulapcuhsatnpuhza930hpu34n_") // setting from
    .to(&["did::xyz:34r3cu403hnth03r49g03", "did:xyz:30489jnutnjqhiu0uh540u8hunoe"]) // setting to
    .body(TEST_DID) // packing in some payload
    .as_jws(&SignatureAlgorithm::EdDsa)
    .sign(SignatureAlgorithm::EdDsa.signer(), &sign_keypair.to_bytes()).unwrap();

//... transport is happening here ...

// Receiving JWS
let received = Message::verify(&message.as_bytes(), &sign_keypair.public.to_bytes());

4. 准备要调解的 JWE 消息 -> 调解 -> 接收

  • 消息应首先在 .routed_by() 方法调用中使用接收者的密钥加密。
  • 然后应使用调解者的密钥在 .seal() 方法调用中加密 - 这可以多次进行 - 一次对应链中的每个调解者,但应严格顺序进行,以匹配链中的调解者顺序。
  • 方法调用 .seal() 必须.as_jwe(CryptoAlgorithm) 前面,因为中介可能使用与目的地不同的算法和密钥类型,这不是自动预测或填充的。
  • 用于加密的密钥应按反向顺序使用 - 最终目的地 - 最后一个中介 - 第二到最后的中介 - 等等。洋葱式。

转到: 完整测试

let mediated = Message::new()
    // setting from
    .from("did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp")
    // setting to
    .to(&["did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG"])
    // packing in some payload
    .body(r#"{"foo":"bar"}"#)
    // set JOSE header for XC20P algorithm
    .as_jwe(&CryptoAlgorithm::XC20P, Some(&bobs_public))
    // custom header
    .add_header_field("my_custom_key".into(), "my_custom_value".into())
    // another custom header
    .add_header_field("another_key".into(), "another_value".into())
    // set kid header
    .kid(&"Ef1sFuyOozYm3CEY4iCdwqxiSyXZ5Br-eUDdQXk6jaQ")
    // here we use destination key to bob and `to` header of mediator -
    //**THIS MUST BE LAST IN THE CHAIN** - after this call you'll get new instance of envelope `Message` destined to the mediator.
    .routed_by(
        &alice_private,
        Some(vec![Some(&bobs_public)]),
        "did:key:z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf",
        Some(&mediators_public),
    );
assert!(mediated.is_ok());

//... transport to mediator is happening here ...

// Received by mediator
let mediator_received = Message::receive(
    &mediated.unwrap(),
    Some(&mediators_private),
    Some(&alice_public),
    None,
);
assert!(mediator_received.is_ok());

// Get inner JWE as string from message
let mediator_received_unwrapped = mediator_received.unwrap().get_body().unwrap();
let pl_string = String::from_utf8_lossy(mediator_received_unwrapped.as_ref());
let message_to_forward: Mediated = serde_json::from_str(&pl_string).unwrap();
let attached_jwe = serde_json::from_slice::<Jwe>(&message_to_forward.payload);
assert!(attached_jwe.is_ok());
let str_jwe = serde_json::to_string(&attached_jwe.unwrap());
assert!(str_jwe.is_ok());

//... transport to destination is happening here ...

// Received by Bob
let bob_received = Message::receive(
    &String::from_utf8_lossy(&message_to_forward.payload),
    Some(&bobs_private),
    Some(&alice_public),
    None,
);
assert!(bob_received.is_ok());

5. 准备包含在JWE中的JWS信封 -> 签名 -> 打包 -> 接收

  • JWS头根据签名算法类型自动设置。
  • 消息形成和加密与其他JWE示例中的方式相同。
  • 本例中使用ED25519-dalek签名,使用密钥对进行签名,使用公钥进行验证。

转到: 完整测试

let KeyPairSet {
    alice_public,
    alice_private,
    bobs_private,
    bobs_public,
    ..
} = get_keypair_set();
// Message construction
let message = Message::new() // creating message
    .from("did:xyz:ulapcuhsatnpuhza930hpu34n_") // setting from
    .to(&["did::xyz:34r3cu403hnth03r49g03"]) // setting to
    .body(TEST_DID) // packing in some payload
    .as_jwe(&CryptoAlgorithm::XC20P, Some(&bobs_public)) // set JOSE header for XC20P algorithm
    .add_header_field("my_custom_key".into(), "my_custom_value".into()) // custom header
    .add_header_field("another_key".into(), "another_value".into()) // another custom header
    .kid(r#"Ef1sFuyOozYm3CEY4iCdwqxiSyXZ5Br-eUDdQXk6jaQ"#); // set kid header

// Send as signed and encrypted JWS wrapped into JWE
let ready_to_send = message.seal_signed(
    &alice_private,
    Some(vec![Some(&bobs_public)]),
    SignatureAlgorithm::EdDsa,
    &sign_keypair.to_bytes(),
).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 = Message::receive(
    &ready_to_send,
    Some(&bobs_private),
    Some(&alice_public),
    None,
); // and now we parse received

6. 多个接收者,每个接收者使用共享密钥进行静态密钥封装

  • ! 仅与 resolve 功能一起工作 - 需要为每个接收者解析公钥以生成共享密钥。
  • 静态密钥在后台随机生成(to 字段有 >1 个接收者)。

转到: 完整测试

// Creating message with multiple recipients.
let m = Message::new()
    .from("did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp")
    .to(&[
        "did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG",
        "did:key:z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf",
    ])
    .as_jwe(&CryptoAlgorithm::XC20P, None);

let jwe = m.seal(&alice_private, None);
// Packing was ok?
assert!(jwe.is_ok());

let jwe = jwe.unwrap();

// Each of the recipients receive it in same way as before (direct with single recipient)
let received_first = Message::receive(&jwe, Some(&bobs_private), None, None);
let received_second = Message::receive(&jwe, Some(&carol_private), None, None);

// All good without any extra inputs
assert!(received_first.is_ok());
assert!(received_second.is_ok());

7. 与 attachments 一起工作

7.1 添加 Attachment

use didcomm_rs::{Message, AttachmentBuilder, AttachmentDataBuilder};

let payload = b"some usefull data";
let mut m = Message:new();
    m.append_attachment(
        AttachmentBuilder::new(true)
            .with_id("best attachment")
            .with_data(
                AttachmentDataBuilder::new()
                    .with_raw_payload(payload)
            )
        );

use didcomm_rs::{Message, AttachmentBuilder, AttachmentDataBuilder};

let attachments: Vec<AttachmentBuilder>; // instantiate properly

let mut m = Message:new();

for attachment in attachments {
    m.append_attachment(attachment);
}

7.2 解析 Attachment

// `m` is `receive()`'d instance of a `Message`

let something_im_looking_for = m.get_attachments().filter(|single| single.id == "id I'm looking for");
assert!(something_im_looking_for.next().is_some());

for found in something_im_looking_for {
    // process attachments
}

8. 线程

默认情况下,所有新消息都使用随机UUID作为 thid 标头值,并使用空的 pthid 值。

要回复线程中的消息并复制 thidpthid,请使用 reply_to 方法


let m = Message::new()
    .reply_to(&received)
    // - other methods to form a message
    ;

要设置父线程ID(或 pthid 标头),请使用 with_parent 方法


let m = Message::new()
    .with_parent(&receievd)
    // - other methods to form a message
    ;

9. 其他应用级标头和装饰器

为了满足任何其他标头值,存在一个通用方法:Message::add_header_field' 此方法由一个 HashMap` 的 <String, String> 支持。如果存在键,则更新其值。


let m = Message::new()
    .add_header_field("key", "value")
    .add_header_field("~decorator", "value")
    // - other methods to form a message
    ;

要查找特定应用级标头是否存在并获取其值,应使用 get_application_params 方法。


let m: Message; // proprely instantiated received message

if let Some((my_key, my_value)) = m.get_application_params().filter(|(key, _)| key == "my_key").first();

可插入的加密

为了使用自己的消息加密和/或签名算法的实现,实现这些特质

didcomm_rs::crypto::Cypher

didcomm_rs::crypto::Signer

不要使用 default 功能 - 可能在未来更改。

实现后 - 使用它们而不是上面的示例中的 CryptoAlgorithmSignatureAlgorithm

强类型消息有效负载(主体)

转到: 完整测试

在大多数情况下,应用程序实现更喜欢有强类型消息主体而不是原始 Vec<u8>。对于此场景,应为目标类型实现 Shape 特质。

  • 首先,让我们定义我们的目标类型。本例中的JSON。
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct DesiredShape {
    num_field: usize,
    string_field: String,
}
  • 接下来,为目标类型实现 Shape 特质
impl Shape for DesiredShape {
    type Err = Error;
    fn shape(m: &Message) -> Result<DesiredShape, Error> {
        serde_json::from_str(&m.get_body().unwrap())
            .map_err(|e| Error::SerdeError(e))
    }
}
  • 现在我们可以在我们的 Message 上调用 shape() 并将其塑形。
  • 在这个例子中,我们期望JSON有效负载并使用其反序列化器来获取我们的数据,但您的实现可以与任何序列化一起工作。
let body = r#"{"num_field":123,"string_field":"foobar"}"#.to_string();
let message = Message::new() // creating message
    .from("did:xyz:ulapcuhsatnpuhza930hpu34n_") // setting from
    .to(&["did::xyz:34r3cu403hnth03r49g03"]) // setting to
    .body(&body); // packing in some payload
let received_typed_body = DesiredShape::shape(&message).unwrap(); // Where m = Message

免责声明

这是DIDComm V2规范的示例实现。DIDComm V2规范仍在DIF的DIDComm工作组中积极开发,因此可能会发生变化。

依赖关系

~7–21MB
~291K SLoC