@derec-alliance/nodejs 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -29,6 +29,24 @@ No native dependencies are required.
29
29
 
30
30
  ---
31
31
 
32
+ ## Protocol schema
33
+
34
+ The package ships the DeRec `.proto` schema at its root so you can generate
35
+ code for any language without a `lib-derec` checkout:
36
+
37
+ - `proto/` — the 18 schema files, flat. Every import is a bare filename, so a
38
+ single include root resolves the whole closure:
39
+ `protoc --proto_path=node_modules/@derec-alliance/nodejs/proto node_modules/@derec-alliance/nodejs/proto/*.proto`
40
+ - `derec_descriptor.bin` — the same closure precompiled, well-known types
41
+ included. Needs no include path at all. Compiled with
42
+ `--include_source_info`, so code generated from it keeps the protocol's doc
43
+ comments instead of emitting bare type declarations.
44
+
45
+ The schema is versioned with the package: `vX.Y.Z` carries exactly the schema
46
+ `vX.Y.Z` was built from.
47
+
48
+ ---
49
+
32
50
  ## Design Overview
33
51
 
34
52
  The NodeJS SDK is a **thin binding layer** over the Rust implementation.
Binary file
Binary file
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@derec-alliance/nodejs",
3
3
  "description": "Node.js WebAssembly bindings for derec-library, the Rust SDK for the DeRec protocol.",
4
- "version": "0.0.3",
4
+ "version": "0.0.4",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
7
7
  "type": "git",
@@ -14,6 +14,8 @@
14
14
  "derec_library.d.ts",
15
15
  "index.js",
16
16
  "index.d.ts",
17
+ "proto",
18
+ "derec_descriptor.bin",
17
19
  "README.md",
18
20
  "LICENSE"
19
21
  ],
@@ -0,0 +1,79 @@
1
+ /*
2
+ * Copyright (c) DeRec Alliance and its Contributors.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ syntax = "proto3";
7
+
8
+ package org.derecalliance.derec.protobuf;
9
+
10
+ // CommittedDeRecShare represents a DeRec share that has been committed and
11
+ // is ready to be given to a helper for storage.
12
+ //
13
+ // During recovery, this protobuf message is returned.
14
+ //
15
+ // The commitment uses a Merkle tree. The hash of the share given to each
16
+ // helper forms a leaf in the tree. Each leaf and internal node hash is a
17
+ // SHA-384 hash.
18
+ //
19
+ // The Merkle path from a leaf to the root (i.e., the sibling nodes along
20
+ // that route) is called merklePath. The root hash is referred to as the
21
+ // "commitment".
22
+ message CommittedDeRecShare {
23
+
24
+ // Protobuf serialization of DeRecShare.
25
+ bytes deRecShare = 1;
26
+
27
+ // The Merkle root.
28
+ bytes commitment = 2;
29
+
30
+ // Represents a leaf or interior node in the Merkle path.
31
+ // isLeft is true if the sibling is a left child.
32
+ message SiblingHash {
33
+ bool isLeft = 1;
34
+ bytes hash = 2;
35
+ }
36
+
37
+ // The bottom-up Merkle path from the leaf to the root.
38
+ repeated SiblingHash merklePath = 3;
39
+ }
40
+
41
+
42
+ // DeRecShare contains the information that a sharer provides to a helper.
43
+ //
44
+ // The sharer first generates a random AES-256 key k and uses it to
45
+ // AES-GCM encrypt the secret. A random polynomial f is generated such
46
+ // that f(0) = k. The polynomial is then evaluated at a random x value
47
+ // for the intended helper, and the share contains the value y where
48
+ // f(x) = y.
49
+ //
50
+ // The computation should be done in GF(p), where p is the smallest
51
+ // 256-bit prime. The degree of the polynomial determines the threshold
52
+ // number of helpers required to reconstruct the secret.
53
+ //
54
+ // The message also includes the secretId and the share version number,
55
+ // because they must be serialized together with the share so that they
56
+ // are covered by the same signature.
57
+ message DeRecShare {
58
+
59
+ // The result of serializing the secret to be shared and encrypting it
60
+ // with a randomly generated AES-256 key.
61
+ bytes encryptedSecret = 1;
62
+
63
+ // Random 256-bit integer encoded as two's complement, big-endian.
64
+ bytes x = 2;
65
+
66
+ // The value y = f(x).
67
+ bytes y = 3;
68
+
69
+ // Numeric identifier of the secret associated with the share.
70
+ //
71
+ // Must be unique for each secret created by a sharer.
72
+ uint64 secretId = 4;
73
+
74
+ // Version number of the share.
75
+ //
76
+ // A helper is entitled to ignore any StoreShareRequestMessage whose
77
+ // version is less than or equal to the last seen version.
78
+ uint32 version = 5;
79
+ }
@@ -0,0 +1,104 @@
1
+ /*
2
+ * Copyright (c) DeRec Alliance and its Contributors.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ syntax = "proto3";
7
+
8
+ package org.derecalliance.derec.protobuf;
9
+
10
+ // CommunicationInfoKeyValue represents a single application-defined
11
+ // key-value pair used to convey user-facing identification data.
12
+ //
13
+ // # Purpose
14
+ //
15
+ // This structure is intended for:
16
+ //
17
+ // - displaying human-readable information to users during pairing
18
+ // - helping users identify and confirm counterparties
19
+ // - providing contextual metadata for UI/UX purposes
20
+ //
21
+ // # Semantics
22
+ //
23
+ // - The `key` is an application-defined identifier (e.g., "name", "phone")
24
+ // - The `value` contains the associated data, either as a string or raw bytes
25
+ //
26
+ // The protocol does not interpret these fields; they are opaque to the
27
+ // DeRec protocol and are only intended for application-level use.
28
+ //
29
+ // # Encoding
30
+ //
31
+ // Exactly one of the `value` fields MUST be set.
32
+ //
33
+ // Implementations SHOULD:
34
+ //
35
+ // - prefer `stringValue` for human-readable data
36
+ // - use `bytesValue` for structured or binary data (e.g., encoded identifiers)
37
+ //
38
+ // # Interoperability
39
+ //
40
+ // Applications SHOULD use consistent and well-known keys where possible
41
+ // (e.g., "name", "email", "phone") to improve cross-implementation UX,
42
+ // but no global registry is enforced by the protocol.
43
+ message CommunicationInfoKeyValue {
44
+ // Application-defined key identifying the type of information.
45
+ //
46
+ // Examples include "name", "email", "phone", or "accountId".
47
+ string key = 1;
48
+
49
+ // Value associated with the key.
50
+ //
51
+ // Exactly one of the following fields MUST be set.
52
+ oneof value {
53
+ // Human-readable string value.
54
+ //
55
+ // This is the preferred representation for most user-facing data.
56
+ string stringValue = 2;
57
+
58
+ // Binary value.
59
+ //
60
+ // May be used for structured or encoded data that is not naturally
61
+ // represented as a string.
62
+ bytes bytesValue = 3;
63
+ }
64
+ }
65
+
66
+ // CommunicationInfo contains application-level identifying information
67
+ // about a participant (Owner or Helper).
68
+ //
69
+ // # Purpose
70
+ //
71
+ // This message is used to:
72
+ //
73
+ // - present identifying details to users during pairing
74
+ // - assist users in verifying the identity of the counterparty
75
+ // - provide contextual metadata for UI display
76
+ //
77
+ // # Semantics
78
+ //
79
+ // - The contents are entirely application-defined
80
+ // - The protocol does not validate, interpret, or enforce any schema
81
+ // - Entries are independent key-value pairs
82
+ //
83
+ // # Security Considerations
84
+ //
85
+ // - This data MUST NOT be relied upon for authentication
86
+ // - Authentication is handled outside the protocol (e.g., in-person,
87
+ // KYC, or application-specific mechanisms)
88
+ //
89
+ // # Privacy Considerations
90
+ //
91
+ // - Applications SHOULD avoid including sensitive information unless
92
+ // necessary for the user experience
93
+ // - Users should be informed about what data is being shared
94
+ //
95
+ // # Interoperability
96
+ //
97
+ // While no schema is enforced, using common keys improves usability
98
+ // across implementations.
99
+ message CommunicationInfo {
100
+ // List of key-value entries describing the participant.
101
+ //
102
+ // Each entry represents one piece of identifying information.
103
+ repeated CommunicationInfoKeyValue communicationInfoEntries = 1;
104
+ }
@@ -0,0 +1,264 @@
1
+ /*
2
+ * Copyright (c) DeRec Alliance and its Contributors.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ syntax = "proto3";
7
+
8
+ import "google/protobuf/timestamp.proto";
9
+ import "transportprotocol.proto";
10
+
11
+ package org.derecalliance.derec.protobuf;
12
+
13
+ // ContactMessage is the only DeRec protocol message that is not wrapped
14
+ // inside a DeRecMessage.
15
+ //
16
+ // This message is exchanged out-of-band before pairing begins. It provides
17
+ // the minimum information required for another party to initiate contact and
18
+ // send the first protocol message for a new pairing session.
19
+ //
20
+ // Unlike normal DeRec protocol messages, a ContactMessage:
21
+ //
22
+ // - is not wrapped in a DeRecMessage
23
+ // - is not signed
24
+ // - is not encrypted
25
+ // - is not delivered through an already-established DeRec channel
26
+ //
27
+ // Because it is exchanged before any secure channel exists, this message
28
+ // should only contain bootstrap information needed to establish that channel.
29
+ // It is typically transferred through a trusted or user-mediated mechanism.
30
+ //
31
+ // Typical exchange mechanisms include:
32
+ //
33
+ // - QR codes displayed by either the Owner or Helper and scanned by the other
34
+ // party
35
+ // - distribution inside an application controlled by one of the parties
36
+ // - transmission over an already-existing communication channel when the
37
+ // parties have a prior relationship
38
+ //
39
+ // The recipient of this message uses its contents to construct and send the
40
+ // initial pairing request. In particular, the ContactMessage conveys:
41
+ //
42
+ // - the public encryption key material needed to protect the first inbound
43
+ // pairing message
44
+ // - a channel identifier that the recipient must echo in subsequent protocol
45
+ // messages sent to this contact
46
+ // - a nonce that binds the pairing attempt to the out-of-band contact exchange
47
+ // - the transport information required to deliver the pairing request
48
+ //
49
+ // The nonce may be transmitted together with the rest of the message or
50
+ // delivered separately by the application, depending on the authentication
51
+ // and pairing UX.
52
+ //
53
+ // # Modes
54
+ //
55
+ // `ContactMessage` supports three delivery modes for the public encryption
56
+ // material, selected by `contactMode`:
57
+ //
58
+ // - `INLINE_KEYS` (default): both `mlkemEncapsulationKey` and
59
+ // `eciesPublicKey` are present in the contact. The recipient may proceed
60
+ // directly to `PairRequestMessage`. This is the original, wire-equivalent
61
+ // behavior — existing contacts (which have no `contactMode` set) are
62
+ // parsed as `INLINE_KEYS` by virtue of the proto3 default.
63
+ //
64
+ // - `HASHED_KEYS`: `contactBindingHash` is present and both
65
+ // key fields are absent. The contact is small enough to fit in a QR code
66
+ // or short URL. The recipient must first exchange a `PrePairRequest` /
67
+ // `PrePairResponse` pair to obtain the actual keys, then verify them
68
+ // against the hash before sending `PairRequestMessage`. The hash binding
69
+ // gives the OOB-shared contact the same MITM resistance as inline-keys
70
+ // mode despite the keys traveling over the (unauthenticated) transport.
71
+ // Because the `PrePair*` messages are plaintext, the `transportProtocol`
72
+ // carried in this mode MUST be ephemeral — see the security note on
73
+ // `PrePairRequestMessage` for the required ephemeral-then-swap discipline.
74
+ //
75
+ // - `NO_KEYS`: all of `mlkemEncapsulationKey`, `eciesPublicKey`, and
76
+ // `contactBindingHash` are absent. The contact carries only
77
+ // `channelId`, `nonce`, and `transportProtocol` — small enough to be
78
+ // hand-typed or dictated over the phone. There is no cryptographic
79
+ // binding: the recipient's `PrePairRequest` triggers on-the-fly key
80
+ // generation on the contact creator, and the returned keys are used
81
+ // without hash verification. The security model rests **entirely** on
82
+ // the out-of-band delivery channel being fully trusted (e.g. a
83
+ // verified email from an already-KYC-authenticated institution). The
84
+ // `nonce` correlates the incoming request to the intended user; because
85
+ // the nonce is intentionally small for human typability, applications
86
+ // MUST rate-limit incoming `PrePairRequest`s per `channelId` and expire
87
+ // outstanding NoKeys contacts on a short timer. Not appropriate for
88
+ // peer-to-peer pairing over untrusted channels — use `HASHED_KEYS` or
89
+ // `INLINE_KEYS` there.
90
+ //
91
+ // Because nothing binds the published keys to the contact, a `NO_KEYS`
92
+ // channel MUST NOT be used until the pair is confirmed out of band. An
93
+ // implementation MUST hold it unusable — no secrets sent, inbound
94
+ // messages ignored — until both sides compare the human-readable
95
+ // fingerprint derived from the established shared key and it matches. A
96
+ // man-in-the-middle on the `PrePair` leg yields two different shared
97
+ // keys and therefore two different fingerprints, so this comparison is
98
+ // what `HASHED_KEYS` gets from its `contactBindingHash` and `NO_KEYS`
99
+ // can get no other way. This is a requirement of the mode, not a
100
+ // deployment choice.
101
+ //
102
+ // The mode selector and the per-mode field-presence invariants are enforced
103
+ // by the implementation; mixing fields across modes is a protocol error.
104
+
105
+ // Selects the delivery mode for the public encryption material in
106
+ // `ContactMessage`. See `ContactMessage` for details.
107
+ enum ContactMode {
108
+ // Default. `mlkemEncapsulationKey` and `eciesPublicKey` are inlined
109
+ // in the contact.
110
+ INLINE_KEYS = 0;
111
+ // `contactBindingHash` is inlined in the contact; keys are obtained via
112
+ // `PrePairRequest` / `PrePairResponse` and verified against the hash.
113
+ HASHED_KEYS = 1;
114
+ // No key material and no hash are inlined. The contact carries only
115
+ // `channelId`, `nonce`, and `transportProtocol`; the contact creator
116
+ // generates key material on the fly when the corresponding
117
+ // `PrePairRequest` arrives. Only appropriate when the out-of-band
118
+ // delivery channel is fully trusted, and the channel stays unusable
119
+ // until both sides confirm the fingerprint out of band — nothing else
120
+ // binds the published keys to the contact.
121
+ NO_KEYS = 2;
122
+ }
123
+
124
+ message ContactMessage {
125
+
126
+ // Selects how the public encryption material is delivered. Defaults to
127
+ // `INLINE_KEYS` (current behavior) when unset.
128
+ ContactMode contactMode = 1;
129
+
130
+ // Serialized ML-KEM-768 encapsulation key.
131
+ //
132
+ // Present only when `contactMode == INLINE_KEYS`. Used by
133
+ // the recipient to construct the initial pairing message.
134
+ optional bytes mlkemEncapsulationKey = 2;
135
+
136
+ // Serialized ECIES public key.
137
+ //
138
+ // Present only when `contactMode == INLINE_KEYS`. Used by
139
+ // the recipient to construct the initial pairing message.
140
+ optional bytes eciesPublicKey = 3;
141
+
142
+ // SHA-384 commitment to the public encryption material.
143
+ //
144
+ // Present only when `contactMode == HASHED_KEYS`. Computed
145
+ // as:
146
+ //
147
+ // ```text
148
+ // SHA-384(
149
+ // mlkemEncapsulationKey
150
+ // || eciesPublicKey
151
+ // || u64_be(nonce)
152
+ // || u64_be(channelId)
153
+ // )
154
+ // ```
155
+ //
156
+ // The recipient obtains the actual keys via `PrePairRequest` and
157
+ // recomputes this hash to confirm integrity before proceeding to
158
+ // `PairRequestMessage`.
159
+ optional bytes contactBindingHash = 4;
160
+
161
+ // Channel identifier associated with this contact.
162
+ //
163
+ // This value identifies the communication channel that the creator of this
164
+ // ContactMessage expects to use for the new pairing. After receiving this
165
+ // contact, the initiating party includes this channelId in subsequent
166
+ // protocol messages sent to the contact creator.
167
+ //
168
+ // The recipient uses this identifier to determine which local key material
169
+ // and pairing state should be used when processing the message.
170
+ //
171
+ // If an implementation generates multiple pending contact records or
172
+ // multiple encryption key pairs for different pairing attempts, it should
173
+ // retain the mapping from channelId to the corresponding local state so it
174
+ // can process inbound messages without trial decryption across all keys.
175
+ //
176
+ // This value MUST be unique within the scope required by the implementation
177
+ // to disambiguate concurrent or stored pairing sessions.
178
+ uint64 channelId = 5;
179
+
180
+ // Random nonce used to bind the pairing request to this contact exchange.
181
+ //
182
+ // The initiator includes this value in the pairing request so the creator
183
+ // of the ContactMessage can confirm that the request corresponds to the
184
+ // same out-of-band contact information that was just shared.
185
+ //
186
+ // This helps the application detect mismatched, stale, or spoofed pairing
187
+ // attempts during the bootstrap phase.
188
+ //
189
+ // Applications may choose to reveal this nonce only after sufficient
190
+ // out-of-band authentication has taken place.
191
+ uint64 nonce = 6;
192
+
193
+ // Transport information used to reach the creator of this contact.
194
+ //
195
+ // This specifies both:
196
+ //
197
+ // - the endpoint to which the first protocol message should be sent
198
+ // - the transport protocol that determines how that endpoint is interpreted
199
+ // and used
200
+ //
201
+ // The initiating party uses this field to deliver the initial pairing
202
+ // request. Subsequent protocol exchanges may continue using the same
203
+ // transport information or updated transport details, depending on the
204
+ // protocol and application behavior.
205
+ //
206
+ // Deprecated: superseded by `supportedTransports`, which carries every
207
+ // endpoint rather than one.
208
+ //
209
+ // **Reading this field directly is now incorrect.** Its meaning narrowed
210
+ // from "the endpoint" to "one entry of a list, and possibly absent": a
211
+ // creator that has moved past this field advertises only
212
+ // `supportedTransports`. A reader that was correct before this release is
213
+ // a bug now — it rejects, or fails to reach, a peer that is offering it a
214
+ // perfectly good endpoint. Resolve both spellings instead of reading
215
+ // either: `advertised_endpoints()` (Rust and TypeScript),
216
+ // `AdvertisedEndpoints()` (Go), `AdvertisedEndpoints()` (.NET).
217
+ //
218
+ // Senders still populate it: a sender that sets `supportedTransports`
219
+ // MUST also set this to a single best-compatibility choice, so
220
+ // implementations predating the list still pair.
221
+ //
222
+ // **Scheduled for removal in v0.0.5.**
223
+ TransportProtocol transportProtocol = 7 [deprecated = true];
224
+
225
+ // Timestamp indicating when the sender created this message.
226
+ //
227
+ // This value is expressed in UTC and can be used for:
228
+ //
229
+ // - replay detection
230
+ // - timeout handling
231
+ // - logging and observability
232
+ google.protobuf.Timestamp timestamp = 8;
233
+
234
+ // Every transport endpoint the creator of this contact can be reached on.
235
+ //
236
+ // Each entry is a complete endpoint: a URI plus the protocol that says
237
+ // how to interpret it. The list is written in the creator's own
238
+ // preference order.
239
+ //
240
+ // # Selection is the recipient's
241
+ //
242
+ // A recipient picks whichever entry suits it, ordered by its **own**
243
+ // preference rather than the sender's. The order here expresses
244
+ // availability, not a ranking the recipient must honor.
245
+ //
246
+ // # Compatibility
247
+ //
248
+ // Absent means "only `transportProtocol` is offered", which is how every
249
+ // implementation predating this field behaves. A sender populating this
250
+ // list MUST also set `transportProtocol` to a single best-compatibility
251
+ // choice so those implementations still pair.
252
+ //
253
+ // # Security
254
+ //
255
+ // `contactBindingHash` does not cover this field, exactly as it does not
256
+ // cover `transportProtocol`. An attacker able to rewrite an
257
+ // out-of-band contact can therefore remove entries. Removing the secure
258
+ // ones leaves only plaintext, which the recipient's transport policy
259
+ // refuses unless plaintext has been explicitly opted into. DeRec messages
260
+ // are signed and encrypted at the application layer on every transport,
261
+ // so substituting one secure transport for another is not a loss of
262
+ // confidentiality or authenticity.
263
+ repeated TransportProtocol supportedTransports = 9;
264
+ }