raw
smg_comms_skeys_s...    1  -- Message reader & writers for SMG Communication Protocol
smg_comms_skeys_s... 2 -- S.MG, 2018
smg_comms_skeys_s... 3
smg_comms_actions... 4 with Raw_Types; use Raw_Types;
smg_comms_skeys_s... 5 with Interfaces; use Interfaces;
smg_comms_skeys_s... 6 with Serpent;
smg_comms_skeys_s... 7 with System; use System;
smg_comms_files 8 with Ada.Assertions; use Ada.Assertions;
smg_comms_skeys_s... 9
smg_comms_skeys_s... 10 package body Messages is
smg_comms_skeys_s... 11
smg_comms_keymgm 12 ----------------------
smg_comms_keymgm 13 -- Serpent Messages --
smg_comms_keymgm 14 ----------------------
smg_comms_keymgm 15
smg_comms_skeys_s... 16 procedure Write_SKeys_SMsg( Keyset : in Serpent_Keyset;
smg_comms_skeys_s... 17 Counter : in Interfaces.Unsigned_16;
smg_comms_actions... 18 Pad : in Raw_Types.Octets_8;
smg_comms_skeys_s... 19 Msg : out Raw_Types.Serpent_Msg) is
smg_comms_keymgm 20 begin
smg_comms_keymgm 21 -- call internal write on Octets with correct type id
smg_comms_actions... 22 Write_SKeys( Keyset, Counter, SKeys_S_Type, Pad, Msg );
smg_comms_keymgm 23 end Write_SKeys_SMsg;
smg_comms_keymgm 24
smg_comms_keymgm 25
smg_comms_keymgm 26 -- Reads a Serpent keyset from given Serpent Message
smg_comms_keymgm 27 procedure Read_SKeys_SMsg( Msg : in Raw_Types.Serpent_Msg;
smg_comms_keymgm 28 Counter : out Interfaces.Unsigned_16;
smg_comms_keymgm 29 Keyset : out Serpent_Keyset) is
smg_comms_keymgm 30 begin
smg_comms_keymgm 31 -- check type id and call internal Read_SKeys if correct
smg_comms_keymgm 32 if Msg(Msg'First) /= SKeys_S_Type then
smg_comms_keymgm 33 raise Invalid_Msg;
smg_comms_keymgm 34 else
smg_comms_keymgm 35 Read_SKeys( Msg, Counter, Keyset );
smg_comms_keymgm 36 end if;
smg_comms_keymgm 37 end Read_SKeys_SMsg;
smg_comms_keymgm 38
smg_comms_keymgm 39 -- writes given key mgm structure into a Serpent message
smg_comms_keymgm 40 procedure Write_KMgm_SMsg( KMgm : in Keys_Mgm;
smg_comms_keymgm 41 Counter : in Interfaces.Unsigned_16;
smg_comms_actions... 42 Pad : in Raw_Types.Octets_8;
smg_comms_keymgm 43 Msg : out Raw_Types.Serpent_Msg) is
smg_comms_keymgm 44 begin
smg_comms_keymgm 45 -- call internal write of key mgm with correct type ID
smg_comms_actions... 46 Write_KMgm( KMgm, Counter, Key_Mgm_S_Type, Pad, Msg );
smg_comms_keymgm 47 end Write_KMgm_SMsg;
smg_comms_keymgm 48
smg_comms_keymgm 49 -- reads a key mgm structure from the given Serpent message
smg_comms_keymgm 50 procedure Read_KMgm_SMsg( Msg : in Raw_Types.Serpent_Msg;
smg_comms_keymgm 51 Counter : out Interfaces.Unsigned_16;
smg_comms_keymgm 52 KMgm : out Keys_Mgm) is
smg_comms_keymgm 53 begin
smg_comms_keymgm 54 -- check type id and call internal Read_KMgm if correct
smg_comms_keymgm 55 if Msg(Msg'First) /= Key_Mgm_S_Type then
smg_comms_keymgm 56 raise Invalid_Msg;
smg_comms_keymgm 57 else
smg_comms_keymgm 58 Read_KMgm( Msg, Counter, KMgm );
smg_comms_keymgm 59 end if;
smg_comms_keymgm 60 end Read_KMgm_SMsg;
smg_comms_keymgm 61
smg_comms_files 62 ------ File Transfer ------
smg_comms_files 63 procedure Write_File_Transfer( Chunk : in File_Chunk;
smg_comms_actions... 64 Pad : in Raw_Types.Octets_8;
smg_comms_files 65 Msg : out Raw_Types.Serpent_Msg) is
smg_comms_files 66 Pos: Integer := Msg'First;
smg_comms_files 67 U16: Interfaces.Unsigned_16;
smg_comms_files 68 begin
smg_comms_files 69 -- write type ID
smg_comms_files 70 Msg(Pos) := File_Transfer_S_Type;
smg_comms_files 71 Pos := Pos + 1;
smg_comms_files 72
smg_comms_files 73 -- write filename as text field (size+2, text)
smg_comms_files 74 -- check against overflows
smg_comms_actions... 75 if Chunk.Name_Len > Raw_Types.Text_Len'Last - 2 or
smg_comms_files 76 Pos + Integer(Chunk.Name_Len) + 2 > Msg'Last then
smg_comms_files 77 raise Invalid_Msg;
smg_comms_files 78 end if;
smg_comms_files 79
smg_comms_files 80 -- write total size: filename size + 2
smg_comms_files 81 U16 := Interfaces.Unsigned_16( Chunk.Name_Len + 2 );
smg_comms_files 82 Write_U16( Msg, Pos, U16 );
smg_comms_files 83
smg_comms_files 84 -- write filename
smg_comms_files 85 String_To_Octets( Chunk.Filename,
smg_comms_files 86 Msg(Pos..Pos+Integer(Chunk.Name_Len)-1) );
smg_comms_files 87 Pos := Pos + Integer(Chunk.Name_Len);
smg_comms_files 88
smg_comms_files 89 --write content
smg_comms_files 90 -- check against overflow, including the 2 octets for counter at the end
smg_comms_actions... 91 if Chunk.Len > Raw_Types.Text_Len'Last - 2 or
smg_comms_files 92 Pos + Integer(Chunk.Len) + 4 > Msg'Last then
smg_comms_files 93 raise Invalid_Msg;
smg_comms_files 94 end if;
smg_comms_files 95
smg_comms_files 96 -- write total size for this text field
smg_comms_files 97 U16 := Interfaces.Unsigned_16( Chunk.Len + 2 );
smg_comms_files 98 Write_U16( Msg, Pos, U16 );
smg_comms_files 99
smg_comms_files 100 -- write actual content
smg_comms_files 101 Msg(Pos..Pos+Chunk.Content'Length-1) := Chunk.Content;
smg_comms_files 102 Pos := Pos + Chunk.Content'Length;
smg_comms_files 103
smg_comms_actions... 104 -- write counter + padding
smg_comms_actions... 105 Write_End( Msg, Pos, Chunk.Count, Pad );
smg_comms_files 106
smg_comms_files 107 end Write_File_Transfer;
smg_comms_files 108
smg_comms_files 109 -- The opposite of Write_File_Transfer method above.
smg_comms_files 110 -- Counter will contain the message counter
smg_comms_files 111 -- Chunk will contain the chunk counter, filename and content
smg_comms_files 112 procedure Read_File_Transfer( Msg : in Raw_Types.Serpent_Msg;
smg_comms_files 113 Chunk : out File_Chunk) is
smg_comms_files 114 Pos: Integer := Msg'First;
smg_comms_files 115 U16: Interfaces.Unsigned_16;
smg_comms_files 116 S_Name, E_Name: Integer; --start/end for filename in Msg
smg_comms_actions... 117 S_Len: Raw_Types.Text_Len; -- length of filename
smg_comms_files 118 S_Content, E_Content: Integer; --start/end for content in Msg
smg_comms_actions... 119 Content_Len: Raw_Types.Text_Len; -- length of content
smg_comms_files 120 begin
smg_comms_files 121 -- read and check type ID
smg_comms_files 122 if Msg(Pos) /= File_Transfer_S_Type then
smg_comms_files 123 raise Invalid_Msg;
smg_comms_files 124 end if;
smg_comms_files 125 Pos := Pos + 1;
smg_comms_files 126
smg_comms_files 127 -- read filename size
smg_comms_files 128 Read_U16( Msg, Pos, U16 );
smg_comms_files 129
smg_comms_files 130 -- check for overflow and underflow; filename size >= 1
smg_comms_files 131 if Pos + Integer(U16) - 2 > Msg'Last or
smg_comms_files 132 U16 < 3 then
smg_comms_files 133 raise Invalid_Msg;
smg_comms_files 134 end if;
smg_comms_files 135 U16 := U16 - 2;
smg_comms_actions... 136 S_Len := Raw_Types.Text_Len(U16);
smg_comms_files 137
smg_comms_files 138 -- set start + end for reading filename later, when ready
smg_comms_files 139 S_Name := Pos;
smg_comms_files 140 E_Name := Pos + Integer(U16)-1;
smg_comms_files 141 Pos := Pos + S_Len;
smg_comms_files 142
smg_comms_files 143 -- read size of content
smg_comms_files 144 Read_U16( Msg, Pos, U16 );
smg_comms_files 145 -- check for overflow and underflow; content >=1; counter =2 octets
smg_comms_files 146 if Pos + Integer(U16) - 1 > Msg'Last or
smg_comms_files 147 U16 < 3 then
smg_comms_files 148 raise Invalid_msg;
smg_comms_files 149 end if;
smg_comms_files 150 U16 := U16 - 2;
smg_comms_actions... 151 Content_Len := Raw_Types.Text_Len(U16);
smg_comms_files 152 -- set start and end for reading content later, when ready
smg_comms_files 153 S_Content := Pos;
smg_comms_files 154 E_Content := Pos + Integer(U16) - 1;
smg_comms_files 155 Pos := Pos + Content_Len;
smg_comms_files 156
smg_comms_files 157 -- read counter
smg_comms_files 158 Read_U16( Msg, Pos, U16 );
smg_comms_files 159 -- check chunking validity i.e. if counter>0 then no padding
smg_comms_files 160 if U16 /= 0 and Pos /= Msg'Last then
smg_comms_files 161 raise Invalid_Msg;
smg_comms_files 162 end if;
smg_comms_files 163
smg_comms_files 164 -- create File_Chunk structure and fill it with data from Msg
smg_comms_files 165 declare
smg_comms_files 166 FC : File_Chunk( Len => Content_Len,
smg_comms_files 167 Count => U16,
smg_comms_files 168 Name_Len => S_Len);
smg_comms_files 169 begin
smg_comms_files 170 -- read from Msg
smg_comms_files 171 FC.Content := Msg( S_Content..E_Content );
smg_comms_files 172 Octets_To_String( Msg( S_Name..E_Name ), FC.Filename);
smg_comms_files 173 -- copy to output var
smg_comms_files 174 Chunk := FC;
smg_comms_files 175 end;
smg_comms_files 176
smg_comms_files 177 end Read_File_Transfer;
smg_comms_files 178
smg_comms_files 179 ---- File Requests ----
smg_comms_files 180 procedure Write_File_Request( FR : in Filenames;
smg_comms_files 181 Counter : in Interfaces.Unsigned_16;
smg_comms_actions... 182 Pad : in Raw_Types.Octets_8;
smg_comms_files 183 Msg : out Raw_Types.Serpent_Msg;
smg_comms_files 184 Written : out Natural) is
smg_comms_files 185 Pos : Integer := Msg'First;
smg_comms_files 186 Max_Pos: Integer := Msg'Last - 2; -- 2 octets at end for counter
smg_comms_files 187 Text_Sz: Integer;
smg_comms_files 188 Max_Sz : Integer;
smg_comms_files 189 begin
smg_comms_files 190 -- write ID for File Request type
smg_comms_files 191 Msg( Pos ) := File_Req_S_Type;
smg_comms_files 192 Pos := Pos + 1;
smg_comms_files 193
smg_comms_files 194 -- write Text size: filenames + separators
smg_comms_files 195 -- consider fewer filenames if they don't ALL fit
smg_comms_files 196 -- 2 octets are taken by size itself
smg_comms_files 197 Max_Sz := Max_Pos - Pos - 1;
smg_comms_files 198 Text_Sz := FR.Sz + FR.F_No - 1;
smg_comms_files 199 if Text_Sz > Max_Sz then
smg_comms_files 200 -- walk the array of filenames backwards and stop when they fit
smg_comms_files 201 Written := FR.F_No - 1;
smg_comms_files 202 -- calculate actual size written based on start of first discarded
smg_comms_files 203 -- filename and (Written -1) octets for needed separators
smg_comms_files 204 Text_Sz := Integer(FR.Starts(Written+1)) - FR.Starts'First +
smg_comms_files 205 (Written - 1);
smg_comms_files 206
smg_comms_files 207 -- loop until either fits or nothing left
smg_comms_files 208 while Written > 0 and Text_Sz > Max_Sz loop
smg_comms_files 209 Written := Written - 1;
smg_comms_files 210 Text_Sz := Integer(FR.Starts(Written+1))- FR.Starts'First +
smg_comms_files 211 (Written - 1);
smg_comms_files 212 end loop;
smg_comms_files 213 -- check that there is what to write, since nothing -> invalid message
smg_comms_files 214 if Written = 0 then
smg_comms_files 215 raise Invalid_Msg;
smg_comms_files 216 end if;
smg_comms_files 217
smg_comms_files 218 else --from if Text_Sz > Max_Sz
smg_comms_files 219 -- ALL are written
smg_comms_files 220 Written := FR.F_No;
smg_comms_files 221 end if;
smg_comms_files 222
smg_comms_files 223 -- write Text_Sz + 2 (i.e. TOTAL size)
smg_comms_files 224 if Text_Sz + 2 > Integer(Interfaces.Unsigned_16'Last) then
smg_comms_files 225 raise Invalid_Msg;
smg_comms_files 226 end if;
smg_comms_files 227
smg_comms_files 228 Write_U16( Msg, Pos, Interfaces.Unsigned_16(Text_Sz+2) );
smg_comms_files 229
smg_comms_files 230 -- write filenames separated by Sep
smg_comms_files 231 for I in 1..Written loop
smg_comms_files 232 declare
smg_comms_files 233 Start_Pos : Positive;
smg_comms_files 234 End_Pos : Positive;
smg_comms_files 235 Len : Positive;
smg_comms_files 236 begin
smg_comms_files 237 -- current start pos in FR.S
smg_comms_files 238 Start_Pos := Positive( FR.Starts( FR.Starts'First + I - 1));
smg_comms_files 239
smg_comms_files 240 -- calculate end based on start of next name or last
smg_comms_files 241 if I < FR.F_No then
smg_comms_files 242 End_Pos := Positive( FR.Starts( FR.Starts'First + I)) - 1;
smg_comms_files 243 else
smg_comms_files 244 End_Pos := FR.S'Last;
smg_comms_files 245 end if;
smg_comms_files 246
smg_comms_files 247 -- NB: this WILL fail if starting positions are not in order!
smg_comms_files 248 Len := End_Pos - Start_Pos + 1;
smg_comms_files 249 if Len <= 0 then
smg_comms_files 250 raise Invalid_Msg;
smg_comms_files 251 end if;
smg_comms_files 252
smg_comms_files 253 --write the actual filename
smg_comms_files 254 String_To_Octets( FR.S( Start_Pos..End_Pos ), Msg(Pos..Pos+Len-1) );
smg_comms_files 255 Pos := Pos + Len;
smg_comms_files 256
smg_comms_files 257 --if it's not the last one, write a separator
smg_comms_files 258 if I < Written then
smg_comms_files 259 Msg(Pos) := Sep;
smg_comms_files 260 Pos := Pos + 1;
smg_comms_files 261 end if;
smg_comms_files 262 end;
smg_comms_files 263 end loop;
smg_comms_files 264
smg_comms_actions... 265 -- write counter + padding
smg_comms_actions... 266 Write_End( Msg, Pos, Counter, Pad );
smg_comms_files 267
smg_comms_files 268 end Write_File_Request;
smg_comms_files 269
smg_comms_files 270 -- Reads a request for files; the opposite of Write_File_Request above
smg_comms_files 271 procedure Read_File_Request( Msg : in Raw_Types.Serpent_Msg;
smg_comms_files 272 Counter : out Interfaces.Unsigned_16;
smg_comms_files 273 FR : out Filenames) is
smg_comms_files 274 Pos : Integer := Msg'First;
smg_comms_files 275 Max_Pos : Integer := Msg'Last - 2; --at least 2 reserved for counter
smg_comms_files 276 Text_Sz : Integer;
smg_comms_files 277 Max_Sz : Integer := Max_Pos - Pos - 1; --text only i.e. w.o. size itself
smg_comms_files 278 F_No : Integer;
smg_comms_files 279 U16 : Interfaces.Unsigned_16;
smg_comms_files 280 begin
smg_comms_files 281 -- read type ID and check
smg_comms_files 282 if Msg(Pos) /= File_Req_S_Type then
smg_comms_files 283 raise Invalid_Msg;
smg_comms_files 284 end if;
smg_comms_files 285 Pos := Pos + 1;
smg_comms_files 286
smg_comms_files 287 -- read total size of filenames+separators
smg_comms_files 288 Read_U16( Msg, Pos, U16 );
smg_comms_files 289 Text_Sz := Integer(U16);
smg_comms_files 290 -- take away the 2 octets for size itself
smg_comms_files 291 Text_Sz := Text_Sz - 2;
smg_comms_files 292
smg_comms_files 293 -- check that Text_Sz is not overflowing/underflowing
smg_comms_files 294 if Text_Sz < 1 or Text_Sz > Max_Sz then
smg_comms_files 295 raise Invalid_Msg;
smg_comms_files 296 end if;
smg_comms_files 297
smg_comms_files 298 -- count first the separators to know how many filenames
smg_comms_files 299 -- NB: there is always at least 1 filename as Text_Sz > 0
smg_comms_files 300 F_No := 1;
smg_comms_files 301 for I in Pos .. Pos + Text_Sz - 1 loop
smg_comms_files 302 if Msg(I) = Sep then
smg_comms_files 303 F_No := F_No + 1;
smg_comms_files 304 end if;
smg_comms_files 305 end loop;
smg_comms_files 306
smg_comms_files 307 -- create the output structure and discard separators
smg_comms_files 308 -- text without separators should be Text_Sz - F_No + 1
smg_comms_files 309 -- (because ONLY one separator between 2 filenames allowed)
smg_comms_files 310 -- if it's not that => Invalid_Msg
smg_comms_files 311 -- F_No and Text_Sz are not overflow (earlier check + calc)
smg_comms_files 312 declare
smg_comms_actions... 313 F : Filenames(Raw_Types.Text_Len(F_No),
smg_comms_actions... 314 Raw_Types.Text_Len(Text_Sz-F_No+1));
smg_comms_files 315 S_Pos : Positive;
smg_comms_files 316 Index : Positive;
smg_comms_files 317 begin
smg_comms_files 318 S_Pos := F.S'First;
smg_comms_files 319 Index := F.Starts'First;
smg_comms_files 320 F.Starts(Index) := Interfaces.Unsigned_16(S_Pos);
smg_comms_files 321
smg_comms_files 322 for I in Pos .. Pos + Text_Sz - 1 loop
smg_comms_files 323 -- copy over to F.S anything that is not separator
smg_comms_files 324 if Msg(I) /= Sep then
smg_comms_files 325 F.S( S_Pos ) := Character'Val(Msg(I));
smg_comms_files 326 S_Pos := S_Pos + 1;
smg_comms_files 327 else
smg_comms_files 328 -- if it's separator, check and if ok, add next as start
smg_comms_files 329 if I = Pos + Text_Sz or -- separator as last character is error
smg_comms_files 330 Msg(I+1) = Sep or -- 2 consecutive separators is error
smg_comms_files 331 Index >= F.Starts'Last then -- too many separators is error
smg_comms_files 332 raise Invalid_Msg;
smg_comms_files 333 else
smg_comms_files 334 Index := Index + 1;
smg_comms_files 335 F.Starts( Index ) := Interfaces.Unsigned_16(S_Pos);
smg_comms_files 336 end if;
smg_comms_files 337 end if;
smg_comms_files 338 end loop;
smg_comms_files 339
smg_comms_files 340 -- copy the whole structure to output variable
smg_comms_files 341 FR := F;
smg_comms_files 342 end;
smg_comms_files 343
smg_comms_files 344 -- read message counter now
smg_comms_files 345 Pos := Pos + Text_Sz;
smg_comms_files 346 Read_U16( Msg, Pos, Counter );
smg_comms_files 347
smg_comms_files 348 end Read_File_Request;
smg_comms_keymgm 349
smg_comms_actions... 350 -- writes the action (octets+length) into the specified Serpent message
smg_comms_actions... 351 procedure Write_Action( A : in Raw_Types.Text_Octets;
smg_comms_actions... 352 Counter : in Interfaces.Unsigned_16;
smg_comms_actions... 353 Pad : in Raw_Types.Octets_8;
smg_comms_actions... 354 Msg : out Raw_Types.Serpent_Msg) is
smg_comms_actions... 355 Pos : Natural := Msg'First + 1;
smg_comms_actions... 356 MaxPos : Natural := Msg'Last - 1; --2 octets reserved for counter at end
smg_comms_actions... 357 U16 : Interfaces.Unsigned_16;
smg_comms_actions... 358 begin
smg_comms_actions... 359 -- check whether given action FITS into a Serpent message
smg_comms_actions... 360 if Pos + 2 + A.Len > MaxPos then
smg_comms_actions... 361 raise Invalid_Msg;
smg_comms_actions... 362 end if;
smg_comms_actions... 363
smg_comms_actions... 364 -- write correct type ID
smg_comms_actions... 365 Msg( Msg'First ) := Client_Action_S_Type;
smg_comms_actions... 366
smg_comms_actions... 367 -- write action's TOTAL length
smg_comms_actions... 368 U16 := Interfaces.Unsigned_16(A.Len + 2);
smg_comms_actions... 369 Write_U16( Msg, Pos, U16 );
smg_comms_actions... 370
smg_comms_actions... 371 -- write the action itself
smg_comms_actions... 372 Msg( Pos..Pos+A.Len-1 ) := A.Content;
smg_comms_actions... 373 Pos := Pos + A.Len;
smg_comms_actions... 374
smg_comms_actions... 375 -- write counter + padding
smg_comms_actions... 376 Write_End( Msg, Pos, Counter, Pad );
smg_comms_actions... 377
smg_comms_actions... 378 end Write_Action;
smg_comms_actions... 379
smg_comms_actions... 380 -- reads a client action as octets+length from the given Serpent message
smg_comms_actions... 381 procedure Read_Action( Msg : in Raw_Types.Serpent_Msg;
smg_comms_actions... 382 Counter : out Interfaces.Unsigned_16;
smg_comms_actions... 383 A : out Raw_Types.Text_Octets) is
smg_comms_actions... 384 Pos : Natural := Msg'First + 1;
smg_comms_actions... 385 U16 : Interfaces.Unsigned_16;
smg_comms_actions... 386 begin
smg_comms_actions... 387 -- read and check message type ID
smg_comms_actions... 388 if Msg( Msg'First ) /= Client_Action_S_Type then
smg_comms_actions... 389 raise Invalid_Msg;
smg_comms_actions... 390 end if;
smg_comms_actions... 391
smg_comms_actions... 392 -- read size of action (content+ 2 octets the size itself)
smg_comms_actions... 393 Read_U16( Msg, Pos, U16 );
smg_comms_actions... 394
smg_comms_actions... 395 -- check size
smg_comms_actions... 396 if U16 < 3 or Pos + Natural(U16) - 2 > Msg'Last - 1 then
smg_comms_actions... 397 raise Invalid_Msg;
smg_comms_actions... 398 else
smg_comms_actions... 399 U16 := U16 - 2; --size of content only
smg_comms_actions... 400 end if;
smg_comms_actions... 401
smg_comms_actions... 402 -- create action, read it from message + assign to output variable
smg_comms_actions... 403 declare
smg_comms_actions... 404 Act : Raw_Types.Text_Octets( Raw_Types.Text_Len( U16 ) );
smg_comms_actions... 405 begin
smg_comms_actions... 406 Act.Content := Msg( Pos..Pos+Act.Len-1 );
smg_comms_actions... 407 Pos := Pos + Act.Len;
smg_comms_actions... 408 A := Act;
smg_comms_actions... 409 end;
smg_comms_actions... 410
smg_comms_actions... 411 -- read counter
smg_comms_actions... 412 Read_U16( Msg, Pos, Counter );
smg_comms_actions... 413
smg_comms_actions... 414 end Read_Action;
smg_comms_actions... 415
smg_comms_keymgm 416 ------------------
smg_comms_keymgm 417 -- RSA Messages --
smg_comms_keymgm 418 ------------------
smg_comms_keymgm 419
smg_comms_actions... 420 procedure Write_RKeys_RMsg( K : in Player_RSA;
smg_comms_actions... 421 Counter : in Interfaces.Unsigned_16;
smg_comms_actions... 422 Pad : in Raw_Types.Octets_8;
smg_comms_actions... 423 Msg : out Raw_Types.RSA_Msg) is
smg_comms_actions... 424 Pos : Natural := Msg'First + 1;
smg_comms_actions... 425 begin
smg_comms_actions... 426 -- write correct message type
smg_comms_actions... 427 Msg( Msg'First ) := RKeys_R_Type;
smg_comms_actions... 428
smg_comms_actions... 429 -- write protocol version and subversion
smg_comms_actions... 430 Msg( Pos ) := K.Proto_V;
smg_comms_actions... 431 Pos := Pos + 1;
smg_comms_actions... 432 Write_U16( Msg, Pos, K.Proto_Subv );
smg_comms_actions... 433
smg_comms_actions... 434 -- write keccak hash of client binary
smg_comms_actions... 435 Msg( Pos..Pos + K.Client_Hash'Length-1 ) := K.Client_Hash;
smg_comms_actions... 436 Pos := Pos + K.Client_Hash'Length;
smg_comms_actions... 437
smg_comms_actions... 438 -- write e of RSA key
smg_comms_actions... 439 Msg( Pos..Pos + K.e'Length - 1 ) := K.e;
smg_comms_actions... 440 Pos := Pos + K.e'Length;
smg_comms_actions... 441
smg_comms_actions... 442 -- write n of RSA key
smg_comms_actions... 443 Msg( Pos..Pos + K.n'Length - 1 ) := K.n;
smg_comms_actions... 444 Pos := Pos + K.n'Length;
smg_comms_actions... 445
smg_comms_actions... 446 -- write preferred padding
smg_comms_actions... 447 Msg( Pos..Pos + K.Padding'Length - 1 ) := K.Padding;
smg_comms_actions... 448 Pos := Pos + K.Padding'Length;
smg_comms_actions... 449
smg_comms_actions... 450 -- write counter + padding
smg_comms_actions... 451 Write_End( Msg, Pos, Counter, Pad );
smg_comms_actions... 452
smg_comms_actions... 453 end Write_RKeys_RMsg;
smg_comms_actions... 454
smg_comms_actions... 455 -- Reads a RSA Keyset (Player_RSA structures) from the given RSA Message.
smg_comms_actions... 456 -- Opposite of Write_RKeys_RMsg above
smg_comms_actions... 457 procedure Read_RKeys_RMsg( Msg : in Raw_Types.RSA_Msg;
smg_comms_actions... 458 Counter : out Interfaces.Unsigned_16;
smg_comms_actions... 459 K : out Player_RSA) is
smg_comms_actions... 460 Pos : Natural := Msg'First + 1;
smg_comms_actions... 461 begin
smg_comms_actions... 462 -- check type id and raise exception if incorrect
smg_comms_actions... 463 if Msg(Msg'First) /= RKeys_R_Type then
smg_comms_actions... 464 raise Invalid_Msg;
smg_comms_actions... 465 end if;
smg_comms_actions... 466
smg_comms_actions... 467 -- read protocol version and subversion
smg_comms_actions... 468 K.Proto_V := Msg( Pos );
smg_comms_actions... 469 Pos := Pos + 1;
smg_comms_actions... 470 Read_U16( Msg, Pos, K.Proto_Subv );
smg_comms_actions... 471
smg_comms_actions... 472 -- read Keccak hash of client binary
smg_comms_actions... 473 K.Client_Hash := Msg( Pos..Pos+K.Client_Hash'Length - 1 );
smg_comms_actions... 474 Pos := Pos + K.Client_Hash'Length;
smg_comms_actions... 475
smg_comms_actions... 476 -- read e
smg_comms_actions... 477 K.e := Msg( Pos .. Pos + K.e'Length - 1 );
smg_comms_actions... 478 Pos := Pos + K.e'Length;
smg_comms_actions... 479
smg_comms_actions... 480 -- read n
smg_comms_actions... 481 K.n := Msg( Pos .. Pos + K.n'Length - 1 );
smg_comms_actions... 482 Pos := Pos + K.n'Length;
smg_comms_actions... 483
smg_comms_actions... 484 -- read choice of padding
smg_comms_actions... 485 K.Padding := Msg( Pos .. Pos+K.Padding'Length - 1 );
smg_comms_actions... 486 Pos := Pos + K.Padding'Length;
smg_comms_actions... 487
smg_comms_actions... 488 -- read message counter
smg_comms_actions... 489 Read_U16( Msg, Pos, Counter );
smg_comms_actions... 490
smg_comms_actions... 491 -- the rest is message padding, so ignore it
smg_comms_actions... 492
smg_comms_actions... 493 end Read_RKeys_RMsg;
smg_comms_actions... 494
smg_comms_keymgm 495 procedure Write_SKeys_RMsg( Keyset : in Serpent_Keyset;
smg_comms_keymgm 496 Counter : in Interfaces.Unsigned_16;
smg_comms_actions... 497 Pad : in Raw_Types.Octets_8;
smg_comms_keymgm 498 Msg : out Raw_Types.RSA_Msg) is
smg_comms_keymgm 499 begin
smg_comms_keymgm 500 -- call internal write of Serpent keys with correct type ID
smg_comms_actions... 501 Write_SKeys( Keyset, Counter, SKeys_R_Type, Pad, Msg );
smg_comms_keymgm 502 end Write_SKeys_RMsg;
smg_comms_keymgm 503
smg_comms_keymgm 504 procedure Read_SKeys_RMsg( Msg : in Raw_Types.RSA_Msg;
smg_comms_keymgm 505 Counter : out Interfaces.Unsigned_16;
smg_comms_keymgm 506 Keyset : out Serpent_Keyset) is
smg_comms_keymgm 507 begin
smg_comms_keymgm 508 -- check type id and call internal Read_SKeys if correct
smg_comms_keymgm 509 if Msg(Msg'First) /= SKeys_R_Type then
smg_comms_keymgm 510 raise Invalid_Msg;
smg_comms_keymgm 511 else
smg_comms_keymgm 512 Read_SKeys( Msg, Counter, Keyset );
smg_comms_keymgm 513 end if;
smg_comms_keymgm 514 end Read_SKeys_RMsg;
smg_comms_keymgm 515
smg_comms_keymgm 516 procedure Write_KMgm_RMsg( KMgm : in Keys_Mgm;
smg_comms_keymgm 517 Counter : in Interfaces.Unsigned_16;
smg_comms_actions... 518 Pad : in Raw_Types.Octets_8;
smg_comms_keymgm 519 Msg : out Raw_Types.RSA_Msg) is
smg_comms_keymgm 520 begin
smg_comms_keymgm 521 -- call internal write of key mgm with correct type ID
smg_comms_actions... 522 Write_KMgm( KMgm, Counter, Key_Mgm_R_Type, Pad, Msg );
smg_comms_keymgm 523 end Write_KMgm_RMsg;
smg_comms_keymgm 524
smg_comms_keymgm 525 procedure Read_KMgm_RMsg( Msg : in Raw_Types.RSA_Msg;
smg_comms_keymgm 526 Counter : out Interfaces.Unsigned_16;
smg_comms_keymgm 527 KMgm : out Keys_Mgm) is
smg_comms_keymgm 528 begin
smg_comms_keymgm 529 -- check type id and call internal Read_KMgm if correct
smg_comms_keymgm 530 if Msg(Msg'First) /= Key_Mgm_R_Type then
smg_comms_keymgm 531 raise Invalid_Msg;
smg_comms_keymgm 532 else
smg_comms_keymgm 533 Read_KMgm( Msg, Counter, KMgm );
smg_comms_keymgm 534 end if;
smg_comms_keymgm 535 end Read_KMgm_RMsg;
smg_comms_keymgm 536
smg_comms_files 537
smg_comms_files 538 ----------Utilities ----------
smg_comms_files 539 -- String to Octets conversion
smg_comms_files 540 procedure String_To_Octets(Str: in String; O: out Raw_Types.Octets) is
smg_comms_files 541 begin
smg_comms_files 542 Assert( Str'Length = O'Length );
smg_comms_files 543 for I in 1..Str'Length loop
smg_comms_files 544 O( O'First+I-1 ) := Character'Pos(Str(Str'First + I - 1 ));
smg_comms_files 545 end loop;
smg_comms_files 546 end String_To_Octets;
smg_comms_files 547
smg_comms_files 548 -- Octets to string conversion
smg_comms_files 549 -- NB: Str'Length has to be EQUAL to Octets'Length!
smg_comms_files 550 procedure Octets_To_String(O: in Raw_Types.Octets; Str: out String) is
smg_comms_files 551 begin
smg_comms_files 552 Assert( O'Length = Str'Length );
smg_comms_files 553 for I in 1..O'Length loop
smg_comms_files 554 Str( Str'First+I-1 ) := Character'Val(O(O'First + I - 1 ));
smg_comms_files 555 end loop;
smg_comms_files 556 end Octets_To_String;
smg_comms_files 557
smg_comms_keymgm 558 ------------------
smg_comms_keymgm 559 -- private part --
smg_comms_keymgm 560 ------------------
smg_comms_keymgm 561 procedure Cast_LE( LE: in out Raw_Types.Octets ) is
smg_comms_keymgm 562 begin
smg_comms_keymgm 563 -- flip octets ONLY if native is big endian.
smg_comms_keymgm 564 if System.Default_Bit_Order = System.High_Order_First then
smg_comms_keymgm 565 declare
smg_comms_keymgm 566 BE: constant Raw_Types.Octets := LE;
smg_comms_keymgm 567 begin
smg_comms_keymgm 568 for I in 1..LE'Length loop
smg_comms_keymgm 569 LE(LE'First+I-1) := BE(BE'Last-I+1);
smg_comms_keymgm 570 end loop;
smg_comms_keymgm 571 end;
smg_comms_keymgm 572 end if;
smg_comms_keymgm 573 -- NOTHING to do for native little endian
smg_comms_keymgm 574 end Cast_LE;
smg_comms_keymgm 575
smg_comms_keymgm 576 procedure Write_SKeys( Keyset : in Serpent_Keyset;
smg_comms_keymgm 577 Counter : in Interfaces.Unsigned_16;
smg_comms_keymgm 578 Type_ID : in Interfaces.Unsigned_8;
smg_comms_actions... 579 Pad : in Raw_Types.Octets_8;
smg_comms_keymgm 580 Msg : out Raw_Types.Octets) is
smg_comms_skeys_s... 581 Pos : Integer := Msg'First;
smg_comms_skeys_s... 582 Check : CRC32.CRC32;
smg_comms_skeys_s... 583 K : Serpent.Key;
smg_comms_skeys_s... 584 begin
smg_comms_skeys_s... 585 -- write Type ID
smg_comms_keymgm 586 Msg(Pos) := Type_ID;
smg_comms_skeys_s... 587 Pos := Pos + 1;
smg_comms_skeys_s... 588
smg_comms_skeys_s... 589 -- write count of keys (NB: this IS 8 bits by definition)
smg_comms_skeys_s... 590 Msg(Pos) := Keyset.Keys'Length;
smg_comms_skeys_s... 591 Pos := Pos + 1;
smg_comms_skeys_s... 592
smg_comms_skeys_s... 593 -- write keys
smg_comms_skeys_s... 594 for I in Keyset.Keys'Range loop
smg_comms_skeys_s... 595 -- retrieve Key to write
smg_comms_skeys_s... 596 K := Keyset.Keys( I );
smg_comms_skeys_s... 597
smg_comms_skeys_s... 598 -- write key itself
smg_comms_skeys_s... 599 Msg(Pos..Pos+K'Length-1) := K;
smg_comms_skeys_s... 600 -- ensure little endian order in message
smg_comms_skeys_s... 601 Cast_LE(Msg(Pos..Pos+K'Length-1));
smg_comms_skeys_s... 602 Pos := Pos + K'Length;
smg_comms_skeys_s... 603
smg_comms_skeys_s... 604 -- write CRC of key
smg_comms_skeys_s... 605 Check := CRC32.CRC( K );
smg_comms_skeys_s... 606 Msg(Pos..Pos+3) := Raw_Types.Cast(Check);
smg_comms_skeys_s... 607 Cast_LE(Msg(Pos..Pos+3));
smg_comms_skeys_s... 608 Pos := Pos + 4;
smg_comms_skeys_s... 609 end loop;
smg_comms_skeys_s... 610
smg_comms_skeys_s... 611 -- write flag
smg_comms_skeys_s... 612 Msg(Pos) := Keyset.Flag;
smg_comms_skeys_s... 613 Pos := Pos + 1;
smg_comms_skeys_s... 614
smg_comms_actions... 615 -- write counter + padding
smg_comms_actions... 616 Write_End( Msg, Pos, Counter, Pad );
smg_comms_skeys_s... 617
smg_comms_keymgm 618 end Write_SKeys;
smg_comms_skeys_s... 619
smg_comms_keymgm 620 procedure Read_SKeys( Msg : in Raw_Types.Octets;
smg_comms_keymgm 621 Counter : out Interfaces.Unsigned_16;
smg_comms_keymgm 622 Keyset : out Serpent_Keyset) is
smg_comms_skeys_s... 623 Pos: Integer := Msg'First;
smg_comms_skeys_s... 624 begin
smg_comms_skeys_s... 625 -- read type and check
smg_comms_keymgm 626 if Msg(Pos) = SKeys_S_Type or
smg_comms_keymgm 627 Msg(Pos) = SKeys_R_Type then
smg_comms_skeys_s... 628 Pos := Pos + 1;
smg_comms_skeys_s... 629 else
smg_comms_skeys_s... 630 raise Invalid_Msg;
smg_comms_skeys_s... 631 end if;
smg_comms_skeys_s... 632
smg_comms_skeys_s... 633 -- read count of keys and check
smg_comms_skeys_s... 634 if Msg(Pos) in Keys_Count'Range then
smg_comms_skeys_s... 635 declare
smg_comms_skeys_s... 636 N : Keys_Count := Keys_Count(Msg(Pos));
smg_comms_skeys_s... 637 KS : Serpent_Keyset(N);
smg_comms_skeys_s... 638 K : Serpent.Key;
smg_comms_skeys_s... 639 Check : CRC32.CRC32;
smg_comms_skeys_s... 640 O4 : Raw_Types.Octets_4;
smg_comms_skeys_s... 641 begin
smg_comms_skeys_s... 642 Pos := Pos + 1;
smg_comms_skeys_s... 643 --read keys and check crc for each
smg_comms_skeys_s... 644 for I in 1 .. N loop
smg_comms_skeys_s... 645 -- read key and advance pos
smg_comms_skeys_s... 646 K := Msg(Pos..Pos+K'Length-1);
smg_comms_skeys_s... 647 Cast_LE(K);
smg_comms_skeys_s... 648 Pos := Pos + K'Length;
smg_comms_skeys_s... 649 -- read crc and compare to crc32(key)
smg_comms_skeys_s... 650 O4 := Msg(Pos..Pos+3);
smg_comms_skeys_s... 651 Cast_LE(O4);
smg_comms_skeys_s... 652 Check := Raw_Types.Cast(O4);
smg_comms_skeys_s... 653 Pos := Pos + 4;
smg_comms_skeys_s... 654 if Check /= CRC32.CRC(K) then
smg_comms_skeys_s... 655 raise Invalid_Msg;
smg_comms_skeys_s... 656 end if;
smg_comms_skeys_s... 657 -- if it got here, key is fine so add to set
smg_comms_skeys_s... 658 KS.Keys(KS.Keys'First + I -1) := K;
smg_comms_skeys_s... 659 end loop;
smg_comms_skeys_s... 660 -- read and set flag
smg_comms_skeys_s... 661 KS.Flag := Msg(Pos);
smg_comms_skeys_s... 662 Pos := Pos + 1;
smg_comms_skeys_s... 663 -- read and set message counter
smg_comms_files 664 Read_U16( Msg, Pos, Counter );
smg_comms_skeys_s... 665 -- rest of message is padding so it's ignored
smg_comms_skeys_s... 666 -- copy keyset to output variable
smg_comms_skeys_s... 667 Keyset := KS;
smg_comms_skeys_s... 668 end;
smg_comms_skeys_s... 669 else
smg_comms_skeys_s... 670 raise Invalid_Msg;
smg_comms_skeys_s... 671 end if;
smg_comms_keymgm 672 end Read_SKeys;
smg_comms_skeys_s... 673
smg_comms_keymgm 674 -- writes given key management structure to the given octets array
smg_comms_keymgm 675 procedure Write_KMgm( KMgm : in Keys_Mgm;
smg_comms_keymgm 676 Counter : in Interfaces.Unsigned_16;
smg_comms_keymgm 677 Type_ID : in Interfaces.Unsigned_8;
smg_comms_actions... 678 Pad : in Raw_Types.Octets_8;
smg_comms_keymgm 679 Msg : out Raw_Types.Octets) is
smg_comms_keymgm 680 Pos : Integer := Msg'First;
smg_comms_skeys_s... 681 begin
smg_comms_keymgm 682 -- write given type id
smg_comms_keymgm 683 Msg(Pos) := Type_ID;
smg_comms_keymgm 684 Pos := Pos + 1;
smg_comms_keymgm 685
smg_comms_keymgm 686 -- write count of server keys requested
smg_comms_keymgm 687 Msg(Pos) := KMgm.N_Server;
smg_comms_keymgm 688 Pos := Pos + 1;
smg_comms_keymgm 689
smg_comms_keymgm 690 -- write count of client keys requested
smg_comms_keymgm 691 Msg(Pos) := KMgm.N_Client;
smg_comms_keymgm 692 Pos := Pos + 1;
smg_comms_keymgm 693
smg_comms_keymgm 694 -- write id of key preferred for further inbound Serpent messages
smg_comms_keymgm 695 Msg(Pos) := KMgm.Key_ID;
smg_comms_keymgm 696 Pos := Pos + 1;
smg_comms_keymgm 697
smg_comms_keymgm 698 -- write count of burnt keys in this message
smg_comms_keymgm 699 Msg(Pos..Pos) := Cast( KMgm.N_Burnt );
smg_comms_keymgm 700 Pos := Pos + 1;
smg_comms_keymgm 701
smg_comms_keymgm 702 -- if there are any burnt keys, write their ids
smg_comms_keymgm 703 if KMgm.N_Burnt > 0 then
smg_comms_keymgm 704 Msg( Pos .. Pos + KMgm.Burnt'Length - 1 ) := KMgm.Burnt;
smg_comms_keymgm 705 Pos := Pos + KMgm.Burnt'Length;
smg_comms_skeys_s... 706 end if;
smg_comms_keymgm 707
smg_comms_actions... 708 -- write counter + padding
smg_comms_actions... 709 Write_End( Msg, Pos, Counter, Pad );
smg_comms_keymgm 710
smg_comms_keymgm 711 end Write_KMgm;
smg_comms_keymgm 712
smg_comms_keymgm 713 -- attempts to read from the given array of octets a key management structure
smg_comms_keymgm 714 procedure Read_KMgm( Msg : in Raw_Types.Octets;
smg_comms_keymgm 715 Counter : out Interfaces.Unsigned_16;
smg_comms_keymgm 716 KMgm : out Keys_Mgm) is
smg_comms_keymgm 717 Pos : Integer := Msg'First;
smg_comms_keymgm 718 Burnt_Pos : Integer := Msg'First + 4;
smg_comms_keymgm 719 begin
smg_comms_keymgm 720 -- read type and check
smg_comms_keymgm 721 if Msg(Pos) = Key_Mgm_S_Type or
smg_comms_keymgm 722 Msg(Pos) = Key_Mgm_R_Type then
smg_comms_keymgm 723 Pos := Pos + 1;
smg_comms_keymgm 724 else
smg_comms_keymgm 725 raise Invalid_Msg;
smg_comms_keymgm 726 end if;
smg_comms_keymgm 727
smg_comms_keymgm 728 -- read the count of burnt keys and check
smg_comms_keymgm 729 -- NB: Burnt_Pos IS in range of Counter_8bits since it's an octet
smg_comms_keymgm 730 declare
smg_comms_keymgm 731 N_Burnt : Counter_8bits := Counter_8bits(Msg(Burnt_Pos));
smg_comms_keymgm 732 Mgm : Keys_Mgm(N_Burnt);
smg_comms_keymgm 733 begin
smg_comms_keymgm 734 -- read count of server keys requested
smg_comms_keymgm 735 Mgm.N_Server := Msg(Pos);
smg_comms_keymgm 736 Pos := Pos + 1;
smg_comms_keymgm 737
smg_comms_keymgm 738 -- read count of client keys requested
smg_comms_keymgm 739 Mgm.N_Client := Msg(Pos);
smg_comms_keymgm 740 Pos := Pos + 1;
smg_comms_keymgm 741
smg_comms_keymgm 742 -- read ID of Serpent key preferred for further inbound messages
smg_comms_keymgm 743 Mgm.Key_ID := Msg(Pos);
smg_comms_keymgm 744 Pos := Pos + 2; --skip the count of burnt keys as it's read already
smg_comms_keymgm 745
smg_comms_keymgm 746 -- read ids of burnt keys, if any
smg_comms_keymgm 747 if N_Burnt > 0 then
smg_comms_keymgm 748 Mgm.Burnt := Msg(Pos..Pos+N_Burnt-1);
smg_comms_keymgm 749 Pos := Pos + N_Burnt;
smg_comms_keymgm 750 end if;
smg_comms_keymgm 751
smg_comms_keymgm 752 -- read and set message counter
smg_comms_files 753 Read_U16( Msg, Pos, Counter );
smg_comms_keymgm 754 -- rest of message is padding so it's ignored
smg_comms_keymgm 755 -- copy the keys mgm structure to output param
smg_comms_keymgm 756 KMgm := Mgm;
smg_comms_keymgm 757 end;
smg_comms_keymgm 758 end Read_KMgm;
smg_comms_keymgm 759
smg_comms_files 760 -- Write a 16 bits value to Octets at Pos; Pos increases by 2.
smg_comms_files 761 procedure Write_U16( Msg: in out Raw_Types.Octets;
smg_comms_files 762 Pos: in out Natural;
smg_comms_files 763 U16: in Interfaces.Unsigned_16) is
smg_comms_files 764 begin
smg_comms_files 765 Msg(Pos..Pos+1) := Raw_Types.Cast(U16);
smg_comms_files 766 Cast_LE(Msg(Pos..Pos+1));
smg_comms_files 767 Pos := Pos + 2;
smg_comms_files 768 end Write_U16;
smg_comms_files 769
smg_comms_files 770 -- Read a 16-bits values from Octets from Pos; Pos increases by 2.
smg_comms_files 771 procedure Read_U16( Msg: in Raw_Types.Octets;
smg_comms_files 772 Pos: in out Natural;
smg_comms_files 773 U16: out Interfaces.Unsigned_16) is
smg_comms_files 774 O2 : Raw_Types.Octets_2;
smg_comms_files 775 begin
smg_comms_files 776 O2 := Msg(Pos..Pos+1);
smg_comms_files 777 Cast_LE(O2);
smg_comms_files 778 U16 := Raw_Types.Cast(O2);
smg_comms_files 779 Pos := Pos + 2;
smg_comms_files 780 end Read_U16;
smg_comms_skeys_s... 781
smg_comms_actions... 782 -- Writes Counter and padding (rng or otherwise) into Msg starting from Pos.
smg_comms_actions... 783 procedure Write_End( Msg : in out Raw_Types.Octets;
smg_comms_actions... 784 Pos : in out Natural;
smg_comms_actions... 785 Counter : in Interfaces.Unsigned_16;
smg_comms_actions... 786 Padding : in Raw_Types.Octets_8) is
smg_comms_actions... 787 begin
smg_comms_actions... 788 -- check that there is space for Counter at the very least
smg_comms_actions... 789 if Pos > Msg'Last - 1 then
smg_comms_actions... 790 raise Invalid_Msg;
smg_comms_actions... 791 end if;
smg_comms_actions... 792
smg_comms_actions... 793 -- write counter
smg_comms_actions... 794 Write_U16( Msg, Pos, Counter );
smg_comms_actions... 795
smg_comms_actions... 796 -- pad to the end of the message
smg_comms_actions... 797 if Pos <= Msg'Last then
smg_comms_actions... 798 if Padding = RNG_PAD then
smg_comms_actions... 799 RNG.Get_Octets( Msg( Pos..Msg'Last ) );
smg_comms_actions... 800 else
smg_comms_actions... 801 -- repeat the Padding value itself
smg_comms_actions... 802 for I in Pos..Msg'Last loop
smg_comms_actions... 803 Msg(I) := Padding( Padding'First + (I - Pos) mod Padding'Length );
smg_comms_actions... 804 end loop;
smg_comms_actions... 805 end if;
smg_comms_actions... 806 -- either rng or fixed, update Pos though
smg_comms_actions... 807 Pos := Msg'Last + 1;
smg_comms_actions... 808 end if;
smg_comms_actions... 809 end Write_End;
smg_comms_actions... 810
smg_comms_skeys_s... 811 end Messages;