Introduction and Overview
AcornSSL provides a BSD-like socket interface for communication through the TCP/IP protocol stack to resources that require a secure connection. It uses the mbedTLS library for the majority of its implementation; the module itself calls the corresponding mbedTLS functions and wraps them into a set of SWIs that mirror the equivalent calls provided by the Internet module's BSD sockets interface.
At the time of writing, AcornSSL supports all current TLS standards:
- TLS version 1.0
- TLS version 1.1
- TLS version 1.2
The earlier SSL version 2 and 3 standards are considered too insecure and are not supported at all, and TLS version 1.0 is likely to be withdrawn in future. The client SWI interface offers no mechanism to select which protocol version is used; attempting to connect to a server that only offers one of the insecure methods will fail.
Application authors who need a network resource protected by TLS, most commonly for the https: URL scheme handled by AcornHTTP, use AcornSSL either directly through its SWI interface, or indirectly through a higher-level fetcher module that already knows how to drive it. Because untrusted or unexpected certificates may need to be approved by a person before a connection can proceed, AcornSSL also provides a desktop user interface: a small Wimp task that displays a certificate confirmation dialogue whenever a session's server certificate cannot be verified automatically.
Technical Details
Handles and sessions
Every AcornSSL SWI operates on an ssl handle, obtained from SWI AcornSSL_Creat or SWI AcornSSL_CreateSession. This handle is superficially similar to a socket handle from SWI Socket_Creat, but it is not a socket handle and must be treated as an opaque value: it must not be used with the Internet module's socket SWIs, and a socket handle must not be used with the AcornSSL SWIs.
The AcornSSL SWIs are deliberately close analogues of the corresponding socket SWIs, so that code already written against a BSD-like sockets API can be adapted to use secure connections with minimal change. Which SWIs are needed depends on whether an insecure exchange is required before the connection becomes secure. Some protocols need an initial plain-text exchange to negotiate whether, or how, to proceed to a secure connection; others are secure from the first byte:
| Secure from the start | Secure after negotiation |
|---|---|
| AcornSSL_Creat | Socket_Creat -- get a handle |
| AcornSSL_Connect | Socket_Connect -- connect to the remote host |
| (secure dialogue) | Socket_Read / Socket_Write -- insecure dialogue |
| AcornSSL_CreateSession -- associate the handle | |
| AcornSSL_Read / AcornSSL_Write | AcornSSL_Read / AcornSSL_Write -- secure dialogue |
| AcornSSL_Close | AcornSSL_Close then Socket_Close |
SWI AcornSSL_CreateSession is the SWI used to upgrade an existing, already-open socket (typically one obtained from SWI Socket_Creat and already connected) into a secure session, without needing to close and reopen the connection.
Data structures
Several AcornSSL SWIs operate on fixed-format data blocks that are shared with the Internet module's socket SWIs. These are documented here so that the individual SWI definitions below can refer to them, rather than repeating the field layout at every point of use.
Address structure
SWI AcornSSL_Connect, SWI AcornSSL_Getpeername, and SWI AcornSSL_Getsockname all use the same address structure as the corresponding Socket_ SWIs. For the IPv4 sessions AcornSSL currently supports, this is the 16-byte sockaddr_in block:
| Offset | Size | Field | Contents |
|---|---|---|---|
| 0 | 1 | sin_len | Length of the structure, in bytes (16) |
| 1 | 1 | sin_family | AF_INET (2); no other address family is currently supported |
| 2 | 2 | sin_port | Port number, in network byte order |
| 4 | 4 | sin_addr | IPv4 address, in network byte order |
| 8 | 8 | sin_zero | Reserved, must be zero |
On SWI AcornSSL_Connect the caller supplies this structure to describe the remote host to connect to. On SWI AcornSSL_Getpeername and SWI AcornSSL_Getsockname the SWI fills it in to describe the remote or local end of the connection respectively.
Status structure
SWI AcornSSL_Stat fills in the same struct stat block as SWI Socket_Stat, defined in C:TCPIPLibs.sys.h.stat. Because this structure was designed to describe files rather than sockets, most of its fields are not meaningful for an ssl handle and should be ignored; only st_mode, which identifies the handle as a socket, and st_blksize, which gives a hint of the optimal I/O size for the connection, are populated with useful values.
| Offset | Size | Field | Contents |
|---|---|---|---|
| 0 | 2 | st_dev | Undefined |
| 4 | 4 | st_ino | Undefined |
| 8 | 2 | st_mode | File type and mode; S_IFSOCK is set to identify the handle as a socket |
| 10 | 2 | st_nlink | Undefined |
| 12 | 2 | st_uid | Undefined |
| 14 | 2 | st_gid | Undefined |
| 16 | 2 | st_rdev | Undefined |
| 20 | 4 | st_size | Undefined |
| 24 | 8 | st_atimespec | Undefined |
| 32 | 8 | st_mtimespec | Undefined |
| 40 | 8 | st_ctimespec | Undefined |
| 48 | 4 | st_blksize | Optimal block size for reads and writes on this connection |
| 52 | 4 | st_blocks | Undefined |
| 56 | 4 | st_flags | Undefined |
| 60 | 4 | st_gen | Undefined |
The buffer passed in R1 must be large enough to hold the whole structure (64 bytes).
Blocking versus non-blocking operation
select()
An application that wants to use select() semantics cannot select directly on an ssl handle. The semantics of select() require a set of bitmaps with bits relating to each socket number, but ssl handles are opaque values that do not contain small incrementing integers, so the FD_SET, FD_CLR, and FD_ISSET macros cannot be used with them.
If an application's structure requires select(), the recommended approach is to create the underlying socket with SWI Socket_Creat and then associate it with an ssl handle using SWI AcornSSL_CreateSession. Because SWI Socket_Creat does return small incrementing socket numbers, those can be used with select() as normal.
A successful select() does not necessarily mean application data is available. Data that appears ready for reading or writing on the underlying socket may in fact be the security library handling an alert, or updating a session ticket, so a successful select() can still be followed by SWI AcornSSL_Recv or SWI AcornSSL_Send returning nothing.
FIONBIO
SWI AcornSSL_Ioctl can be used to mark an ssl handle as non-blocking, by applying the FIONBIO command with a pointer to an integer value of 1 (to enable non-blocking operation) or 0 (to disable it).
If this is done before connecting, the connect operation also becomes non-blocking and returns EINPROGRESS. Since it is not possible to select() on an ssl handle directly, there is no convenient way to discover when such a connection becomes ready for writing. The simplest workaround is to leave the connection as blocking (the default) until after it has connected, and only then use FIONBIO before performing reads and writes. Alternatively, an application may proceed regardless and expect ENOTCONN from any SWI that needs an established connection: SWI AcornSSL_Recv, SWI AcornSSL_Send, SWI AcornSSL_Read, SWI AcornSSL_Write, and SWI AcornSSL_Getpeername.
Extension socket options
A session can be configured by calling SWI AcornSSL_Getsockopt or SWI AcornSSL_Setsockopt after SWI AcornSSL_Creat or SWI AcornSSL_CreateSession, but before SWI AcornSSL_Connect, to alter parameters relating to that specific session. Two extension options are defined, in addition to the standard socket options recognised by the Internet module:
| Option | Meaning |
|---|---|
| 11E0 |
SO_ACORNSSL_HOSTNAME -- associates the expected
name of the remote host with the current session. Because name resolution
is performed by the client, and a single peer can present several alias
names, there would otherwise be no way to cross-check, from the IP
address alone, whether the certificate presented by the server was issued
to the expected name. The expected host name is also required if the
server administrator has enabled SNI (Server Name Identification); without
it no name is sent during handshaking, and the connection is likely to be
refused. A name pointer of NULL (the default, if
this option is never set) skips checking the peer's name and does not
send an expected name while handshaking.
Unlike most options, the value of this option is itself a pointer, so it does not follow the usual pattern of R3 pointing at a buffer that holds the value:
|
| 11E1 | SO_ACORNSSL_PROMPTTIME -- defines how long, in centiseconds, the certificate confirmation dialogue will wait for a response if a certificate is flagged as bad. A value of zero displays no prompt and fails the connection immediately. To set the option, optval is a pointer to the value in centiseconds and optlen is 4 (the size of an integer); to read it back, optval is a pointer to receive the integer, and optlen is a pointer to an integer holding 4. |
Loading the module
Application authors who want to be sure the AcornSSL SWIs are available should use *RMEnsure in the usual manner, for example:
RMEnsure AcornSSL 1.23 RMLoad System:Modules.Network.URL.AcornSSL
RMEnsure AcornSSL 1.23 Error Application requires AcornSSL 1.23 or later
A more comprehensive series of *RMEnsure commands would be needed if an earlier AcornSSL is already loaded with active clients, because loading a second copy of the module would end those sessions. When tracking security enhancements it can be assumed that the latest available version is always preferable, even at the expense of terminating existing sessions.
The System: path variable should be used, as shown above, rather than a reference to a specific RISC OS version's directory inside !System; the module may in future be shipped with builds tailored to different RISC OS versions.
SWI Calls
AcornSSL's SWIs occupy the chunk based at &50F80. Except where noted, each SWI is a close analogue of the correspondingly named SWI provided by the Internet module for plain BSD sockets, but operating on an ssl handle rather than a socket handle.
| R0 | = |
| ||||||||||||
| R1 | = | type, as for SWI Socket_Creat; SOCK_STREAM for a TLS connection | ||||||||||||
| R2 | = | protocol, as for SWI Socket_Creat; normally 0 | ||||||||||||
| R0 | = | ssl handle |
Analogous to SWI Socket_Creat, this SWI initialises a new secure session, returning a handle to use with the other AcornSSL SWIs. The handle is not a socket handle and must be treated as opaque.
If a socket handle from SWI Socket_Creat has already been opened and needs to be upgraded to a secure session, use SWI AcornSSL_CreateSession instead.
| R0 | = | ssl handle | ||||||
| R1 | = |
operation, as for SWI Socket_Ioctl:
| ||||||
| R2 | = | pointer to argument, dependent on the operation; for FIONBIO this is a pointer to a 4-byte integer that is zero to disable non-blocking operation, or non-zero to enable it |
| R0 | = | 0 for success |
Analogous to SWI Socket_Ioctl. In particular, the FIONBIO operation is used to switch an ssl handle between blocking and non-blocking operation; AcornSSL records this setting on the ssl handle itself, separately from the underlying socket, so that it can apply it consistently to the TLS reads and writes it performs on the caller's behalf.
| R0 | = | ssl handle |
| R1 | = | pointer to Address structure, describing the remote host to connect to |
| R2 | = | size of the address structure, in bytes (16 for an IPv4 address) |
| R0 | = | 0 for success |
Analogous to SWI Socket_Connect.
| R0 | = | ssl handle |
| R1 | = | direction of shutdown |
| R0 | = | 0 for success |
Analogous to SWI Socket_Shutdown.
| R0 | = | ssl handle |
| R0 | = | 0 for success |
Analogous to SWI Socket_Close. Closing the secure session with this SWI does not close the underlying socket handle when the session was created with SWI AcornSSL_CreateSession; the caller remains responsible for closing that socket with SWI Socket_Close.
| R0 | = | ssl handle |
| R1 | = | option level, as for SWI Socket_Getsockopt; the Extension socket options are also recognised at level SOL_SOCKET |
| R2 | = | option, as for SWI Socket_Getsockopt or one of the Extension socket options |
| R3 | = | pointer to buffer to receive the option value |
| R4 | = | pointer to a word holding the size of the buffer at R3; updated on exit to the size actually written, for standard socket options |
| R0 | = | 0 for success |
Analogous to SWI Socket_Getsockopt. As well as the standard socket options, this SWI recognises the Extension socket options described under Technical Details.
For the AcornSSL extension options, R4 is not updated on exit; the caller must set it to 4 on entry, and it is left unchanged. For SO_ACORNSSL_HOSTNAME specifically, R3 does not follow the usual buffer pattern: see the option's own description under Extension socket options for the exact indirection it uses.
| R0 | = | ssl handle |
| R1 | = | pointer to data to send |
| R2 | = | amount of data to send |
| R0 | = | amount of data written |
Analogous to SWI Socket_Write; equivalent to SWI AcornSSL_Send with flags of 0.
| R0 | = | ssl handle | ||||||||||||||||||||||||
| R1 | = | pointer to data to receive | ||||||||||||||||||||||||
| R2 | = | size of data buffer | ||||||||||||||||||||||||
| R3 | = |
option flags:
| ||||||||||||||||||||||||
| R0 | = | amount of data received |
Analogous to SWI Socket_Recv.
While a secure link is being established, and a certificate exchange is taking place that may require the user to confirm an untrusted certificate, the link's status may be reported as ENOTCONN, so as to match the response the Internet module gives before a link is up.
If MSG_DONTWAIT and MSG_WAITALL are both set, or both clear, the call uses whichever blocking mode was last set with SWI AcornSSL_Ioctl (blocking, unless FIONBIO has been used to enable non-blocking operation).
| R0 | = | socket handle from Socket_Creat | ||||||||||||||||||
| R1 | = |
| ||||||||||||||||||
| R2 - R3 | = | dependent on reason code | ||||||||||||||||||
| R0 | = | ssl handle |
This SWI performs a similar function to SWI AcornSSL_Creat, but allows the caller to hand over a previously opened socket, so that an insecure exchange over that socket can be followed by a switch to a secure session without closing the connection.
| R0 | = | socket handle from Socket_Creat |
| R1 | = | CreateSession_New (0) |
| R0 | = | ssl handle |
Creates a new secure session that takes over the given socket handle.
| R0 | = | socket handle from Socket_Creat |
| R1 | = | CreateSession_ReuseAuth (1) |
| R2 | = | ssl handle of another secure session already authenticated with the server |
| R0 | = | ssl handle |
Creates a new secure session that takes over the given socket handle, using the authentication already established by another, already-authenticated secure session to the same server to complete the connection. This avoids the need to re-authenticate, and re-run the certificate confirmation process, for a second connection that the caller already knows to be trusted.
| R0 | = | ssl handle |
| R1 | = | pointer to buffer to receive the Address structure |
| R2 | = | pointer to a word holding the size of the buffer at R1; updated on exit to the size of the structure written |
| R0 | = | 0 for success |
Analogous to SWI Socket_Getpeername; returns the address of the remote host at the other end of the underlying connection.
| R0 | = | ssl handle |
| R1 | = | pointer to buffer to receive the Address structure |
| R2 | = | pointer to a word holding the size of the buffer at R1; updated on exit to the size of the structure written |
| R0 | = | 0 for success |
Analogous to SWI Socket_Getsockname; returns the local address in use by the underlying connection.
| R0 | = | ssl handle |
| R1 | = | option level, as for SWI Socket_Setsockopt; the Extension socket options are also recognised at level SOL_SOCKET |
| R2 | = | option, as for SWI Socket_Setsockopt or one of the Extension socket options |
| R3 | = | pointer to buffer holding the option value to set, for most options (see below for an exception) |
| R4 | = | size of the option value, in bytes |
| R0 | = | 0 for success |
Analogous to SWI Socket_Setsockopt. As well as the standard socket options, this SWI recognises the Extension socket options described under Technical Details. Extension options may only be set after SWI AcornSSL_Creat or SWI AcornSSL_CreateSession, and before SWI AcornSSL_Connect.
SO_ACORNSSL_HOSTNAME is an exception to the usual buffer pattern for R3: because the value being set is itself a pointer, R3 must be the address of the host name string directly, not the address of a variable holding that address. See the option's own description under Extension socket options for details.
| R0 | = | ssl handle |
| R1 | = | pointer to buffer to receive the Status structure (64 bytes) |
| R0 | = | 0 for success |
Analogous to SWI Socket_Stat; fills in the Status structure for the underlying socket of the ssl handle. As with SWI Socket_Stat, most fields of the structure are not meaningful for a socket and should be ignored; only st_mode and st_blksize carry useful information. See Technical Details for the full field layout.
| R0 - R9 | preserved | |
| R0 | = | 100 times the module's version number |
Analogous to SWI Socket_Version.
If extra features are added to AcornSSL in the future, this version number can be read to determine whether the loaded copy of the module is able to support those features.
| R0 | = | ssl handle |
| R1 | = | pointer to data to receive |
| R2 | = | size of data buffer |
| R0 | = | amount of data received |
Analogous to SWI Socket_Read; equivalent to SWI AcornSSL_Recv with flags of 0.
| R0 | = | ssl handle | ||||||||||||||||
| R1 | = | pointer to data to send | ||||||||||||||||
| R2 | = | amount of data to send | ||||||||||||||||
| R3 | = |
option flags:
| ||||||||||||||||
| R0 | = | amount of data written |
Analogous to SWI Socket_Send.
While a secure link is being established, and a certificate exchange is taking place that may require the user to confirm an untrusted certificate, the link's status may be reported as ENOTCONN, so as to match the response the Internet module gives before a link is up.
If MSG_DONTWAIT and MSG_WAITALL are both set, or both clear, the call uses whichever blocking mode was last set with SWI AcornSSL_Ioctl (blocking, unless FIONBIO has been used to enable non-blocking operation).
This SWI number is reserved for future use. AcornSSL does not currently define or implement it, and calling it returns the standard Bad SWI error.
Service Calls
AcornSSL issues Service_URLModule_SSL to communicate important events about its own availability. Client software should not claim this service call.
| R0 | = |
| ||||||
| R1 | = | &83E02 (Service_URLModule_SSL) | ||||||
| R2 | = | the module's version number multiplied by 100 |
| R0 - R2 | preserved | |
AcornSSL issues this service call, with R0 set to 0, when it becomes available, and again, with R0 set to 1, when it is about to become unavailable (for example when it is being re-loaded, or killed). Recipients must preserve all registers and must not claim this service call.
AcornHTTP re-announces AcornSSL's presence by generating this service call itself if AcornHTTP starts after AcornSSL has already announced itself, so that other modules relying on the https: scheme still see the announcement even if their own startup missed the original call.
Commands
Starts the Wimp task that displays AcornSSL's certificate confirmation dialogue. This command takes no parameters, and is issued automatically by AcornSSL itself, in response to Service_StartWimp, when the desktop starts; it should not normally be issued directly.
If the task is already running, the command reports an error rather than starting a second copy.
Certificate Confirmation Dialogue
When AcornSSL cannot fully verify a server's certificate chain against its trusted root certificates, or the certificate presented fails one of its other checks, the connection does not fail outright. Instead, provided the desktop is running and SO_ACORNSSL_PROMPTTIME has not been set to zero for that session, AcornSSL asks the user whether the connection should be allowed to proceed. While this confirmation is pending, and while any handshaking that requires it is in progress, calls such as SWI AcornSSL_Recv and SWI AcornSSL_Send report the connection's status as ENOTCONN.
The certificate confirmation dialogue is presented by a small Wimp task, started automatically as *Desktop_AcornSSL when the desktop starts. This task is separate from the AcornSSL module itself: the module runs the TLS handshake and evaluates the certificate chain in the background, and communicates with the task only to display a dialogue and collect the user's decision.
What the dialogue shows
The dialogue is shown once for each certificate in the chain that needs the user's attention, starting with the certificate presented by the remote server (the subject) and, where the user chooses to inspect it, working back towards the root of trust (the issuer). Each certificate's dialogue shows:
- the certificate's issuer
- the certificate's subject
- the period for which the certificate is valid
- the certificate's serial number
- the certificate's signature
Where a check on the certificate has failed, the corresponding field is marked to draw attention to the specific problem: an expired, not-yet-valid, revoked, or otherwise unacceptable validity period; a signature the module could not verify; or an issuer that is not among the trusted roots. The final certificate in the chain is the root; it has no further issuer to inspect, so its dialogue omits the button used to move up the chain.
Responding to the dialogue
For the certificate at the top of the chain (the one the server presented), the dialogue offers three responses:
- refuses the connection. Rejecting any certificate in the chain rejects the whole chain, and the corresponding SWI call fails.
- allows the connection to proceed this time only; the same certificate will be queried again on a future connection.
- allows the connection to proceed, and records an exception so that the same certificate is accepted automatically in future without prompting again.
Dialogues for certificates further up the chain (the issuers) only offer and , since the decision to trust the chain permanently is made once, at the top level, after the intervening issuers have been reviewed.
Each certificate's dialogue also offers a button, which writes the certificate to a pipe file in PEM form and opens it in the user's default text viewer, so the certificate's contents can be inspected in detail before a decision is made.
Timing out
SWI AcornSSL_Setsockopt with SO_ACORNSSL_PROMPTTIME controls how long, in centiseconds, a session will wait for the user to respond before the connection attempt fails. A value of zero suppresses the dialogue entirely for that session and fails the connection as soon as the certificate check fails, which is appropriate for background or unattended connections where no user is available to respond.
Error Messages
AcornSSL's own errors are allocated the range &813F20 to &813F3F. These are used for failures that are specific to the module, or that arise from a SWI that has no direct BSD sockets equivalent (such as SWI AcornSSL_CreateSession).
Most errors from the socket-like SWIs are instead mapped onto Unix errors in the DCI4 error range, for maximum compatibility with the BSD sockets API used by the Internet module. The full list of these errors can be found in C:TCPIPLibs.sys.h.errno, and macros to recognise and extract them from a RISC OS error are in C:TCPIPLibs.sys.h.dcistructs. Errors propagated from a component AcornSSL relies on (for example, a ‘File not found’ error while reading a certificate file) are passed back unchanged.
An ssl handle passed to a SWI does not refer to a currently open session.
The session's internal mbedTLS context is not in a usable state.
The underlying security library failed to initialise; the substituted value gives the mbedTLS reason code.
The certificate verification machinery could not be set up; the substituted value gives the mbedTLS reason code.
The store of trusted root certificates could not be found or read.
AcornSSL was unable to allocate memory needed to complete the request.
The underlying socket reported an error that could not be mapped onto a Unix error number; the substituted value gives the underlying error code.
The TLS handshake failed; the substituted value gives the mbedTLS handshake state at the point of failure.
A parameter passed to a SWI was invalid; the substituted value gives the mbedTLS error code that identified the problem.
The store of certificate exceptions, recorded when the user chooses in the certificate confirmation dialogue, could not be found or read.
Examples
Connecting with certificate name checking
This example shows the minimum sequence of calls needed to open a secure connection directly, associating an expected host name with the session so that the peer's certificate is checked against it, in the same way a web browser checks a server's certificate against the name in the URL.
int ssl, err; const char *hostname = "example.com"; /* Get a handle, and check the peer's name during handshaking */ ssl = call AcornSSL_Creat, get ssl handle; call AcornSSL_Setsockopt, SO_ACORNSSL_HOSTNAME, hostname, 4; /* Connect; this may trigger the certificate confirmation dialogue */ err = call AcornSSL_Connect, address, addresslen; /* Read and write the secure connection as usual */ call AcornSSL_Write, ...; call AcornSSL_Read, ...; /* Finished */ call AcornSSL_Close;
Upgrading an existing socket
This example shows a protocol that starts with a plain-text exchange over a socket created with SWI Socket_Creat, and only switches to a secure session partway through the conversation, as is typical for protocols such as STARTTLS-style negotiations.
int sock, ssl; /* Open and connect an ordinary socket, and talk to it in plain text */ sock = call Socket_Creat, get socket handle; call Socket_Connect, address, addresslen; negotiate whether to go secure, in plain text; /* Hand the socket over to AcornSSL and continue securely */ ssl = call AcornSSL_CreateSession, sock, CreateSession_New; call AcornSSL_Write, ...; call AcornSSL_Read, ...; /* Finished; close the secure session, then the underlying socket */ call AcornSSL_Close; call Socket_Close, sock;