Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .github/examples-manifest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,17 @@ profiles:
--enable-earlydata --enable-keygen --enable-des3 --enable-static --enable-shared
cflags: "-DHAVE_SECRET_CALLBACK"

tlschainverifycb:
# tls plus --enable-chain-verify-cb, which no release carries yet. Kept
# separate because configure exits 1 on an unrecognized --enable-*, so
# putting it in `tls` would fail that whole dir on the stable tag.
flags: >-
--enable-tls13 --enable-ech --enable-writedup --enable-pkcallbacks
--enable-postauth --enable-cryptocb --enable-opensslall --enable-session-ticket
--enable-earlydata --enable-keygen --enable-des3 --enable-chain-verify-cb
--enable-static --enable-shared
cflags: "-DHAVE_SECRET_CALLBACK"

dtls:
# earlydata requires session tickets or PSK: without session-ticket,
# configure dies with "cannot enable earlydata without enabling session
Expand Down Expand Up @@ -959,6 +970,27 @@ examples:
# {client,server}-tls-uart (needs /dev/ttyUSB0), and the four *-perf
# benchmarks. All still build.

# Split out of tls above purely so the other 22 targets keep testing the
# released library. --enable-chain-verify-cb reached wolfSSL master in
# wolfSSL/wolfssl#11367 and no release contains it yet; configure exits 1 on
# an unrecognized --enable-*, so on a stable tag this profile cannot even be
# built. Drop the pin and fold this back into tls once a release has it.
- id: tls-chainverifycb
path: tls
profile: tlschainverifycb
wolfssl_ref: master
# Only the pair, not the whole dir: everything else here is already built
# and run by the tls entry against both refs.
build: [make, client-tls-chainverifycb, server-tls]
run:
# Sends a fixed message rather than reading stdin, so no stdin here and
# the server never sees "shutdown".
- pair:
server: [./server-tls]
client: [./client-tls-chainverifycb, 127.0.0.1]
server_exit: killed
expect: "server: I hear ya fa shizzle"

- id: dtls
path: dtls
profile: dtls
Expand Down
32 changes: 10 additions & 22 deletions .github/scripts/manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,15 @@ def cmd_wolfssl_matrix(data, refs, tier, shas=None):
rebuild -- otherwise every example pays a full wolfSSL build.
"""
pinned = dict(zip(refs, shas)) if shas else {}
profiles = sorted({e["profile"] for e in live_entries(data, tier)})
# Exactly the (profile, ref) pairs some entry asks for. A pinned entry must
# seed its own pair, and a profile only pinned entries use must NOT be built
# for the refs they exclude: configure exits 1 on an unrecognized
# --enable-*, so a profile naming a flag a ref predates fails to build.
wanted = {
(e["profile"], ref)
for e in live_entries(data, tier)
for ref in entry_refs(e, refs)
}
out = [
{
"profile": name,
Expand All @@ -350,28 +358,8 @@ def cmd_wolfssl_matrix(data, refs, tier, shas=None):
"cflags": data["profiles"][name].get("cflags", ""),
"overlay": data["profiles"][name].get("overlay", ""),
}
for name in profiles
for ref in refs
for name, ref in sorted(wanted)
]
# A pinned example needs its (profile, ref) seeded too, or its job pays a
# full wolfSSL build on every run.
for e in live_entries(data, tier):
ref = e.get("wolfssl_ref")
if ref and not any(
o["profile"] == e["profile"] and o["wolfssl_ref"] == ref for o in out
):
out.append(
{
"profile": e["profile"],
"wolfssl_ref": ref,
"wolfssl_sha": pinned.get(ref, ref),
"flags": " ".join(
data["profiles"][e["profile"]].get("flags", "").split()
),
"cflags": data["profiles"][e["profile"]].get("cflags", ""),
"overlay": data["profiles"][e["profile"]].get("overlay", ""),
}
)
print(json.dumps(out))


Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ android/wolfssljni-ndk-sample/proguard-project.txt
/tls/server-tls13-earlydata
/tls/client-tls-bio
/tls/client-tls-cacb
/tls/client-tls-chainverifycb
/tls/client-tls-callback
/tls/client-tls-cryptocb
/tls/client-tls-ecdhe
Expand Down
1 change: 1 addition & 0 deletions tls/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ debug: all
%-threaded: CFLAGS+=-pthread
%-writedup: CFLAGS+=-pthread
memory-tls: CFLAGS+=-pthread
client-tls-chainverifycb: CFLAGS+=-pthread

# compile tcp examples without the LIBS variable
%-tcp: LIBS=
Expand Down
91 changes: 91 additions & 0 deletions tls/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ into.
3. [Running](#run-ecc)

6. [Encrypted Client Hello](#ech)
7. [Chain Verify Callback](#chainverifycb)



Expand Down Expand Up @@ -182,6 +183,9 @@ For `client-tls-writedup` and `server-tls-writedup`, it is required that
wolfSSL be configured with the `--enable-writedup` flag. Remember to build
and install wolfSSL after configuring it with this flag.

For `client-tls-chainverifycb`, it is required that wolfSSL be configured
with the `--enable-chain-verify-cb` flag.


## <a name="tcp">A simple TCP client/server pair</a>

Expand Down Expand Up @@ -1596,3 +1600,90 @@ Expected behavior:

Please contact wolfSSL at support@wolfssl.com with any questions, bug fixes,
or suggested feature additions.


## <a name="chainverifycb">Chain Verify Callback</a>

A chain verify callback replaces wolfSSL's peer certificate verification
completely, so an external root of trust - an HSM or secure element holding
the anchors - can make the trust decision instead.

To run this example build wolfSSL with the feature:

```sh
./configure --enable-chain-verify-cb && make && sudo make install
```

`client-tls-chainverifycb` is a TLS client that loads no CA certificate at
all. It installs a chain verify callback, which wolfSSL calls with the
server's certificates as raw DER once it has decoded them. wolfSSL builds no
chain, verifies no signature and checks no date or host name: the trust
decision is entirely the application's.

Here the application is a small "trust service" on its own thread, standing
in for the HSM. The callback copies the chain, hands it to the service and
returns `CHAIN_VERIFY_WANT_E`, which suspends the handshake. The client keeps
re-entering `wolfSSL_connect()`; each time the callback is asked again, and
once the service has a verdict the handshake completes or fails.

What to look for:

* `wolfSSL_CTX_SetChainVerifyCb()` is the only verification-related setup.
There is no `wolfSSL_CTX_load_verify_locations()`.
* `chain_verify_cb()` verifies nothing itself. On the first call it copies the
DER buffers, which are only valid for the duration of the call, and defers.
* `service_verify()` is what the root of trust does with the chain: walk it
from the certificate nearest the anchor down to the server's own. It uses a
CertManager the SSL object knows nothing about; an HSM would use its own
store.
* The connect loop treats `CHAIN_VERIFY_WANT_E` like `WANT_READ`: something to
wait for, then call `wolfSSL_connect()` again.

Start a server presenting this repository's `certs/server-cert.pem`, for
example the one in the wolfSSL source tree. `-d` stops it asking for a client
certificate, and it serves one connection and exits:

```sh
cd wolfssl
./examples/server/server -d -p 11111 \
-c ../wolfssl-examples/certs/server-cert.pem \
-k ../wolfssl-examples/certs/server-key.pem
```

Then, from this directory:

```sh
make client-tls-chainverifycb
./client-tls-chainverifycb
trust service: anchors loaded from ../certs/ca-cert.pem, running on its own thread
client: no CA loaded, chain verify callback installed
callback: 2 certificate(s) handed to the trust service, deferring
callback: no verdict yet, deferring again
callback: trust service accepted the chain
client: handshake done, TLSv1.2
server: I hear you fa shizzle!
```

Two switches show the other outcomes:

* `-n` installs no callback. With no CA loaded the handshake fails with
`ASN_NO_SIGNER_E` (-188): wolfSSL's own verification has nothing to trust,
which is exactly why the callback exists.
* `-x` makes the service reject every chain. The handshake fails with
`CHAIN_VERIFY_CB_E` (-524) and the server receives a single
`bad_certificate` alert; the reason the service gave is never sent to it.

`-a anchor.pem` points the service at a different trust anchor, and the host
and port default to `127.0.0.1 11111`.

wolfSSL still decodes every certificate before the callback runs, so malformed
DER fails the handshake without the callback seeing it, and it still enforces
the minimum key sizes on the server's own key. Everything about trust is the
callback's. It is consulted even under `WOLFSSL_VERIFY_NONE`.

DTLS, raw public keys and verifying a stapled OCSP response are not supported
together with the callback: the setters refuse them, and a connection that
uses one of them anyway fails with `CHAIN_VERIFY_UNSUPPORTED_E` before the
callback is called.

See `ChainVerifyCb` in `wolfssl/ssl.h` for the full contract.
Loading
Loading