Skip to content

Commit f3d9a8f

Browse files
Alpn callbach examples:
This impliments a callback parsing system and negotiation of apln protocols
1 parent 290ef01 commit f3d9a8f

5 files changed

Lines changed: 854 additions & 1 deletion

File tree

‎.github/examples-manifest.yml‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,9 @@ profiles:
128128
# absent: it needs the separate wolfAsyncCrypt repo patched in.
129129
# keygen + des3: client-tls-pkcs12 needs both (PKCS12 default PBE is 3DES) or
130130
# it prints "not configured with ..." and exits 0.
131+
# alpn: {client,server}-tls-alpn need it; --enable-opensslall does not imply it.
131132
flags: >-
132-
--enable-tls13 --enable-ech --enable-writedup --enable-pkcallbacks
133+
--enable-tls13 --enable-ech --enable-alpn --enable-writedup --enable-pkcallbacks
133134
--enable-postauth --enable-cryptocb --enable-opensslall --enable-session-ticket
134135
--enable-earlydata --enable-keygen --enable-des3 --enable-static --enable-shared
135136
cflags: "-DHAVE_SECRET_CALLBACK"
@@ -950,6 +951,14 @@ examples:
950951
stdin: "hello\nshutdown\n"
951952
server_exit: killed
952953
expect: "Successful resume"
954+
# the client offers http/1.1 first, so h2 here proves the server's select
955+
# callback applied its own preference order rather than the client's.
956+
- pair:
957+
server: [./server-tls-alpn]
958+
client: [./client-tls-alpn, 127.0.0.1]
959+
stdin: "shutdown\n"
960+
server_exit: clean
961+
expect: "Server selected ALPN protocol: h2"
953962
# Deliberately not run: client-ech (external crypto.cloudflare.com:443),
954963
# {client,server}-tls-uart (needs /dev/ttyUSB0), and the four *-perf
955964
# benchmarks. All still build.

‎.gitignore‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ android/wolfssljni-ndk-sample/proguard-project.txt
8888
/tls/client-tls13
8989
/tls/client-tls13-resume
9090
/tls/client-tls13-earlydata
91+
/tls/client-tls-alpn
92+
/tls/server-tls-alpn
9193
/tls/server-tls13-earlydata
9294
/tls/client-tls-bio
9395
/tls/client-tls-cacb

‎tls/README.md‎

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ into.
119119
3. [Running](#run-ecc)
120120
121121
6. [Encrypted Client Hello](#ech)
122+
7. [ALPN](#alpn)
122123
123124
124125
@@ -1591,6 +1592,126 @@ Expected behavior:
15911592
- The server logs the received early data and replies both during early-data
15921593
processing and again after the handshake is complete.
15931594

1595+
## <a name="alpn">ALPN</a>
1596+
1597+
`client-tls-alpn` and `server-tls-alpn` demonstrate Application-Layer
1598+
Protocol Negotiation (ALPN, RFC 7301) through wolfSSL's OpenSSL-compatible
1599+
API. The client lists the application protocols it speaks in its
1600+
ClientHello, the server picks one, and both sides read the result after the
1601+
handshake.
1602+
1603+
The native wolfSSL API, `wolfSSL_UseALPN()`, takes a comma-separated list.
1604+
The OpenSSL-compatible calls used here take the list in wire format instead:
1605+
each name preceded by its length, so `"h2,http/1.1"` becomes
1606+
`{ 2,'h','2', 8,'h','t','t','p','/','1','.','1' }`.
1607+
1608+
`client-tls-alpn`:
1609+
1610+
- `wolfSSL_CTX_set_alpn_protos()` sets the default list (`http/1.1,h2`) for
1611+
every connection made from the context.
1612+
- `wolfSSL_set_alpn_protos()` overrides it for one connection when a list is
1613+
given on the command line.
1614+
- `wolfSSL_get0_alpn_selected()` reads the protocol the server chose. An
1615+
empty result means none was selected.
1616+
1617+
`server-tls-alpn`:
1618+
1619+
- `wolfSSL_CTX_set_alpn_select_cb()` registers a selection callback for every
1620+
connection, with the server's preference list (`h2,http/1.1`) as its
1621+
argument, kept in wire format like the client's. The callback receives the
1622+
client's wire-format list and hands both lists to
1623+
`wolfSSL_select_next_proto()`, which walks the server's preferences in order
1624+
and picks the first one the client offered. The callback returns
1625+
`SSL_TLSEXT_ERR_OK` with that pick, or `SSL_TLSEXT_ERR_ALERT_FATAL` when
1626+
`wolfSSL_select_next_proto()` does not return `WOLFSSL_NPN_NEGOTIATED`
1627+
(on no overlap it still fills in the client's first protocol, the NPN
1628+
fallback, so the return value must be checked). The fatal return fails the
1629+
handshake with a `no_application_protocol` alert, as RFC 7301 requires when
1630+
there is no common protocol. Returning `SSL_TLSEXT_ERR_NOACK` instead would
1631+
ignore the client's ALPN extension and continue with no protocol agreed.
1632+
- `wolfSSL_set_alpn_select_cb()` sets the callback and its argument for one
1633+
connection, overriding the context's. The server uses it when a preference
1634+
list is given on the command line.
1635+
- `wolfSSL_ALPN_GetProtocol()` reads the negotiated protocol and
1636+
`wolfSSL_ALPN_GetPeerProtocol()` the whole list the client offered, as a
1637+
comma-separated string released with `wolfSSL_ALPN_FreePeerProtocol()`.
1638+
1639+
### Building
1640+
1641+
The selection callback and `wolfSSL_get0_alpn_selected()` are part of the
1642+
OpenSSL compatibility layer enabled by `OPENSSL_ALL`, and ALPN is off by
1643+
default, so build wolfSSL with both:
1644+
1645+
```sh
1646+
./configure --enable-opensslall --enable-alpn && make && sudo make install
1647+
```
1648+
1649+
Without them both programs print a message and exit. Note that
1650+
`wolfSSL_CTX_set_alpn_protos()` and `wolfSSL_set_alpn_protos()` return 0 on
1651+
success under `--enable-opensslall` (`WOLFSSL_ERROR_CODE_OPENSSL`, matching
1652+
OpenSSL) and `WOLFSSL_SUCCESS` otherwise; the client handles both.
1653+
1654+
Then, in `wolfssl-examples/tls`:
1655+
1656+
```sh
1657+
make server-tls-alpn client-tls-alpn
1658+
```
1659+
1660+
### Running
1661+
1662+
Server in one terminal:
1663+
1664+
```sh
1665+
./server-tls-alpn
1666+
Waiting for a connection...
1667+
```
1668+
1669+
Client in another. The client lists `http/1.1` first, the server prefers
1670+
`h2`, and the server's preference wins:
1671+
1672+
```sh
1673+
./client-tls-alpn 127.0.0.1
1674+
Offering ALPN protocols: http/1.1,h2
1675+
Server selected ALPN protocol: h2
1676+
Message for server: hello
1677+
Server: I hear ya fa shizzle!
1678+
```
1679+
1680+
The server shows the callback's choice and the getters' results:
1681+
1682+
```
1683+
ALPN callback: client offered http/1.1,h2; selected h2
1684+
Client connected successfully
1685+
Negotiated ALPN protocol: h2
1686+
Client offered: http/1.1,h2
1687+
Client: hello
1688+
```
1689+
1690+
Offer only `http/1.1` and that is what gets picked; offer something the
1691+
server does not speak and the handshake fails:
1692+
1693+
```sh
1694+
./client-tls-alpn 127.0.0.1 http/1.1
1695+
Offering ALPN protocols: http/1.1
1696+
Server selected ALPN protocol: http/1.1
1697+
1698+
./client-tls-alpn 127.0.0.1 spdy/3
1699+
Offering ALPN protocols: spdy/3
1700+
ERROR: failed to connect to wolfSSL: -313 (received alert fatal error)
1701+
```
1702+
1703+
On the server side the rejection looks like this:
1704+
1705+
```
1706+
ALPN callback: client offered spdy/3; no common protocol, rejecting the handshake
1707+
wolfSSL_accept error = -405 (Unrecognized protocol name Error)
1708+
```
1709+
1710+
Start the server as `./server-tls-alpn http/1.1,h2` to flip its preference
1711+
for every connection through `wolfSSL_set_alpn_select_cb()`; the default
1712+
client then gets `http/1.1`. Send `shutdown` as the message to stop the
1713+
server. A rejected client does not stop it.
1714+
15941715

15951716
## Support
15961717

0 commit comments

Comments
 (0)