tls_client.adb 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. -- tls_client.adb
  2. --
  3. -- Copyright (C) 2006-2023 wolfSSL Inc.
  4. --
  5. -- This file is part of wolfSSL.
  6. --
  7. -- wolfSSL is free software; you can redistribute it and/or modify
  8. -- it under the terms of the GNU General Public License as published by
  9. -- the Free Software Foundation; either version 2 of the License, or
  10. -- (at your option) any later version.
  11. --
  12. -- wolfSSL is distributed in the hope that it will be useful,
  13. -- but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. -- GNU General Public License for more details.
  16. --
  17. -- You should have received a copy of the GNU General Public License
  18. -- along with this program; if not, write to the Free Software
  19. -- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
  20. --
  21. -- Ada Standard Library packages.
  22. with Ada.Characters.Handling;
  23. with Ada.Strings.Bounded;
  24. with Ada.Text_IO;
  25. with Interfaces.C;
  26. with SPARK_Terminal;
  27. package body Tls_Client with SPARK_Mode is
  28. use type WolfSSL.Mode_Type;
  29. use type WolfSSL.Byte_Index;
  30. use type WolfSSL.Byte_Array;
  31. use type WolfSSL.Subprogram_Result;
  32. subtype Byte_Index is WolfSSL.Byte_Index;
  33. Success : WolfSSL.Subprogram_Result renames WolfSSL.Success;
  34. subtype Byte_Type is WolfSSL.Byte_Type;
  35. package Natural_IO is new Ada.Text_IO.Integer_IO (Natural);
  36. procedure Put (Text : String) is
  37. begin
  38. Ada.Text_IO.Put (Text);
  39. end Put;
  40. procedure Put (Number : Natural)
  41. with
  42. Annotate => (GNATprove, Might_Not_Return)
  43. is
  44. begin
  45. Natural_IO.Put (Item => Number, Width => 0, Base => 10);
  46. end Put;
  47. procedure Put (Number : Byte_Index)
  48. with
  49. Annotate => (GNATprove, Might_Not_Return)
  50. is
  51. begin
  52. Natural_IO.Put (Item => Natural (Number), Width => 0, Base => 10);
  53. end Put;
  54. procedure Put_Line (Text : String) is
  55. begin
  56. Ada.Text_IO.Put_Line (Text);
  57. end Put_Line;
  58. procedure New_Line is
  59. begin
  60. Ada.Text_IO.New_Line;
  61. end New_Line;
  62. subtype Exit_Status is SPARK_Terminal.Exit_Status;
  63. Exit_Status_Success : Exit_Status renames SPARK_Terminal.Exit_Status_Success;
  64. Exit_Status_Failure : Exit_Status renames SPARK_Terminal.Exit_Status_Failure;
  65. procedure Set (Status : Exit_Status) with Global => null is
  66. begin
  67. SPARK_Terminal.Set_Exit_Status (Status);
  68. end Set;
  69. subtype Port_Type is SPARK_Sockets.Port_Type;
  70. subtype Level_Type is SPARK_Sockets.Level_Type;
  71. subtype Socket_Type is SPARK_Sockets.Socket_Type;
  72. subtype Option_Name is SPARK_Sockets.Option_Name;
  73. subtype Option_Type is SPARK_Sockets.Option_Type;
  74. subtype Family_Type is SPARK_Sockets.Family_Type;
  75. subtype Sock_Addr_Type is SPARK_Sockets.Sock_Addr_Type;
  76. subtype Inet_Addr_Type is SPARK_Sockets.Inet_Addr_Type;
  77. use type Family_Type;
  78. Socket_Error : exception renames SPARK_Sockets.Socket_Error;
  79. Reuse_Address : Option_Name renames SPARK_Sockets.Reuse_Address;
  80. Socket_Level : Level_Type renames SPARK_Sockets.Socket_Level;
  81. Family_Inet : Family_Type renames SPARK_Sockets.Family_Inet;
  82. Any_Inet_Addr : Inet_Addr_Type renames SPARK_Sockets.Any_Inet_Addr;
  83. CERT_FILE : constant String := "../../../certs/client-cert.pem";
  84. KEY_FILE : constant String := "../../../certs/client-key.pem";
  85. CA_FILE : constant String := "../../../certs/ca-cert.pem";
  86. subtype Byte_Array is WolfSSL.Byte_Array;
  87. function Argument_Count return Natural renames
  88. SPARK_Terminal.Argument_Count;
  89. function Argument (Number : Positive) return String with
  90. Pre => Number <= Argument_Count;
  91. function Argument (Number : Positive) return String is
  92. begin
  93. return SPARK_Terminal.Argument (Number);
  94. end Argument;
  95. procedure Run (Ssl : in out WolfSSL.WolfSSL_Type;
  96. Ctx : in out WolfSSL.Context_Type;
  97. Client : in out SPARK_Sockets.Optional_Socket) is
  98. A : Sock_Addr_Type;
  99. C : SPARK_Sockets.Optional_Socket renames Client;
  100. D : Byte_Array (1 .. 200);
  101. P : constant Port_Type := 11111;
  102. Addr : SPARK_Sockets.Optional_Inet_Addr;
  103. Count : WolfSSL.Byte_Index;
  104. Text : String (1 .. 200);
  105. Last : Natural;
  106. Input : WolfSSL.Read_Result;
  107. Output : WolfSSL.Write_Result;
  108. Result : WolfSSL.Subprogram_Result;
  109. DTLS : Boolean;
  110. begin
  111. Result := WolfSSL.Initialize;
  112. if Result /= Success then
  113. Put_Line ("ERROR: Failed to initialize the WolfSSL library.");
  114. return;
  115. end if;
  116. if Argument_Count < 1
  117. or Argument_Count > 2
  118. or (Argument_Count = 2 and then Argument (2) /= "--dtls")
  119. then
  120. Put_Line ("usage: tls_client_main <IPv4 address> [--dtls]");
  121. return;
  122. end if;
  123. DTLS := (SPARK_Terminal.Argument_Count = 2);
  124. if DTLS then
  125. SPARK_Sockets.Create_Datagram_Socket (C);
  126. else
  127. SPARK_Sockets.Create_Stream_Socket (C);
  128. end if;
  129. if not C.Exists then
  130. declare
  131. Mode : constant String := (if DTLS then "datagram" else "stream");
  132. begin
  133. Put_Line ("ERROR: Failed to create " & Mode & " socket.");
  134. return;
  135. end;
  136. end if;
  137. Addr := SPARK_Sockets.Inet_Addr (Argument (1));
  138. if not Addr.Exists or
  139. (Addr.Exists and then Addr.Addr.Family /= Family_Inet)
  140. then
  141. Put_Line ("ERROR: please specify IPv4 address.");
  142. SPARK_Sockets.Close_Socket (C);
  143. Set (Exit_Status_Failure);
  144. return;
  145. end if;
  146. A := (Family => Family_Inet,
  147. Addr => Addr.Addr,
  148. Port => P);
  149. if not DTLS then
  150. Result := SPARK_Sockets.Connect_Socket (Socket => C.Socket,
  151. Server => A);
  152. if Result /= Success then
  153. Put_Line ("ERROR: Failed to connect to server.");
  154. SPARK_Sockets.Close_Socket (C);
  155. Set (Exit_Status_Failure);
  156. return;
  157. end if;
  158. end if;
  159. -- Create and initialize WOLFSSL_CTX.
  160. WolfSSL.Create_Context
  161. (Method =>
  162. (if DTLS then
  163. WolfSSL.DTLSv1_3_Client_Method
  164. else
  165. WolfSSL.TLSv1_3_Client_Method),
  166. Context => Ctx);
  167. if not WolfSSL.Is_Valid (Ctx) then
  168. Put_Line ("ERROR: failed to create WOLFSSL_CTX.");
  169. SPARK_Sockets.Close_Socket (C);
  170. Set (Exit_Status_Failure);
  171. return;
  172. end if;
  173. -- Require mutual authentication.
  174. WolfSSL.Set_Verify
  175. (Context => Ctx,
  176. Mode => WolfSSL.Verify_Peer or WolfSSL.Verify_Fail_If_No_Peer_Cert);
  177. -- Load client certificate into WOLFSSL_CTX.
  178. Result := WolfSSL.Use_Certificate_File (Context => Ctx,
  179. File => CERT_FILE,
  180. Format => WolfSSL.Format_Pem);
  181. if Result /= Success then
  182. Put ("ERROR: failed to load ");
  183. Put (CERT_FILE);
  184. Put (", please check the file.");
  185. New_Line;
  186. SPARK_Sockets.Close_Socket (C);
  187. WolfSSL.Free (Context => Ctx);
  188. Set (Exit_Status_Failure);
  189. return;
  190. end if;
  191. -- Load client key into WOLFSSL_CTX.
  192. Result := WolfSSL.Use_Private_Key_File (Context => Ctx,
  193. File => KEY_FILE,
  194. Format => WolfSSL.Format_Pem);
  195. if Result /= Success then
  196. Put ("ERROR: failed to load ");
  197. Put (KEY_FILE);
  198. Put (", please check the file.");
  199. New_Line;
  200. SPARK_Sockets.Close_Socket (C);
  201. WolfSSL.Free (Context => Ctx);
  202. Set (Exit_Status_Failure);
  203. return;
  204. end if;
  205. -- Load CA certificate into WOLFSSL_CTX.
  206. Result := WolfSSL.Load_Verify_Locations (Context => Ctx,
  207. File => CA_FILE,
  208. Path => "");
  209. if Result /= Success then
  210. Put ("ERROR: failed to load ");
  211. Put (CA_FILE);
  212. Put (", please check the file.");
  213. New_Line;
  214. SPARK_Sockets.Close_Socket (C);
  215. WolfSSL.Free (Context => Ctx);
  216. Set (Exit_Status_Failure);
  217. return;
  218. end if;
  219. -- Create a WOLFSSL object.
  220. WolfSSL.Create_WolfSSL (Context => Ctx, Ssl => Ssl);
  221. if not WolfSSL.Is_Valid (Ssl) then
  222. Put_Line ("ERROR: failed to create WOLFSSL object.");
  223. SPARK_Sockets.Close_Socket (C);
  224. WolfSSL.Free (Context => Ctx);
  225. Set (Exit_Status_Failure);
  226. return;
  227. end if;
  228. if DTLS then
  229. Result := WolfSSL.DTLS_Set_Peer(Ssl => Ssl,
  230. Address => A);
  231. if Result /= Success then
  232. Put_Line ("ERROR: Failed to set the DTLS peer.");
  233. SPARK_Sockets.Close_Socket (C);
  234. WolfSSL.Free (Ssl);
  235. WolfSSL.Free (Context => Ctx);
  236. Set (Exit_Status_Failure);
  237. return;
  238. end if;
  239. end if;
  240. -- Attach wolfSSL to the socket.
  241. Result := WolfSSL.Attach (Ssl => Ssl,
  242. Socket => SPARK_Sockets.To_C (C.Socket));
  243. if Result /= Success then
  244. Put_Line ("ERROR: Failed to set the file descriptor.");
  245. SPARK_Sockets.Close_Socket (C);
  246. WolfSSL.Free (Ssl);
  247. WolfSSL.Free (Context => Ctx);
  248. Set (Exit_Status_Failure);
  249. return;
  250. end if;
  251. Result := WolfSSL.Connect (Ssl);
  252. if Result /= Success then
  253. Put_Line ("ERROR: failed to connect to wolfSSL.");
  254. SPARK_Sockets.Close_Socket (C);
  255. WolfSSL.Free (Ssl);
  256. WolfSSL.Free (Context => Ctx);
  257. Set (Exit_Status_Failure);
  258. return;
  259. end if;
  260. Put ("Message for server: ");
  261. Ada.Text_IO.Get_Line (Text, Last);
  262. SPARK_Sockets.To_C (Item => Text (1 .. Last),
  263. Target => D,
  264. Count => Count);
  265. Output := WolfSSL.Write (Ssl => Ssl,
  266. Data => D (1 .. Count));
  267. if not Output.Success then
  268. Put ("ERROR: write failure");
  269. New_Line;
  270. SPARK_Sockets.Close_Socket (C);
  271. WolfSSL.Free (Ssl);
  272. WolfSSL.Free (Context => Ctx);
  273. return;
  274. end if;
  275. if Natural (Output.Bytes_Written) < Last then
  276. Put ("ERROR: failed to write entire message");
  277. New_Line;
  278. Put (Output.Bytes_Written);
  279. Put (" bytes of ");
  280. Put (Last);
  281. Put ("bytes were sent");
  282. New_Line;
  283. SPARK_Sockets.Close_Socket (C);
  284. WolfSSL.Free (Ssl);
  285. WolfSSL.Free (Context => Ctx);
  286. return;
  287. end if;
  288. Input := WolfSSL.Read (Ssl);
  289. if not Input.Success then
  290. Put_Line ("Read error.");
  291. Set (Exit_Status_Failure);
  292. SPARK_Sockets.Close_Socket (C);
  293. WolfSSL.Free (Ssl);
  294. WolfSSL.Free (Context => Ctx);
  295. return;
  296. end if;
  297. if Input.Buffer'Length > Text'Length then
  298. SPARK_Sockets.To_Ada (Item => Input.Buffer (1 .. 200),
  299. Target => Text,
  300. Count => Last);
  301. else
  302. SPARK_Sockets.To_Ada (Item => Input.Buffer,
  303. Target => Text,
  304. Count => Last);
  305. end if;
  306. Put ("Server: ");
  307. Put (Text (1 .. Last));
  308. New_Line;
  309. SPARK_Sockets.Close_Socket (C);
  310. WolfSSL.Free (Ssl);
  311. WolfSSL.Free (Context => Ctx);
  312. Result := WolfSSL.Finalize;
  313. if Result /= Success then
  314. Put_Line ("ERROR: Failed to finalize the WolfSSL library.");
  315. end if;
  316. end Run;
  317. end Tls_Client;