tls_client.adb 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  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) is
  41. begin
  42. Natural_IO.Put (Item => Number, Width => 0, Base => 10);
  43. end Put;
  44. procedure Put (Number : Byte_Index) is
  45. begin
  46. Natural_IO.Put (Item => Natural (Number), Width => 0, Base => 10);
  47. end Put;
  48. procedure Put_Line (Text : String) is
  49. begin
  50. Ada.Text_IO.Put_Line (Text);
  51. end Put_Line;
  52. procedure New_Line is
  53. begin
  54. Ada.Text_IO.New_Line;
  55. end New_Line;
  56. subtype Exit_Status is SPARK_Terminal.Exit_Status;
  57. Exit_Status_Success : Exit_Status renames SPARK_Terminal.Exit_Status_Success;
  58. Exit_Status_Failure : Exit_Status renames SPARK_Terminal.Exit_Status_Failure;
  59. procedure Set (Status : Exit_Status) with Global => null is
  60. begin
  61. SPARK_Terminal.Set_Exit_Status (Status);
  62. end Set;
  63. subtype Port_Type is SPARK_Sockets.Port_Type;
  64. subtype Level_Type is SPARK_Sockets.Level_Type;
  65. subtype Socket_Type is SPARK_Sockets.Socket_Type;
  66. subtype Option_Name is SPARK_Sockets.Option_Name;
  67. subtype Option_Type is SPARK_Sockets.Option_Type;
  68. subtype Family_Type is SPARK_Sockets.Family_Type;
  69. subtype Sock_Addr_Type is SPARK_Sockets.Sock_Addr_Type;
  70. subtype Inet_Addr_Type is SPARK_Sockets.Inet_Addr_Type;
  71. use type Family_Type;
  72. Socket_Error : exception renames SPARK_Sockets.Socket_Error;
  73. Reuse_Address : Option_Name renames SPARK_Sockets.Reuse_Address;
  74. Socket_Level : Level_Type renames SPARK_Sockets.Socket_Level;
  75. Family_Inet : Family_Type renames SPARK_Sockets.Family_Inet;
  76. Any_Inet_Addr : Inet_Addr_Type renames SPARK_Sockets.Any_Inet_Addr;
  77. CERT_FILE : constant String := "../../../certs/client-cert.pem";
  78. KEY_FILE : constant String := "../../../certs/client-key.pem";
  79. CA_FILE : constant String := "../../../certs/ca-cert.pem";
  80. subtype Byte_Array is WolfSSL.Byte_Array;
  81. function Argument_Count return Natural renames
  82. SPARK_Terminal.Argument_Count;
  83. function Argument (Number : Positive) return String with
  84. Pre => Number <= Argument_Count;
  85. function Argument (Number : Positive) return String is
  86. begin
  87. return SPARK_Terminal.Argument (Number);
  88. end Argument;
  89. procedure Run (Ssl : in out WolfSSL.WolfSSL_Type;
  90. Ctx : in out WolfSSL.Context_Type;
  91. Client : in out SPARK_Sockets.Optional_Socket) is
  92. A : Sock_Addr_Type;
  93. C : SPARK_Sockets.Optional_Socket renames Client;
  94. D : Byte_Array (1 .. 200);
  95. P : constant Port_Type := 11111;
  96. Addr : SPARK_Sockets.Optional_Inet_Addr;
  97. Count : WolfSSL.Byte_Index;
  98. Text : String (1 .. 200);
  99. Last : Natural;
  100. Input : WolfSSL.Read_Result;
  101. Output : WolfSSL.Write_Result;
  102. Result : WolfSSL.Subprogram_Result;
  103. begin
  104. Result := WolfSSL.Initialize;
  105. if Result /= Success then
  106. Put_Line ("ERROR: Failed to initialize the WolfSSL library.");
  107. return;
  108. end if;
  109. if Argument_Count < 1 then
  110. Put_Line ("usage: tcl_client <IPv4 address>");
  111. return;
  112. end if;
  113. SPARK_Sockets.Create_Socket (C);
  114. if not C.Exists then
  115. Put_Line ("ERROR: Failed to create socket.");
  116. return;
  117. end if;
  118. Addr := SPARK_Sockets.Inet_Addr (Argument (1));
  119. if not Addr.Exists or
  120. (Addr.Exists and then Addr.Addr.Family /= Family_Inet)
  121. then
  122. Put_Line ("ERROR: please specify IPv4 address.");
  123. SPARK_Sockets.Close_Socket (C);
  124. Set (Exit_Status_Failure);
  125. return;
  126. end if;
  127. A := (Family => Family_Inet,
  128. Addr => Addr.Addr,
  129. Port => P);
  130. Result := SPARK_Sockets.Connect_Socket (Socket => C.Socket,
  131. Server => A);
  132. if Result /= Success then
  133. Put_Line ("ERROR: Failed to connect to server.");
  134. SPARK_Sockets.Close_Socket (C);
  135. Set (Exit_Status_Failure);
  136. return;
  137. end if;
  138. -- Create and initialize WOLFSSL_CTX.
  139. WolfSSL.Create_Context (Method => WolfSSL.TLSv1_3_Client_Method,
  140. Context => Ctx);
  141. if not WolfSSL.Is_Valid (Ctx) then
  142. Put_Line ("ERROR: failed to create WOLFSSL_CTX.");
  143. SPARK_Sockets.Close_Socket (C);
  144. Set (Exit_Status_Failure);
  145. return;
  146. end if;
  147. -- Load client certificate into WOLFSSL_CTX.
  148. Result := WolfSSL.Use_Certificate_File (Context => Ctx,
  149. File => CERT_FILE,
  150. Format => WolfSSL.Format_Pem);
  151. if Result /= Success then
  152. Put ("ERROR: failed to load ");
  153. Put (CERT_FILE);
  154. Put (", please check the file.");
  155. New_Line;
  156. SPARK_Sockets.Close_Socket (C);
  157. WolfSSL.Free (Context => Ctx);
  158. Set (Exit_Status_Failure);
  159. return;
  160. end if;
  161. -- Load client key into WOLFSSL_CTX.
  162. Result := WolfSSL.Use_Private_Key_File (Context => Ctx,
  163. File => KEY_FILE,
  164. Format => WolfSSL.Format_Pem);
  165. if Result /= Success then
  166. Put ("ERROR: failed to load ");
  167. Put (KEY_FILE);
  168. Put (", please check the file.");
  169. New_Line;
  170. SPARK_Sockets.Close_Socket (C);
  171. WolfSSL.Free (Context => Ctx);
  172. Set (Exit_Status_Failure);
  173. return;
  174. end if;
  175. -- Load CA certificate into WOLFSSL_CTX.
  176. Result := WolfSSL.Load_Verify_Locations (Context => Ctx,
  177. File => CA_FILE,
  178. Path => "");
  179. if Result /= Success then
  180. Put ("ERROR: failed to load ");
  181. Put (CA_FILE);
  182. Put (", please check the file.");
  183. New_Line;
  184. SPARK_Sockets.Close_Socket (C);
  185. WolfSSL.Free (Context => Ctx);
  186. Set (Exit_Status_Failure);
  187. return;
  188. end if;
  189. -- Create a WOLFSSL object.
  190. WolfSSL.Create_WolfSSL (Context => Ctx, Ssl => Ssl);
  191. if not WolfSSL.Is_Valid (Ssl) then
  192. Put_Line ("ERROR: failed to create WOLFSSL object.");
  193. SPARK_Sockets.Close_Socket (C);
  194. WolfSSL.Free (Context => Ctx);
  195. Set (Exit_Status_Failure);
  196. return;
  197. end if;
  198. -- Attach wolfSSL to the socket.
  199. Result := WolfSSL.Attach (Ssl => Ssl,
  200. Socket => SPARK_Sockets.To_C (C.Socket));
  201. if Result /= Success then
  202. Put_Line ("ERROR: Failed to set the file descriptor.");
  203. SPARK_Sockets.Close_Socket (C);
  204. WolfSSL.Free (Ssl);
  205. WolfSSL.Free (Context => Ctx);
  206. Set (Exit_Status_Failure);
  207. return;
  208. end if;
  209. Result := WolfSSL.Connect (Ssl);
  210. if Result /= Success then
  211. Put_Line ("ERROR: failed to connect to wolfSSL.");
  212. SPARK_Sockets.Close_Socket (C);
  213. WolfSSL.Free (Ssl);
  214. WolfSSL.Free (Context => Ctx);
  215. Set (Exit_Status_Failure);
  216. return;
  217. end if;
  218. Put ("Message for server: ");
  219. Ada.Text_IO.Get_Line (Text, Last);
  220. SPARK_Sockets.To_C (Item => Text (1 .. Last),
  221. Target => D,
  222. Count => Count);
  223. Output := WolfSSL.Write (Ssl => Ssl,
  224. Data => D (1 .. Count));
  225. if not Output.Success then
  226. Put ("ERROR: write failure");
  227. New_Line;
  228. SPARK_Sockets.Close_Socket (C);
  229. WolfSSL.Free (Ssl);
  230. WolfSSL.Free (Context => Ctx);
  231. return;
  232. end if;
  233. if Natural (Output.Bytes_Written) < Last then
  234. Put ("ERROR: failed to write entire message");
  235. New_Line;
  236. Put (Output.Bytes_Written);
  237. Put (" bytes of ");
  238. Put (Last);
  239. Put ("bytes were sent");
  240. New_Line;
  241. SPARK_Sockets.Close_Socket (C);
  242. WolfSSL.Free (Ssl);
  243. WolfSSL.Free (Context => Ctx);
  244. return;
  245. end if;
  246. Input := WolfSSL.Read (Ssl);
  247. if not Input.Success then
  248. Put_Line ("Read error.");
  249. Set (Exit_Status_Failure);
  250. SPARK_Sockets.Close_Socket (C);
  251. WolfSSL.Free (Ssl);
  252. WolfSSL.Free (Context => Ctx);
  253. return;
  254. end if;
  255. if Input.Buffer'Length > Text'Length then
  256. SPARK_Sockets.To_Ada (Item => Input.Buffer (1 .. 200),
  257. Target => Text,
  258. Count => Last);
  259. else
  260. SPARK_Sockets.To_Ada (Item => Input.Buffer,
  261. Target => Text,
  262. Count => Last);
  263. end if;
  264. Put ("Server: ");
  265. Put (Text (1 .. Last));
  266. New_Line;
  267. SPARK_Sockets.Close_Socket (C);
  268. WolfSSL.Free (Ssl);
  269. WolfSSL.Free (Context => Ctx);
  270. Result := WolfSSL.Finalize;
  271. if Result /= Success then
  272. Put_Line ("ERROR: Failed to finalize the WolfSSL library.");
  273. end if;
  274. end Run;
  275. end Tls_Client;