-- Serpent - Provide an ideal interface to the Serpent cipher. -- Copyright (C) 2023 Prince Trippy . -- This program is free software: you can redistribute it and/or modify it under the terms of the -- GNU Affero General Public License version 3 as published by the Free Software Foundation. -- This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without -- even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- See the GNU Affero General Public License for more details. -- You should have received a copy of the GNU Affero General Public License along with this program. -- If not, see . with Interfaces; package Serpent is pragma Pure(Serpent); -- I was strongly tempted to provide for an Octet type and according variants of the subprograms. -- Serpent however lacks some qualities of SHA that make supporting multiple such units sensible. -- Namely, Serpent always operates on four thirty-two bit words, and not a variable-length array. -- My SHA implementations allow for arrays of bits, octets, or words because of my Pad procedure. -- That final hash checksum can change based on the unit, but no such thing happens with Serpent. -- That, alongside alignment concerns, mean I'm not at all concerned with supporting octets here. -- Supporting them would ruin my beautiful naming scheme, also, which is more than reason enough. subtype Word is Interfaces.Unsigned_32; type Word_Block is array (Positive range 1 .. 4) of Word; pragma Pack(Word_Block); -- I want to support the more complex encryption and decryption operations, which requires these. -- With Ada 2012 this should be a flat array using a Static_Predicate of, say: A'Length mod 4 = 0 -- However, using Ada 2012 for that one feature is unacceptable, and so this method is preferred. type Word_Array is array (Positive range <>) of Word_Block; pragma Pack(Word_Array); -- This is a nice little idea I think, to make it more difficult to accidentally mix up the data. type Encrypted_Block is new Word_Block; type Decrypted_Block is new Word_Block; type Encrypted_Array is new Word_Array; type Decrypted_Array is new Word_Array; -- I recall not if these following pragmas be necessary, or not, and include them out of caution. pragma Pack(Encrypted_Block); pragma Pack(Decrypted_Block); pragma Pack(Encrypted_Array); pragma Pack(Decrypted_Array); -- The names here are taken from that Serpent cipher paper, but at least Key can name parameters. -- Those who must support key lengths which aren't multiples of this word size are by themselves. subtype Key_Length is Positive range 1 .. 8; -- As is the usual, I recommend not using this type. type User_Key is array (Key_Length range <>) of Word; subtype Full_Key is User_Key(Key_Length'Range); type Sub_Keys is private; function Pad (Key : in User_Key) return Full_Key; function Schedule (Key : in Full_Key) return Sub_Keys; -- This pair is the most basic support for Serpent and also will be made the most efficient pair. procedure Encrypt (Key : in Sub_Keys; Message : in out Word_Block); procedure Decrypt (Key : in Sub_Keys; Message : in out Word_Block); -- These pairs support the optional types and this pair should be the second most efficient pair. procedure Encrypt (Key : in Sub_Keys; Decrypted : in Decrypted_Block; Encrypted : out Encrypted_Block); procedure Decrypt (Key : in Sub_Keys; Encrypted : in Encrypted_Block; Decrypted : out Decrypted_Block); -- Use of function alone should make the types clear where these are used, alongside conversions. function Encrypt (Key : in Sub_Keys; Message : in Decrypted_Block) return Encrypted_Block; function Decrypt (Key : in Sub_Keys; Message : in Encrypted_Block) return Decrypted_Block; -- This pair is provided for sake of completeness and shouldn't cause ambiguities with the other. -- Still, I may remove this pair if I later see otherwise. It may or may not be reasonable here. function Encrypt (Key : in Sub_Keys; Message : in Word_Block) return Word_Block; function Decrypt (Key : in Sub_Keys; Message : in Word_Block) return Word_Block; -- The following six subprograms implement the ``cipher block chaining'' (CBC) mode of operation. -- The most obvious way to implement this pair is as procedures using the in out parameter modes. procedure Encrypt (Key : in Sub_Keys; Message : in out Word_Array; Initial_Mask : in Word_Block := (others => 0)); procedure Decrypt (Key : in Sub_Keys; Message : in out Word_Array; Initial_Mask : in Word_Block := (others => 0)); -- A pair of functions here isn't poor, so long as he who uses them understands the consequences. function Encrypt (Key : in Sub_Keys; Message : in Word_Array; Initial_Mask : in Word_Block := (others => 0)) return Word_Array; function Decrypt (Key : in Sub_Keys; Message : in Word_Array; Initial_Mask : in Word_Block := (others => 0)) return Word_Array; -- Lastly, an in out parameter mode makes no sense for these types and a procedure invites flaws. function Encrypt (Key : in Sub_Keys; Message : in Decrypted_Array; Initial_Mask : in Word_Block := (others => 0)) return Encrypted_Array; function Decrypt (Key : in Sub_Keys; Message : in Encrypted_Array; Initial_Mask : in Word_Block := (others => 0)) return Decrypted_Array; private type S_Box is mod 8; type Key_Schedule is new Integer range -8 .. 131; -- How unfortunate I couldn't make this a Word. type Pre_Keys is array (Key_Schedule) of Word; type Sub_Keys is array (Natural range 0 .. 32) of Word_Block; -- I should make this a Word_Array. Phi : constant Word := 16#9E37_79B9#; end Serpent; .