@estoc/didcomm
v0.4.1-estoc.4
Published
WASM based javascript wrapper for DIDComm
Readme
DIDComm
Basic DIDComm v2 support for modern browsers and NodeJS.
Estoc fork
@estoc/didcomm and @estoc/didcomm-node are built from estoc-net/didcomm-rust,
a fork of sicpa-dlab/didcomm-rust 0.4.1.
Versions are <upstream version>-estoc.<n>.
Changes from upstream:
UnpackOptions.verify_from_prior(defaulttrue). Whenfalse,Message.unpackkeeps the rawfrom_priorJWT in the returned message without resolving its issuer DID, and leavesfrom_priorandfrom_prior_issuer_kidin the metadata unset. Verify it separately withFromPrior.unpack.- Attachment data carries the whole DIDComm v2 attachment model:
hashis accepted on inlinebase64andjsondata, not only onlinks, andjwsis the JSON value the wire carries (an object in the specification's examples) instead of a string. Upstream dropped an inlinehashand rejected an objectjwsas malformed. - Attachment data carrying more than one of
base64,jsonandlinksis malformed. Upstream silently kept the first form it could read and dropped the other, so a receiver never learned the object was ambiguous.
Under the hood
This package is written in Rust using didcomm crate. It compiles
to wasm32 and exposes Javascript/Typescript API with wasm-bindgen help.
Also wasmp-pack helps in packaging and publishing.
Usage
To use didcomm install it with npm
npm install @estoc/didcomm --save # If you plan use webpack or other bundler
npm install @estoc/didcomm-node --save # If you plan use it without bundlers in NodeJS
Run demo
WASM_TARGET=nodejs make # builds NodeJS package in pkg directory
cd ./demo
npm install
npm run startAssumptions and Limitations
- This library requires
wasm32compatible environment (modern browsers and recent NodeJS are supported). - In order to use the library,
SecretsResolverandDIDResolverinterfaces must be implemented on the application level. Demo application provides 2 simple implementationsExampleDIDResolverandExampleSecretsResolverthat allows resolve locally known DID docs and secrets for tests/demo purposes.- Verification materials are expected in JWK, Base58 and Multibase (internally Base58 only) formats.
- In Base58 and Multibase formats, keys using only X25519 and Ed25519 curves are supported.
- For private keys in Base58 and Multibase formats, the verification material value contains both private and public parts (concatenated bytes).
- In Multibase format, bytes of the verification material value is prefixed with the corresponding Multicodec code.
- Key IDs (kids) used in
SecretsResolvermust match the corresponding key IDs from DID Doc verification methods. - Key IDs (kids) in DID Doc verification methods and secrets must be a full DID Fragment, that is
did#key-id. - Verification methods referencing another DID Document are not supported (see Referring to Verification Methods).
- Verification materials are expected in JWK, Base58 and Multibase (internally Base58 only) formats.
- The following curves and algorithms are supported:
- Encryption:
- Curves: X25519, P-256
- Content encryption algorithms:
- XC20P (to be used with ECDH-ES only, default for anoncrypt),
- A256GCM (to be used with ECDH-ES only),
- A256CBC-HS512 (default for authcrypt)
- Key wrapping algorithms: ECDH-ES+A256KW, ECDH-1PU+A256KW
- Signing:
- Curves: Ed25519, Secp256k1, P-256
- Algorithms: EdDSA (with crv=Ed25519), ES256, ES256K
- Encryption:
- Forward protocol is implemented and used by default.
- DID rotation (
fromPriorfield) is supported. - DIDComm has been implemented under the following Assumptions
Examples
A general usage of the API is the following:
- Sender Side:
- Build a
Message(plaintext, payload). - Convert a message to a DIDComm Message for further transporting by calling one of the following:
Message.pack_encryptedto build an Encrypted DIDComm messageMessage.pack_signedto build a Signed DIDComm messageMessage.pack_plaintextto build a Plaintext DIDComm message
- Build a
- Receiver side:
- Call
Message.unpackon receiver side that will decrypt the message, verify signature if needed and return aMessagefor further processing on the application level.
- Call
1. Build an Encrypted DIDComm message for the given recipient
This is the most common DIDComm message to be used in most of the applications.
A DIDComm encrypted message is an encrypted JWM (JSON Web Messages) that
- hides its content from all but authorized recipients
- (optionally) discloses and proves the sender to only those recipients
- provides message integrity guarantees
It is important in privacy-preserving routing. It is what normally moves over network transports in DIDComm applications, and is the safest format for storing DIDComm data at rest.
See Message::pack_encrypted documentation for more details.
Authentication encryption example (most common case):
// --- Build message from ALICE to BOB ---
const msg = new Message({
id: "1234567890",
typ: "application/didcomm-plain+json",
type: "http://example.com/protocols/lets_do_lunch/1.0/proposal",
from: "did:example:alice",
to: ["did:example:bob"],
created_time: 1516269022,
expires_time: 1516385931,
body: { messagespecificattribute: "and its value" },
});
// --- Packing encrypted and authenticated message ---
let didResolver = new ExampleDIDResolver([ALICE_DID_DOC, BOB_DID_DOC]);
let secretsResolver = new ExampleSecretsResolver(ALICE_SECRETS);
const [encryptedMsg, encryptMetadata] = await msg.pack_encrypted(
BOB_DID,
ALICE_DID,
null,
didResolver,
secretsResolver,
{
forward: false, // Forward wrapping is unsupported in current version
}
);
console.log("Encryption metadata is\n", encryptMetadata);
// --- Send message ---
console.log("Sending message\n", encryptedMsg);
// --- Unpacking message ---
didResolver = new ExampleDIDResolver([ALICE_DID_DOC, BOB_DID_DOC]);
secretsResolver = new ExampleSecretsResolver(BOB_SECRETS);
const [unpackedMsg, unpackMetadata] = await Message.unpack(
encrypted_msg,
didResolver,
secretsResolver,
{}
);
console.log("Receved message is\n", unpackedMsg.as_value());
console.log("Receved message unpack metadata is\n", unpackMetadata);Anonymous encryption example:
let [encryptedMsg, encryptMetadata] = await msg.pack_encrypted(
BOB_DID,
null, // Keep sender as None here
null,
didResolver,
secretsResolver,
{
forward: false, // Forward wrapping is unsupported in current version
}
);Encryption with non-repudiation example:
let [encrypted_msg, encrypt_metadata] = await msg.pack_encrypted(
BOB_DID,
ALICE_DID,
ALICE_DID, // Provide information about signer here
did_resolver,
secrets_resolver,
{
forward: false, // Forward wrapping is unsupported in current version
}
);2. Build an unencrypted but Signed DIDComm message
Signed messages are only necessary when
- the origin of plaintext must be provable to third parties
- or the sender can’t be proven to the recipient by authenticated encryption because the recipient is not known in advance (e.g., in a broadcast scenario).
Adding a signature when one is not needed can degrade rather than enhance security because it relinquishes the sender’s ability to speak off the record.
See Message.pack_signed documentation for more details.
let [signed, metadata] = await msg.pack_signed(
ALICE_DID,
didResolver,
secretsResolver
);3. Build a Plaintext DIDComm message
A DIDComm message in its plaintext form that
- is not packaged into any protective envelope
- lacks confidentiality and integrity guarantees
- repudiable
They are therefore not normally transported across security boundaries.
let plaintext = msg.pack_plaintext(didResolver).expect("Unable pack_plaintext");How to build
Install wasm-pack from https://rustwasm.github.io/wasm-pack/installer/ and then
make # Will output modules best-suited to be bundled with webpack
WASM_TARGET=nodejs make # Will output modules that can be directly consumed by NodeJS
WASM_TARGET=web make # Will output modules that can be directly consumed in browser without bundler usageHow to build with wasm-pack build
wasm-pack build # Will output modules best-suited to be bundled with webpack
wasm-pack build --target=nodejs # Will output modules that can be directly consumed by NodeJS
wasm-pack build --target=web # Will output modules that can be directly consumed in browser without bundler usageHow to test in NodeJS
WASM_TARGET=nodejs make
cd ./tests-js
npm install
npm testHow to test in Browser
WASM_TARGET=nodejs make
cd ./tests-js
npm install
npm run test-puppeteerNote tests will be executed with jest+puppeteer in Chromium installed inside node_modules.
Hot to publish to NPM with wasm-pack publish
wasm-pack publish🔋 Batteries Included
wasm-bindgenfor communicating between WebAssembly and JavaScript.console_error_panic_hookfor logging panic messages to the developer console.wee_alloc, an allocator optimized for small code size.
Contribution
PRs are welcome!
The following CI checks are run against every PR:
- No warnings from
cargo check --all-targets - No warnings from
npm run checkintests-jsdirectory - No warnings from
npm run checkindemodirectory - All tests must pass with
npm testintests-jsdirectory - Rust code must be formatted by
cargo fmt --all - Javascript/Typescript code must be formatted by prettier
npx prettier --write .
