Ivyware Security Authenticated & encrypted by default ECDH P-256 · HKDF · AES-256-GCM · ECDSA Melbourne, AU
Ivyware

Ivyware/Security

Security — on by default

Verified.
Encrypted.
Or it refuses to start.

Since August 2026 a TargetCore hub verifies who it is talking to and encrypts what it says, without being asked. Every connection gets a fresh ECDH P-256 agreement, an HKDF-SHA256 session key and AES-256-GCM. Every login carries an ECDSA P-256 signature bound to that exact exchange. There is no algorithm negotiation, because a security upgrade an attacker can decline is one they will decline.

And a hub that requires authentication but holds no identity does not start up quietly insecure. It refuses to arm, names the missing file, and tells you the one-line fix.

Key agreement ECDH P-256 Derivation HKDF-SHA256 Session cypher AES-256-GCM Identity ECDSA P-256 Negotiation none Default required

The handshake

A client dials a service. Nine steps later the link is armed, and a stranger with a perfectly valid key of its own is turned away. The code beside it is what an operator writes at each tier: the kernel API, the facade, or a script.

CLIENT HUB Root.Client identity: Root.Client.key ECDSA P-256 · CREATED ONCE, FOUND THEREAFTER agreement: Root.Client.agree.pub ECDH P-256 · A SEPARATE KEY OTHERS SEAL TO SERVICE HUB Root.Service allow-list ✓ Root.Client EXACT COMPARE · SEVERAL KEYS PER ADDRESS revocation ✓ not revoked FAIL-CLOSED · SIGNED · MERGED, NEVER REPLACED RequireAuth · RequireRevocation · DEFAULT TRUE SOCKET UP · UNTRUSTED · NO MESSAGES YET a·G b·G ab·G → HKDF-SHA256 → session key FRESH PER CONNECTION · NEVER ON THE WIRE AES-256-GCM · 96-bit nonce · 128-bit tag LOGIN · ECDSA sig SIGNATURE BINDS THE PROOF TO THIS EXCHANGE — A PROOF MINTED FOR ONE CONNECTION IS REFUSED ON ANY OTHER ARMED · every link of this hub authenticated and encrypted A STRANGER Root.Rogue valid key · signs perfectly LOGIN · sig ok REFUSED · unknown peer · not on the allow-list

What the operator writes


        

Event log — P2Pevent

What is on by default

Posture · September 2026

The operator-facing policy lives in the repository as SECURITY.md, with the threat model beside it. This is the short form. A tick means enforced without configuration and covered by a test; amber means on, with a scope you should read.

Peer authentication required

A login must carry an ECDSA P-256 signature bound to the connection it arrived on. A hub that requires this and cannot enforce it refuses to arm, naming the missing file, instead of starting up quietly insecure.

Every connection encrypted

Ephemeral ECDH P-256 to HKDF-SHA256 to AES-256-GCM, one key per connection, on every transport including named pipes and serial. No algorithm negotiation.

Pre-login traffic blocked

An application message arriving before login is discarded and the connection dropped. The receive path validates framing bounds before anything is parsed, on both untrusted entry points.

Source address bound to identity

A message's source address is checked against the identity its connection logged in as, on links to descendants and to unrelated peers. Downward relays carry the origin's own signature over addresses, name and body digest, so a relaying hop cannot forge a source.

Revocation, fail-closed, distributed

An allow-list may hold several keys per address; a signed, versioned revocation list from one named authority withdraws any of them, live, and is merged rather than replaced. A hub must name a list or explicitly decline one. Saying nothing does not start.

Accept cap on TCP

A service connection caps concurrent accepted connections, 1024 by default, and refuses the next one at accept rather than leaving it pending. Pipes are bounded by instance count, serial and DMX by physical ports.

Named pipes hardened on Windows

Remote clients rejected, an explicit protected DACL in place of the platform default that granted Everyone read access, first-instance-only creation so a squatter cannot join, and clients open with identification-level impersonation only.

!
End-to-end sealing and origin attestation

On by default in the kernel for bodies that cross an intermediate hub. The facade's secure-hub flag deliberately secures a hub and its edges only, because routed traffic between hubs that are not each other's peers would otherwise be refused wholesale. Raise the switches yourself for pairs you know.

!
Replay refusal

Opt in. A per-hub cache of accepted signatures refuses the same signed block twice; it is bounded by count as well as age, so the residual exposure costs an attacker that many genuinely signed blocks through one hub. A deployment that legitimately re-delivers should leave it off.

Rekeying within a connection

Not implemented. Session keys live as long as the connection. It is on the roadmap in the repository, in priority order, and the numbering is stable.

The review, in the open

SECURITY_REVIEW.md

July 2026

Seventeen findings, kept verbatim

A static review across six layers, from the socket receive path to the C ABI, found a pre-authentication heap overflow, a handshake with no real confidentiality, and no authentication gate at all. The findings are preserved as written, with line numbers, and the bottom line that said "not safe for untrusted peers" is marked superseded rather than deleted.

August 2026

Sixteen closed, one named

Every finding was re-verified against source, finding by finding. Sixteen are closed with a test each. The seventeenth is a caller invariant, not a locatable bug: a cached raw pointer into the store does not survive heap growth. It is documented at the declarations it governs rather than pretended away.

Deleted, not deprecated

The old crypto is gone

The Diffie-Hellman, Rijndael and big-integer classes older documentation described as the security story were never reachable and never constructed, and one carried an unbounded pre-authentication parse. They are deleted from the tree, and a continuous-integration check keeps them from returning as build inputs.

Tests

Twenty-nine security tests and three fuzzers

The kernel's test suite carries twenty-nine security-labelled cases plus fuzz harnesses over the receive framing and the authenticated block parsers. The web front door adds a fuzzer over HTTP parsing. A fuzz campaign found and closed a further pre-authentication finding in September, which is what fuzzers are for.

Two platforms

Same posture on Linux

Cryptography is Windows CNG on Windows and OpenSSL 3 on Linux, behind one interface, pinned to each other by a known-answer test. Named-pipe access control has no DACL on Linux; the AF_UNIX socket lives in a mode 0700 directory instead, and the trust class holds in every mode.

Not audited

Closed is not audited

Every named finding is closed and regression-tested. The configured path has not been externally audited, and we say so in the policy. Msgcore reads binary images whose headers declare their own extent; treat an image from a source you do not control as unsafe to load.

Where to deploy it, honestly

Scope

Appropriate today

Trusted networks first

  • Loopback and single-machine inter-process messaging, where the pipe transport now enforces local-only access.
  • Trusted LAN segments and lab networks.
  • Directly attached hardware buses over RS-232 and in-process exchange.
  • Research, teaching and modernisation work on the codebase itself.

Read the policy first

Untrusted networks

Authentication and encryption are on by default and the review's findings are closed, but the path has not been externally audited. Before exposing a hub to peers you do not control: provision identities, keep the revocation list, cap accepts, and read the hardening advice in SECURITY.md. If you are handling personal data, payments or safety-critical control, talk to us before you talk to the internet.

Found something? Report it privately through the contact page. The policy sets out what to include and what to expect back.

What the refusal looks like

Fail at deployment, not at 3am

A hub that requires authentication and holds no keys does not refuse an attacker, it refuses everyone, at the first peer's login. So the gate moved to arming. This is the whole of the message, and the migration is one line either way.

SpawnHub() on an unprovisioned hub

P2PeerHub(Root.Service) will not arm: no identity key - SetIdentity(path,true) or ProvisionAuth()
ADVICE : Auth is required by default. Provision this hub, or RequireAuth(false) and mean it.