A C SDK for DTN Bundle Protocol v7 (RFC 9171) with a declarative BPSec (RFC 9172 / 9173) runtime control layer.
For engineers coming from IP networking: BPSec is the bundle-layer analogue of IPsec. Where IPsec offers per-packet AH / ESP integrity and confidentiality services driven by SPD/SAD, BPSec offers per-block BIB / BCB services driven by a security policy engine. What's missing in the BP world is the runtime control layer that IPsec stacks expose to applications — a place to declare intent ("protect this flow with these algorithms and these keys") and let the engine handle policy installation, crypto-context reuse, IV management, and key expiry. BP-SDK fills that gap for BPSec.
Key management protocols (DTKA, BERMUDA, SAFE, BPSec-MLS, KMS
adapters) are out of scope and are integrated through the pluggable
bp_key_provider_t contract.
Status: Core scope complete. The declarative BPSec runtime control layer is implemented and verified end-to-end: the intent / policy session API, the IV / key-expiry / crypto-context machinery, and the cross-stack adapter layer (ION
bpsecadminlowering + uD3TN AAP) all work over the in-tree POSIX TCPCL backend. Payload-target BIB-HMAC-SHA-256 and BCB-AES-GCM-256 are the supported RFC 9173 defaults. What remains is small configuration surface rather than core function — non-default contexts / variants (SHA-384/512, AES-128, COSE), additional target / scope options, and richer native-config knobs — layered onto the same API without reworking it.
Public API headers (the consumer contract) live in include/bp_sdk/. Module
sources and their private headers live under src/, grouped by module. Every
module directory plus include/bp_sdk is on the include path, so
#include "bp_x.h" resolves regardless of where the caller lives.
.
├── include/bp_sdk/ Public API: bp_sdk, bp_session, bp_security_intent, bp_utils,
│ bp_adapter, bp_adapter_ion, bp_adapter_ud3tn, bp_bpsec_keys, bp_key_provider
├── src/
│ ├── core/ SDK entry, allocator, CBOR (bp_sdk, bp_utils, bp_cbor)
│ ├── bundle/ Bundle build/parse, fragments, admin records, streaming
│ ├── transport/ TCPCL, storage, pluggable backends (POSIX, Linux AF_BP)
│ ├── session/ Session engine + intent-to-policy lowering
│ ├── bpsec/ BPSec engine, keys, policy store, crypto + key providers
│ └── adapters/ Adapter Contract + bp_secure_link facade (bp_adapter)
│ ├── ion/ ION-DTN: bpsecadmin lowering + adapter
│ └── ud3tn/ uD3TN: AAP v1 client + adapter
├── tests/ Mirrors the source modules; shared test_harness.h
│ ├── integration/ Cross-cutting phase + concurrency suites
│ ├── bpsec/ session/
│ └── adapters/ facade, ion/, ud3tn/ (codec + adapter)
├── examples/ Minimal sample programs
├── Makefile Cross-platform build (Linux / macOS / MinGW)
├── build.bat Windows convenience wrapper
└── README.md
Only headers under include/bp_sdk/ are part of the stable public surface;
headers inside src/ are implementation detail and may change.
make # builds libbp_sdk.a, tests, examples
make test # runs the test suitebuild.batEither path produces build/libbp_sdk.a plus the test and example
binaries under build/.
The core SDK and the in-tree POSIX TCPCL backend have no external
dependency. You only need a host DTN stack installed if you use the
bp_secure_link adapters, which drive that stack's native BPSec engine:
ION via bpsecadmin, uD3TN via AAP.
ION-DTN (nasa-jpl/ION-DTN) —
autotools build; provides bpadmin, bpsecadmin, etc. Officially
supported on Linux (Solaris); macOS / FreeBSD / Raspberry Pi OS are
best-effort.
# prerequisites: gcc, make, autoconf, automake, libtool
git clone https://github.com/nasa-jpl/ION-DTN.git
cd ION-DTN
autoreconf -fi
./configure
make
sudo make install
sudo ldconfiguD3TN (d3tn/ud3tn, AGPLv3) — a lean implementation for POSIX and microcontrollers. Clone with submodules (bundled mbedTLS / TinyCBOR), then build the POSIX target.
# prerequisites: gcc or clang, make, git, python3 (for the AAP tools)
git clone --recurse-submodules https://gitlab.com/d3tn/ud3tn.git
cd ud3tn
make posix # binary at build/posix/ud3tn
make virtualenv # optional: AAP client tooling (pyd3tn, ud3tn_utils)Refer to each project's upstream documentation for the authoritative, version-specific build steps.
#include "bp_sdk.h"
#include <stdio.h>
int main(void) {
bp_init("ipn:1.0", NULL);
bp_send("ipn:1.1", "ipn:2.1",
"Hello, DTN", 10,
BP_PRIORITY_STANDARD, BP_CUSTODY_NONE, 3600, NULL);
bp_endpoint_t *ep;
bp_endpoint_create("ipn:2.1", &ep);
bp_bundle_t *bundle;
if (bp_receive(ep, &bundle, 5000) == BP_SUCCESS) {
printf("got %.*s\n", (int)bundle->payload_len,
(char *)bundle->payload);
bp_bundle_free(bundle);
}
bp_endpoint_destroy(ep);
bp_shutdown();
return 0;
}#include "bp_sdk.h"
#include "bp_session.h"
#include "bp_key_provider.h"
#include "bp_bpsec_keys.h"
int main(void) {
bp_init("ipn:1.0", NULL);
uint8_t key[32] = { /* 32 bytes from your KMS / file / DTKA */ };
bpsec_keystore_add(bpsdk_default_keystore(),
"k1", BPSEC_KEY_TYPE_AES,
key, sizeof(key), NULL, 0);
bp_session_t *s = bp_session_open("uplink");
bp_session_set_source(s, "ipn:1.1");
bp_security_policy_t policy = {
.mode = BPSEC_MODE_BCB_ONLY,
.bcb_context = BPSEC_CTX_AES_GCM_256,
.bcb_targets = BPSEC_TARGET_PAYLOAD,
.bcb_scope = BPSEC_SCOPE_BTSD_ONLY,
.bcb_key_ref = "k1",
};
bp_session_set_security(s, &policy);
bp_delivery_opts_t opts = {
.dest_eid = "ipn:2.1",
.lifetime_ms = 60000,
};
bp_session_send(s, (uint8_t *)"hello", 5, &opts);
bp_session_close(s);
bp_shutdown();
}bp_security_intent_t is the recommended high-level surface: declare what
protection you want (integrity, confidentiality, or both) plus a key, and
BP-SDK fills in the RFC 9173 context and scope with safe defaults. No
wire-level vocabulary leaks into application code. bp_security_policy_t
remains the advanced interface for non-default contexts or scopes.
#include "bp_sdk.h"
#include "bp_security_intent.h"
bp_security_intent_t intent = {
.service = BP_SEC_INTENT_CONFIDENTIAL, /* what, not how */
.target = BP_SEC_TARGET_PAYLOAD,
.key_ref = "k1",
};
bp_session_set_security_intent(s, &intent);Lowering defaults: INTEGRITY → BIB-HMAC-SHA-256, CONFIDENTIAL →
BCB-AES-GCM-256, INTEGRITY_AND_CONFIDENTIAL → BCB-AES-GCM-256 alone (the
GCM tag carries integrity, RFC 9172 §3.9); scope is BTSD-only. The same
bp_secure_link_set_security_intent() entry exists on the adapter facade.
The bp_secure_link facade lets the same code drive a host stack's own
BPSec engine. You declare intent + key references once; the adapter either
lowers the policy onto the stack's native configuration path (ION) or declares
the intent and relies on the node's existing BPSec configuration (uD3TN), then
hands data to the Bundle Protocol Agent (BPA), which owns BIB/BCB construction
and crypto. Switch stacks by changing one string — "ion" or "ud3tn".
#include "bp_adapter.h"
#include "bp_session.h"
int main(void) {
/* "ion" -> lowers to bpsecadmin rules (.bpsecrc) for ION-DTN
"ud3tn" -> registers an endpoint and ships data over AAP */
bp_secure_link_t *link = bp_secure_link_open("ion", "rc=node.bpsecrc");
bp_secure_link_set_source(link, "dtn://sat-1/telemetry");
bp_security_policy_t policy = {
.mode = BPSEC_MODE_BIB_BCB,
.bib_context = BPSEC_CTX_HMAC_SHA2_256, .bib_key_ref = "int-key",
.bib_targets = BPSEC_TARGET_PAYLOAD, .bib_scope = BPSEC_SCOPE_BTSD_ONLY,
.bcb_context = BPSEC_CTX_AES_GCM_256, .bcb_key_ref = "conf-key",
.bcb_targets = BPSEC_TARGET_PAYLOAD, .bcb_scope = BPSEC_SCOPE_BTSD_ONLY,
};
/* Fail-fast: an unapplicable policy is rejected here, before any send. */
bp_secure_link_set_security(link, &policy);
bp_secure_link_send(link, "dtn://ground/sink",
(const uint8_t *)"telemetry", 9, NULL);
bp_secure_link_close(link);
}The Adapter Contract (bp_adapter.h) is the invariant behind this: every
adapter sits above the host BPA and never performs crypto or pushes bundles to
a convergence layer itself.
- ION adapter (
bp_ion_policy.h) translates the declared intent intobpsecadmin-conformantevent_set+policyrulecommands (BIB-HMAC-SHA2sc_id 1, BCB-AES-GCMsc_id 2) and feeds them to ION's policy engine. BP-SDK's lowering currently emits onlykey_name, so it intentionally supports only ION's default variants (SHA-256, AES-256); other variants are rejected atset_securityrather than silently downgraded. - uD3TN adapter (
bp_aap.h) speaks AAP v1 — connect, register the sub-EID (dtn demux / ipn service number perud3tn_aap.md), send bundle. AAP v1 carries no per-flow BPSec policy, so the adapter declares intent and hands data to uD3TN; BPSec enforcement must already be configured in the node.
┌──────────────────────────────────────────────┐
│ Application │
└──────────────┬───────────────────────────────┘
│ bp_send / bp_session_send / bp_receive
┌──────────────▼───────────────────────────────┐
│ Public API (bp_sdk.h, bp_session.h) │
├──────────────────────────────────────────────┤
│ Bundle Core │ SecurityService │
│ bp_bundle │ bp_session │
│ bp_cbor │ bp_bpsec bp_bpsec_keys │
│ bp_fragment │ bp_bpsec_policy │
│ bp_admin │ │
├─────────────────┴─────┬──────────────────────┤
│ Backend abstraction │ Plugin Container │
│ bp_backend │ bp_key_provider │
│ └ POSIX TCPCL │ bp_crypto_backend │
│ └ AF_BP socket │ │
├───────────────────────┴──────────────────────┤
│ Adapter Contract (bp_adapter / secure_link) │
│ └ ION adapter → bpsecadmin rules │
│ └ uD3TN adapter → AAP client │
└───────────────────────────────────────────────┘
- Declarative intent — state integrity / confidentiality + a key via
bp_security_intent_t; RFC 9173 context and scope are filled in for you. - Declarative policy —
mode / context / targets / scope / key_refdeclared once per session for advanced (non-default) control. - Cached crypto contexts — HMAC and AES-GCM key schedules are
expanded on
bp_session_set_security()and reused for every send. - Thread-safe sends — every
bp_session_*call serialises on the session mutex; concurrent sends across sessions run in parallel. - IV uniqueness — 8-byte CSPRNG salt + 4-byte atomic counter, with
optional
bp_iv_state_provider_tfor cross-restart persistence. - Key expiry / TTL — bundle lifetime is rejected if it would
outlive the configured key (
BPSEC_ERR_KEY_TTL_MISMATCH). - Key plugin —
bp_key_provider_tlets BERMUDA / DTKA / file / KMS plug in without forking the SDK. Keystore-backed and text-file reference providers are shipped in-tree. - Crypto backend plugin —
bp_crypto_backend_tlets you swap the in-tree HMAC / AES-GCM for OpenSSL, libsodium, or hardware acceleration.
The Bundle layer is reached through bp_backend_t:
extern bp_backend_t g_posix_backend; /* TCPCL over POSIX sockets */
extern bp_backend_t g_bpsocket_backend; /* Linux AF_BP socket (kernel module) */Selecting a backend is a string match in bp_init()'s config argument
(default: POSIX). Adding a new backend means filling in a bp_backend_t
and exposing it as a global.
- C standard: C11
- Platforms: Linux, macOS, Windows (MinGW-w64)
- Architectures: x86_64, ARM64
- BPv7: RFC 9171
- BPSec: RFC 9172 + RFC 9173 default contexts