ALPN in SecureTransport

I'm writing a toy http server using CFNetwork/CFStream/SecureTransport. It's been a fun exercise and http/https is all working fine.


I've now moved on to adding http2, and thus I want to support ALPN so ciients can detect 'h2' support.


When connecting to my server via safari I can see the ALPN data from safari on the wire, e.g. "\2h2\5h2-16\5h2-15\5h2-14\10spdy/3.1\6spdy/3\10http/1.1\0", and upon the first kCFStreamEventHasBytesAvailable I can dump the SSL/TLS info from the stream which correctly says the expected protocol and cipher.

However, when I also add the following to investigate the ALPN data within SSLContext

SSLContextRef secContext = (SSLContextRef)CFReadStreamCopyProperty(readStream, kCFStreamPropertySSLContext);
CFArrayRef protocols = NULL;
OSStatus err = SSLCopyALPNProtocols(secContext, &protocols)

Then protocols remains NULL, and err is always errSecParam.


Given I actually have TLS/SSL working, the failure of using the ALPN calls to do anything useful makes me wonder if ALPN is actually implemented in SecureTransport for server usage... or am I doing something wrong, or is this framework being deprecated and should I be using something else, i.e. the nw_* framework?


The above represents testing, the true objective is to use SSLSetALPNProtocols() to set "h2" as a server protocol at the same point in my code where I add certficates and enable TLS/SSL, but this seems to have no effect...


In other investigation I've tried adding SSLSetALPNProtocols() and SSLCopyALPNProtocols() into Apples own TLSTool code, again with no useful outcome.



Tested on 10.14.4

Accepted Reply

I'm writing a toy http server using CFNetwork / CFStream / SecureTransport.

Why? I understand why folks would use this stack for for a real project, but for modern code with no deployment target restrictions you should use the Network framework; it’s much nicer.

I think that Secure Transport supports server-side ALPN, although I’ve never used this so I can’t be sure. The most common cause of problems like this is that you’re making the call in the wrong state, but it seems like you’re doing the right thing here (looking for it when you get the first

kCFStreamEventHasBytesAvailable
event).

Regardless, I recommend that you do this just using the Network framework (although be aware that the API to wrangle ALPN comes,

sec_protocol_options_add_tls_application_protocol
and
sec_protocol_metadata_get_negotiated_protocol
, Security framework).

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Replies

I'm writing a toy http server using CFNetwork / CFStream / SecureTransport.

Why? I understand why folks would use this stack for for a real project, but for modern code with no deployment target restrictions you should use the Network framework; it’s much nicer.

I think that Secure Transport supports server-side ALPN, although I’ve never used this so I can’t be sure. The most common cause of problems like this is that you’re making the call in the wrong state, but it seems like you’re doing the right thing here (looking for it when you get the first

kCFStreamEventHasBytesAvailable
event).

Regardless, I recommend that you do this just using the Network framework (although be aware that the API to wrangle ALPN comes,

sec_protocol_options_add_tls_application_protocol
and
sec_protocol_metadata_get_negotiated_protocol
, Security framework).

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thank you, and for the tip where to find the related ALPN API.


I'd avoided the new network framework since the docs on the Apple website for this framework are extremely sparse, but I've just now looking at the actual header files and these do have useful documentation in them. Suggest someone updates the online docs ;-)

Follow up - I'd just like to say, that was ridiculously easy!


I took the "nwcat" example code, monkey patched in my certficate loading code and enabled ALPN via replacing the configure_tls code in the create_and_start_listener() function with:

SecIdentityRef identity = ... load the certificate...;
sec_protocol_options_set_local_identity(sec_options, sec_identity_create(identity));
sec_protocol_options_add_tls_application_protocol(sec_options, "h2");


Connected via safari, and can clearly see that it's sending a http2 connection preface. Thank you.