Merge pull request #7397 from dalybrown/expose-dtsl-in-ada

Expose DTLS in Ada wrapper and update examples
This commit is contained in:
JacobBarthelmeh
2024-04-16 13:37:23 -06:00
committed by GitHub
11 changed files with 318 additions and 66 deletions

1
wrapper/Ada/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
obj

View File

@ -2,7 +2,7 @@
The source code for the Ada/SPARK binding of the WolfSSL library
is the WolfSSL Ada package in the wolfssl.ads and wolfssl.adb files.
The source code here also demonstrates a TLS v1.3 server and client
The source code here also demonstrates a (D)TLS v1.3 server and client
using the WolfSSL Ada binding. The implementation is cross-platform
and compiles on Linux, Mac OS X and Windows.
@ -15,7 +15,8 @@ for the secondary stack. The GNAT User's Guide recommends
avoiding the secondary stack using the restriction
No_Secondary_Stack (see the GNAT configuration file gnat.adc
which instructs compilation of the WolfSSL Ada binding under
this restriction).
this restriction). Note, however, that the examples do make use of the
secondary stack.
Portability: The WolfSSL Ada binding makes no usage of controlled types
and has no dependency upon the Ada.Finalization package.
@ -91,13 +92,13 @@ Make sure the executables for the compiler and GPRBuild are on the PATH
and use gprbuild to build the source code.
## Files
The TLS v1.3 client example in the Ada/SPARK programming language
The (D)TLS v1.3 client example in the Ada/SPARK programming language
using the WolfSSL library can be found in the files:
tls_client_main.adb
tls_client.ads
tls_client.adb
The TLS v1.3 server example in the Ada/SPARK programming language
The (D)TLS v1.3 server example in the Ada/SPARK programming language
using the WolfSSL library can be found in the files:
tls_server_main.adb
tls_server.ads

View File

@ -26,10 +26,6 @@ project Default is
for Spec_Suffix ("C") use ".h";
end Naming;
package Builder is
for Global_Configuration_Pragmas use "gnat.adc";
end Builder;
package Compiler is
for Switches ("C") use
("-DWOLFSSL_USER_SETTINGS", -- Use the user_settings.h file.

View File

@ -19,6 +19,7 @@
-- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
--
with Ada.Streams;
with Interfaces.C;
package body SPARK_Sockets is
@ -33,16 +34,35 @@ package body SPARK_Sockets is
return (Exists => False);
end Inet_Addr;
procedure Create_Socket (Socket : in out Optional_Socket) is
procedure Create_Socket
(Socket : in out Optional_Socket;
Family : GNAT.Sockets.Family_Type;
Mode : GNAT.Sockets.Mode_Type) is
S : Socket_Type;
begin
GNAT.Sockets.Create_Socket (S);
GNAT.Sockets.Create_Socket (S, Family, Mode);
Socket := (Exists => True, Socket => S);
exception
when others =>
Socket := (Exists => False);
end Create_Socket;
procedure Create_Stream_Socket (Socket : in out Optional_Socket) is
begin
Create_Socket
(Socket => Socket,
Family => GNAT.Sockets.Family_Inet,
Mode => GNAT.Sockets.Socket_Stream);
end Create_Stream_Socket;
procedure Create_Datagram_Socket (Socket : in out Optional_Socket) is
begin
Create_Socket
(Socket => Socket,
Family => GNAT.Sockets.Family_Inet,
Mode => GNAT.Sockets.Socket_Datagram);
end Create_Datagram_Socket;
function Connect_Socket (Socket : Socket_Type;
Server : Sock_Addr_Type)
return Subprogram_Result is
@ -99,6 +119,22 @@ package body SPARK_Sockets is
return Failure;
end Listen_Socket;
function Receive_Socket
(Socket : Socket_Type)
return Subprogram_Result is
Item : Ada.Streams.Stream_Element_Array (1 .. 4096);
Last : Ada.Streams.Stream_Element_Offset;
From : GNAT.Sockets.Sock_Addr_Type;
begin
GNAT.Sockets.Receive_Socket (Socket, Item, Last, From);
return Success;
exception
when others =>
return Failure;
end Receive_Socket;
procedure Accept_Socket (Server : Socket_Type;
Socket : out Optional_Socket;
Address : out Sock_Addr_Type;

View File

@ -83,7 +83,10 @@ package SPARK_Sockets with SPARK_Mode is
end case;
end record;
procedure Create_Socket (Socket : in out Optional_Socket) with
procedure Create_Stream_Socket (Socket : in out Optional_Socket) with
Pre => not Socket.Exists;
procedure Create_Datagram_Socket (Socket : in out Optional_Socket) with
Pre => not Socket.Exists;
function Connect_Socket (Socket : Socket_Type;
@ -116,6 +119,8 @@ package SPARK_Sockets with SPARK_Mode is
-- appropriate in usual cases. It can be adjusted according to each
-- application's particular requirements.
function Receive_Socket (Socket : Socket_Type) return Subprogram_Result;
procedure Accept_Socket (Server : Socket_Type;
Socket : out Optional_Socket;
Address : out Sock_Addr_Type;

View File

@ -47,12 +47,18 @@ package body Tls_Client with SPARK_Mode is
Ada.Text_IO.Put (Text);
end Put;
procedure Put (Number : Natural) is
procedure Put (Number : Natural)
with
Annotate => (GNATprove, Might_Not_Return)
is
begin
Natural_IO.Put (Item => Number, Width => 0, Base => 10);
end Put;
procedure Put (Number : Byte_Index) is
procedure Put (Number : Byte_Index)
with
Annotate => (GNATprove, Might_Not_Return)
is
begin
Natural_IO.Put (Item => Natural (Number), Width => 0, Base => 10);
end Put;
@ -137,6 +143,7 @@ package body Tls_Client with SPARK_Mode is
Output : WolfSSL.Write_Result;
Result : WolfSSL.Subprogram_Result;
DTLS : Boolean;
begin
Result := WolfSSL.Initialize;
if Result /= Success then
@ -144,14 +151,29 @@ package body Tls_Client with SPARK_Mode is
return;
end if;
if Argument_Count < 1 then
Put_Line ("usage: tcl_client <IPv4 address>");
if Argument_Count < 1
or Argument_Count > 2
or (Argument_Count = 2 and then Argument (2) /= "--dtls")
then
Put_Line ("usage: tls_client_main <IPv4 address> [--dtls]");
return;
end if;
SPARK_Sockets.Create_Socket (C);
DTLS := (SPARK_Terminal.Argument_Count = 2);
if DTLS then
SPARK_Sockets.Create_Datagram_Socket (C);
else
SPARK_Sockets.Create_Stream_Socket (C);
end if;
if not C.Exists then
Put_Line ("ERROR: Failed to create socket.");
declare
Mode : constant String := (if DTLS then "datagram" else "stream");
begin
Put_Line ("ERROR: Failed to create " & Mode & " socket.");
return;
end;
end if;
Addr := SPARK_Sockets.Inet_Addr (Argument (1));
@ -167,6 +189,7 @@ package body Tls_Client with SPARK_Mode is
Addr => Addr.Addr,
Port => P);
if not DTLS then
Result := SPARK_Sockets.Connect_Socket (Socket => C.Socket,
Server => A);
if Result /= Success then
@ -175,10 +198,17 @@ package body Tls_Client with SPARK_Mode is
Set (Exit_Status_Failure);
return;
end if;
end if;
-- Create and initialize WOLFSSL_CTX.
WolfSSL.Create_Context (Method => WolfSSL.TLSv1_3_Client_Method,
WolfSSL.Create_Context
(Method =>
(if DTLS then
WolfSSL.DTLSv1_3_Client_Method
else
WolfSSL.TLSv1_3_Client_Method),
Context => Ctx);
if not WolfSSL.Is_Valid (Ctx) then
Put_Line ("ERROR: failed to create WOLFSSL_CTX.");
SPARK_Sockets.Close_Socket (C);
@ -186,6 +216,11 @@ package body Tls_Client with SPARK_Mode is
return;
end if;
-- Require mutual authentication.
WolfSSL.Set_Verify
(Context => Ctx,
Mode => WolfSSL.Verify_Peer & WolfSSL.Verify_Fail_If_No_Peer_Cert);
-- Load client certificate into WOLFSSL_CTX.
Result := WolfSSL.Use_Certificate_File (Context => Ctx,
File => CERT_FILE,
@ -241,6 +276,19 @@ package body Tls_Client with SPARK_Mode is
return;
end if;
if DTLS then
Result := WolfSSL.DTLS_Set_Peer(Ssl => Ssl,
Address => A);
if Result /= Success then
Put_Line ("ERROR: Failed to set the DTLS peer.");
SPARK_Sockets.Close_Socket (C);
WolfSSL.Free (Ssl);
WolfSSL.Free (Context => Ctx);
Set (Exit_Status_Failure);
return;
end if;
end if;
-- Attach wolfSSL to the socket.
Result := WolfSSL.Attach (Ssl => Ssl,
Socket => SPARK_Sockets.To_C (C.Socket));

View File

@ -32,6 +32,7 @@ package Tls_Client with SPARK_Mode is
Pre => (not Client.Exists and not
WolfSSL.Is_Valid (Ssl) and not WolfSSL.Is_Valid (Ctx)),
Post => (not Client.Exists and not WolfSSL.Is_Valid (Ssl) and
not WolfSSL.Is_Valid (Ctx));
not WolfSSL.Is_Valid (Ctx)),
Annotate => (GNATprove, Might_Not_Return);
end Tls_Client;

View File

@ -105,6 +105,7 @@ package body Tls_Server with SPARK_Mode is
Ch : Character;
Result : WolfSSL.Subprogram_Result;
DTLS : Boolean;
Shall_Continue : Boolean := True;
Input : WolfSSL.Read_Result;
@ -117,12 +118,31 @@ package body Tls_Server with SPARK_Mode is
return;
end if;
SPARK_Sockets.Create_Socket (Socket => L);
if not L.Exists then
Put_Line ("ERROR: Failed to create socket.");
if SPARK_Terminal.Argument_Count > 1
or (SPARK_Terminal.Argument_Count = 1
and then SPARK_Terminal.Argument (1) /= "--dtls")
then
Put_Line ("usage: tls_server_main [--dtls]");
return;
end if;
DTLS := (SPARK_Terminal.Argument_Count = 1);
if DTLS then
SPARK_Sockets.Create_Datagram_Socket (Socket => L);
else
SPARK_Sockets.Create_Stream_Socket (Socket => L);
end if;
if not L.Exists then
declare
Mode : constant String := (if DTLS then "datagram" else "stream");
begin
Put_Line ("ERROR: Failed to create " & Mode & " socket.");
return;
end;
end if;
Option := (Name => Reuse_Address, Enabled => True);
Result := SPARK_Sockets.Set_Socket_Option (Socket => L.Socket,
Level => Socket_Level,
@ -144,17 +164,32 @@ package body Tls_Server with SPARK_Mode is
return;
end if;
if DTLS then
Result := SPARK_Sockets.Receive_Socket (Socket => L.Socket);
else
Result := SPARK_Sockets.Listen_Socket (Socket => L.Socket,
Length => 5);
end if;
if Result /= Success then
Put_Line ("ERROR: Failed to configure listener socket.");
declare
Operation : constant String := (if DTLS then "receiver" else "listener");
begin
Put_Line ("ERROR: Failed to configure " & Operation & " socket.");
SPARK_Sockets.Close_Socket (L);
return;
end;
end if;
-- Create and initialize WOLFSSL_CTX.
WolfSSL.Create_Context (Method => WolfSSL.TLSv1_3_Server_Method,
WolfSSL.Create_Context
(Method =>
(if DTLS then
WolfSSL.DTLSv1_3_Server_Method
else
WolfSSL.TLSv1_3_Server_Method),
Context => Ctx);
if not WolfSSL.Is_Valid (Ctx) then
Put_Line ("ERROR: failed to create WOLFSSL_CTX.");
SPARK_Sockets.Close_Socket (L);
@ -217,6 +252,7 @@ package body Tls_Server with SPARK_Mode is
pragma Loop_Invariant (not WolfSSL.Is_Valid (Ssl));
pragma Loop_Invariant (WolfSSL.Is_Valid (Ctx));
if not DTLS then
Put_Line ("Waiting for a connection...");
SPARK_Sockets.Accept_Socket (Server => L.Socket,
Socket => C,
@ -228,38 +264,52 @@ package body Tls_Server with SPARK_Mode is
WolfSSL.Free (Context => Ctx);
return;
end if;
end if;
-- Create a WOLFSSL object.
WolfSSL.Create_WolfSSL (Context => Ctx, Ssl => Ssl);
if not WolfSSL.Is_Valid (Ssl) then
Put_Line ("ERROR: failed to create WOLFSSL object.");
SPARK_Sockets.Close_Socket (L);
if not DTLS then
SPARK_Sockets.Close_Socket (C);
end if;
WolfSSL.Free (Context => Ctx);
Set (Exit_Status_Failure);
return;
end if;
-- Attach wolfSSL to the socket.
Result := WolfSSL.Attach (Ssl => Ssl,
Socket => SPARK_Sockets.To_C (C.Socket));
Result := WolfSSL.Attach
(Ssl => Ssl,
Socket => SPARK_Sockets.To_C (if DTLS then L.Socket else C.Socket));
if Result /= Success then
Put_Line ("ERROR: Failed to set the file descriptor.");
WolfSSL.Free (Ssl);
SPARK_Sockets.Close_Socket (L);
if not DTLS then
SPARK_Sockets.Close_Socket (C);
end if;
WolfSSL.Free (Context => Ctx);
Set (Exit_Status_Failure);
return;
end if;
-- Establish TLS connection.
-- Establish (D)TLS connection.
Result := WolfSSL.Accept_Connection (Ssl);
if Result /= Success then
Put_Line ("Accept error.");
WolfSSL.Free (Ssl);
SPARK_Sockets.Close_Socket (L);
if not DTLS then
SPARK_Sockets.Close_Socket (C);
end if;
WolfSSL.Free (Context => Ctx);
Set (Exit_Status_Failure);
return;
@ -272,7 +322,11 @@ package body Tls_Server with SPARK_Mode is
Put_Line ("Read error.");
WolfSSL.Free (Ssl);
SPARK_Sockets.Close_Socket (L);
if not DTLS then
SPARK_Sockets.Close_Socket (C);
end if;
WolfSSL.Free (Context => Ctx);
Set (Exit_Status_Failure);
return;
@ -306,15 +360,24 @@ package body Tls_Server with SPARK_Mode is
end if;
for I in 1 .. 3 loop
Result := WolfSSL.Shutdown (Ssl);
exit when Result = Success;
exit when DTLS or Result = Success;
delay 0.001; -- Delay is expressed in seconds.
end loop;
if Result /= Success then
if not DTLS and then Result /= Success then
Put_Line ("ERROR: Failed to shutdown WolfSSL context.");
end if;
WolfSSL.Free (Ssl);
if DTLS then
Shall_Continue := False;
else
SPARK_Sockets.Close_Socket (C);
end if;
Put_Line ("Shutdown complete.");
end loop;

View File

@ -75,13 +75,9 @@ extern "C" {
#define WOLFSSL_TLS13_NO_PEEK_HANDSHAKE_DONE
/* DTLS */
#if 0
#define WOLFSSL_DTLS
#define WOLFSSL_MULTICAST
/* DTLS v1.3 is not yet included with enable-all */
//#define WOLFSSL_DTLS13
#endif
// #define WOLFSSL_MULTICAST
#define WOLFSSL_DTLS13
/* DG Disabled SSLv3 and TLSv1.0 - should avoid using */
//#define WOLFSSL_ALLOW_SSLV3

View File

@ -19,7 +19,12 @@
-- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
--
pragma Warnings (Off, "* is an internal GNAT unit");
with GNAT.Sockets.Thin_Common;
pragma Warnings (On, "* is an internal GNAT unit");
with Interfaces.C.Extensions;
with Interfaces.C.Strings;
with System;
package body WolfSSL is
@ -97,6 +102,46 @@ package body WolfSSL is
return WolfTLSv1_3_Client_Method;
end TLSv1_3_Client_Method;
function WolfDTLSv1_2_Server_Method return Method_Type with
Convention => C,
External_Name => "wolfDTLSv1_2_server_method",
Import => True;
function DTLSv1_2_Server_Method return Method_Type is
begin
return WolfDTLSv1_2_Server_Method;
end DTLSv1_2_Server_Method;
function WolfDTLSv1_2_Client_Method return Method_Type with
Convention => C,
External_Name => "wolfDTLSv1_2_client_method",
Import => True;
function DTLSv1_2_Client_Method return Method_Type is
begin
return WolfDTLSv1_2_Client_Method;
end DTLSv1_2_Client_Method;
function WolfDTLSv1_3_Server_Method return Method_Type with
Convention => C,
External_Name => "wolfDTLSv1_3_server_method",
Import => True;
function DTLSv1_3_Server_Method return Method_Type is
begin
return WolfDTLSv1_3_Server_Method;
end DTLSv1_3_Server_Method;
function WolfDTLSv1_3_Client_Method return Method_Type with
Convention => C,
External_Name => "wolfDTLSv1_3_client_method",
Import => True;
function DTLSv1_3_Client_Method return Method_Type is
begin
return WolfDTLSv1_3_Client_Method;
end DTLSv1_3_Client_Method;
function WolfSSL_CTX_new (Method : Method_Type)
return Context_Type with
Convention => C, External_Name => "wolfSSL_CTX_new", Import => True;
@ -487,6 +532,41 @@ package body WolfSSL is
return Subprogram_Result (Result);
end Use_Private_Key_Buffer;
function WolfSSL_DTLS_Set_Peer
(ssl : WolfSSL_Type;
peer : GNAT.Sockets.Thin_Common.Sockaddr_Access;
peerSz : Interfaces.C.unsigned)
return int with
Convention => C,
External_Name => "wolfSSL_dtls_set_peer",
Import => True;
function DTLS_Set_Peer
(Ssl : WolfSSL_Type;
Address : GNAT.Sockets.Sock_Addr_Type)
return Subprogram_Result is
Sin : aliased GNAT.Sockets.Thin_Common.Sockaddr;
Length : Interfaces.C.int;
begin
GNAT.Sockets.Thin_Common.Set_Address
(Sin => Sin'Unchecked_Access,
Address => Address,
Length => Length);
pragma Assert (Length >= 0);
return
Subprogram_Result
(WolfSSL_DTLS_Set_Peer
(ssl => Ssl,
peer => Sin'Unchecked_Access,
peerSz => Interfaces.C.unsigned (Length)));
end DTLS_Set_Peer;
function WolfSSL_Set_Fd (Ssl : WolfSSL_Type; Fd : int) return int with
Convention => C,
External_Name => "wolfSSL_set_fd",

View File

@ -19,6 +19,7 @@
-- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
--
with GNAT.Sockets;
with Interfaces.C;
-- This package is annotated "with SPARK_Mode" that SPARK can verify
@ -70,6 +71,22 @@ package WolfSSL with SPARK_Mode is
-- This function is used to indicate that the application is a client
-- and will only support the TLS 1.3 protocol.
function DTLSv1_2_Server_Method return Method_Type;
-- This function is used to indicate that the application is a server
-- and will only support the DTLS 1.2 protocol.
function DTLSv1_2_Client_Method return Method_Type;
-- This function is used to indicate that the application is a client
-- and will only support the DTLS 1.2 protocol.
function DTLSv1_3_Server_Method return Method_Type;
-- This function is used to indicate that the application is a server
-- and will only support the DTLS 1.3 protocol.
function DTLSv1_3_Client_Method return Method_Type;
-- This function is used to indicate that the application is a client
-- and will only support the DTLS 1.3 protocol.
procedure Create_Context (Method : Method_Type;
Context : out Context_Type);
-- This function creates a new SSL context, taking a desired SSL/TLS
@ -270,6 +287,14 @@ package WolfSSL with SPARK_Mode is
-- Format specifies the format type of the buffer; ASN1 or PEM.
-- Please see the examples for proper usage.
function DTLS_Set_Peer
(Ssl : WolfSSL_Type;
Address : GNAT.Sockets.Sock_Addr_Type)
return Subprogram_Result with
Pre => Is_Valid (Ssl);
-- This function wraps the corresponding WolfSSL C function to allow
-- clients to use Ada socket types when implementing a DTLS client.
function Attach (Ssl : WolfSSL_Type;
Socket : Integer)
return Subprogram_Result with