feat: Native OSCORE (RFC 8613) Integration#397
feat: Native OSCORE (RFC 8613) Integration#397stoprocent wants to merge 14 commits intocoapjs:masterfrom
Conversation
Pull Request Test Coverage Report for Build 22158602291Details
💛 - Coveralls |
…ror handling - Drop plaintext messages when OSCORE context exists (no fallback) - Limit Echo auto-retry to 1 attempt to prevent infinite loops - Carry over options and payload on Echo retry - OSCORE-encrypt Echo 4.01 challenges (not plaintext) - Store and verify Echo nonces with timingSafeEqual - Namespace token-to-context bindings by senderId to prevent collisions - Cap token bindings at 10k with LRU eviction - Validate OSCORE option parsing (reject truncated fields) - Standardize error messages to prevent information leakage - Fix _sendError missing address parameter - Use 'close' event for observe token cleanup
bdab499 to
d56d54c
Compare
|
Hey, is there any plan to review and merge? If not I will do a |
Each Server and Agent now snapshots its own frozen parameters at construction time via the new `parameters` option, allowing multiple instances with different timing/size configs without mutating the global.
Extract applyOverrides() and deriveTimings() helpers so refreshTiming() and createParameters() share a single implementation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Hey guys, if you cannot review I will move to new namespace on npm. Just please let me know I understand everyones busy. @JKRhb @Apollon77 |
|
Hi @stoprocent, sorry for getting back to you earlier! I will probably have the time to do a review by tomorrow, I hope that works for you :) In any case thank you for working on this feature, having an OSCORE implementation in node-coap is pretty exciting! :) |
|
No worries! I don’t want to rush the review. That’s the last thing I’d want to do. I just wanted to at least get it started. |
Extract SZX via parseBlockOption instead of passing raw block1 byte, and only re-segment when payload exceeds one block size.
JKRhb
left a comment
There was a problem hiding this comment.
Sorry for the delay! See a couple of initial comments below, I will continue the review in the upcoming days.
| { | ||
| "name": "coap", | ||
| "version": "1.4.2", | ||
| "version": "1.6.0", |
There was a problem hiding this comment.
I think the version should only be bumped after merging this PR.
- Make _parameters field private in Agent and RetrySend classes - Remove unnecessary leading semicolons before type assertions - Fix error message to omit colon when host is undefined https://claude.ai/code/session_017esjyFaP9jrHiBsad1ajp9
…Zjaal fix: address PR coapjs#397 review comments
|
Seems like the CI is failing now since the semicolons were necessary after all :/ Sorry for the mistake, could you revert these two particular changes (or find a different solution that does not involve the semicolons)? Thank you! |
feat: Native OSCORE (RFC 8613) Integration
Summary
Add built-in OSCORE (Object Security for Constrained RESTful Environments) support to node-coap using the
coap-oscorepackage. OSCORE provides end-to-end encryption at the CoAP message level, operating on raw binary buffers so that all existing CoAP features — block transfers, observe, caching, retransmissions — work transparently over encrypted channels.Changes
New Files
lib/oscore.tsSecurityContextManager— server-side registry for OSCORE contexts, keyed by recipientId + optional idContext. Manages token-to-context bindings for response encoding and aggregates SSN change events for persistence.lib/oscore_helpers.tstest/oscore.tsModified Files
package.jsoncoap-oscoredependencymodels/models.tsAgentOptions(oscoreOnly),CoapServerOptions(oscoreContexts,oscoreOnly), andMiddlewareParameters(OSCORE context/metadata fields) — all optional, zero breaking changeslib/incoming_message.tsoscoreContextproperty andisOscoregetter for server-side request introspectionlib/agent.tsaddOscoreContext()/removeOscoreContext(), async decode on incoming messages via_handlePlainMessage()extraction,sender.send()wrapping for transparent outgoing encryption, observe_disableFilteringfor OSCORE streams, Echo auto-retry for 4.01+Echo responseslib/middlewares.tsoscoreDecryptRequestmiddleware — detects OSCORE option, looks up context by KID, decodes raw buffer, re-parses inner packet, binds token for response encoding, handles Echo challenge (status 201) for first-request-after-reboot. UpdatedhandleServerRequestto forward OSCORE metadata.lib/server.ts_oscoreContextManagerand_oscoreOnlyfields, middleware pipeline insertion,_finishSendResponse()helper extracted for async OSCORE response encoding, token lifecycle management (unbind on response/observe finish), updated_handle()signature to accept OSCORE metadata, dynamicaddOscoreContext()/removeOscoreContext()with lazy middleware initializationindex.tsSecurityContextManager, re-exportOSCORE,OscoreContext,OscoreContextStatustypes fromcoap-oscoreREADME.mdDesign Decisions
OSCORE at buffer boundaries — Encrypt after
generate(), decrypt beforeparse(). This keeps all existing CoAP logic working on decrypted packets with zero modifications to block transfer, observe, or caching code.Agent wraps
sender.send()— Instead of modifying everygenerate()call site, the agent monkey-patchessender.send()to encrypt all outgoing buffers. This automatically handles initial requests, block1 segments, block2 continuations, and retransmissions (RetrySend stores the already-encoded buffer).Server uses middleware pipeline —
oscoreDecryptRequestslots into the existing fastseries middleware chain betweenparseRequestandhandleServerRequest. Uses.then()/.catch()(not async/await) for fastseries compatibility.Per-block OSCORE — Per RFC 8613 §4.1.3.4, each block-wise message is independently OSCORE-protected. The sender.send() wrapping handles this automatically.
All additions are optional — Every new field is optional. A server/agent without OSCORE configuration behaves identically to before.
Test Coverage
isOscoreandoscoreContext.senderIdssnevents fire via SecurityContextManager_disableFilteringactiveResults: 486 passing (474 existing + 12 new), 6 pending (unchanged), 0 failing