Hello!

Inspiré(e) de prendre part à la discussion ? Ou de poser une question ou demander de l’aide ?

Alors bienvenues dans les grands sujets des forums de La Bulle : m’inscrire.

Cette partie du forum n’est pas compatible avec les bloqueurs publicitaires

Félicitations à vous, de préférer les accès payants plutôt que la gratuité par la publicité, c’est honnorable et cohérent de votre part. Malheureusement, l’accès payant par micropaiement (qui serait d’environ 1 cent pour 20 pages consultées) n’est pour l’instant pas encore mis en place, et l’accès gratuit sans publicité, est réservé aux membres actif(ve)s du forum. En attendant, si vous souhaitez poursuivre votre visite chez nous, vous pouvez ajouter le site à votre liste blanche, ou encore mieux, désactiver le bloqueur partout. Pour ajouter le site à votre liste blanche, pour Firefox (similaire pour les autres navigateurs), rendez‑vous en bas à gauche de la fenêtre de votre navigateur, et cliquez sur le menu comme dans l’exemple de l’image ci‑dessous, puis rechargez la page, en appuyant sur F5.

Hachage SHA‑1 en Ada
Auteur Message
Administrateur
Avatar de l’utilisateur
  • Genre : Télétubbie
  • Messages : 22197
Ven 26 Juil 2013 05:18
Message Hachage SHA‑1 en Ada
De même qu’avec le sujet Décodeur/encodeur Unicode en Ada (qu’il faut que je termine prochainement), ce sujet est verrouillé en attendant qu’il soit achevé au sens de la documentation qu’il nécessite.

C’est une implémentation en Ada, de la fonction de hachage SHA‑1, tel que spécifiée par la norme fips-180-4.pdf, éditée par le NIST (National Institute of Standards and Technology).

C’est une implémentation volontairement la plus simple possible (mais pas naïve et encore moins bâclée !). Je l’ai conçu de manière à en faire une sorte d’implémentation de référence, à partir de laquelle d’autres variation pourront être dérivée.

Je poste les sources du paquet Ada dans le message suivant, et compléterai plus tard avec le source d’un programme de validation de cette implémentation et par une discussion sur les choix de design fait pendant la conception de ce paquet. Plus tard également, je posterai un lien vers une archive en téléchargement pour ces sources, les fichiers de validation, et la documentation qui accompagnera le tout.

Image
Hibou57

« La perversion de la cité commence par la fraude des mots » [Platon]
Profil Site Internet
Administrateur
Avatar de l’utilisateur
  • Genre : Télétubbie
  • Messages : 22197
Ven 26 Juil 2013 05:23
Message Re: Hachage SHA‑1 en Ada

Source de la spécification du paquet SHA1


Source Ada : 

with System; -- For generic constraints

package SHA1 is

pragma Pure;

-- Types common to all item instances
-- ========================================================================

type State_Type is
(Undefined,
Initialized, -- Reached by invokation of `Initialize`.
Running, -- Pending to recieve data until invokation of `Finalize`.
Valid, -- Hash available.
Invalid); -- No hash available, because message was too long.

subtype Digit_Type is Character range '0' .. 'f';

-- Or more precisely, and if your Ada compiler does not complains:
--
-- subtype Digit_Type is Character range '0' .. 'f'
-- with Static_Predicate =>
-- Digit_Type in -- [3.1]
-- '0' .. '9' |
-- 'a' .. 'f';

subtype Text_Index_Type is Positive range 1 .. 40;

subtype Text_Type is String (Text_Index_Type)
with Static_Predicate =>
(for all I in Text_Type'Range =>
Text_Type (I) in Digit_Type);

-- Items
-- ========================================================================

generic
-- Parameterless
package Items is

-- Methods indepent from hashed item types
-- ---------------------------------------------------------------------

procedure Finalize
with
Pre =>
State in Initialized | Running,
Post =>
(if not Overflowed
then State = Valid
else State = Invalid);

procedure Initialize
with Post =>
(State = Initialized) and
(not Overflowed);

function Overflowed return Boolean -- Message is too long
with Pre => State in Initialized | Running | Valid | Invalid;

function State return State_Type;

-- Incrementally hashing elements buffers
-- ---------------------------------------------------------------------

Bit_Base : constant := 2;

generic

type Bit_Count_Type is range <>
type Element_Type is (<>);
type Index_Type is range <>
type Array_Type is array (Index_Type range <>) of Element_Type;

package Streamers is

-- Make formal parameters visible for clients.
subtype Formal_Bit_Count_Type is Bit_Count_Type;
subtype Formal_Element_Type is Element_Type;
subtype Formal_Index_Type is Index_Type;
subtype Formal_Array_Type is Array_Type;

pragma Assert (0 <= Bit_Count_Type'First);
pragma Assert (Bit_Count_Type'First <= System.Word_Size);

pragma Assert
((Element_Type'Pos (Element_Type'Last) -
Element_Type'Pos (Element_Type'First) + 1) =
(Bit_Base ** Bit_Count_Type'Pos (Bit_Count_Type'Last)));

use type Index_Type;

procedure Append
(Item : Element_Type;
Length : Bit_Count_Type := Bit_Count_Type'Last)
with
Pre =>
State in Initialized | Running,
Post =>
(if 0 < Length
then State = Running
else State = State'Old) and
(if Overflowed'Old
then Overflowed);
-- Append a sub‑string of bits of length `Length`.
-- Append the `Length` lower bits of `Item` starting with the
-- highest weighted bit of these lower bits, down to the lowest.

procedure Append
(Item : Array_Type;
First : Index_Type;
Last : Index_Type)
with
Pre =>
State in Initialized | Running and
Item'First <= First and
Last <= Item'Last,
Post =>
(if First <= Last
then State = Running
else State = State'Old) and
(if Overflowed'Old then Overflowed);

end Streamers;

-- Getting hashes as text
-- ---------------------------------------------------------------------

function Text return Text_Type
with
Pre => (State = Valid),
Post =>
(State = Valid) and
(Text'Result = Text'Result);
-- Avoid compiler warning about Post not mentionning Result.

procedure Write (Target : out Text_Type)
with
Pre => (State = Valid),
Post => (State = Valid);

end Items;

end SHA1;


Source du corps du paquet SHA1 (une implémentation de référence)


Source Ada : 

pragma Assertion_Policy (Check);

package body SHA1 is

-- Physical constraints
-- ========================================================================

package Constraints is

-- “Physical” constraints expressed as numbers

Bit_Base : constant := 2; -- [2.1]
Text_Base : constant := 16;
Nibble_Bit_Count : constant := 4; -- [3.1]
Word_Bit_Count : constant := 32; -- [Figure 1], [2.1], [3.1]
Length_Bit_Count : constant := 64; -- [Figure 1]
Hash_Bit_Count : constant := 160; -- [Figure 1]
Block_Bit_Count : constant := 512; -- [Figure 1]
Shedule_Element_Count : constant := 80;

end Constraints;

-- Bits
-- ========================================================================

package Bits is

-- A modular type.

Base : constant := Constraints.Bit_Base;
Width : constant := 1;

type Instance is mod Base ** Width;

end Bits;

-- Nibbles
-- ========================================================================

package Nibbles is

-- A modular type.
-- Group of four bits; half of a byte.

Base : constant := Constraints.Bit_Base;
Width : constant := Constraints.Nibble_Bit_Count;

type Instance is mod Base ** Width; -- [3.1]

end Nibbles;

-- Words
-- ========================================================================

package Words is

-- A modular type.

Base : constant := Constraints.Bit_Base;
Width : constant := Constraints.Word_Bit_Count;

type Instance is mod Base ** Width; -- [3.1]
type Bit_Count_Type is range 0 .. Width;
type Offset_Type is range 0 .. Width; -- Offset in bits [2.2.2]

function Offset (Bit_Count : Bit_Count_Type) return Offset_Type
is (Offset_Type (Bit_Count));

function Shl (N : Offset_Type; X : Bits.Instance) return Instance
is (Instance (X) * (Base ** Natural (N))); -- Additional definition

function Shl (N : Offset_Type; X : Instance) return Instance
is (X * (Base ** Natural (N))); -- Additional definition

function Shr (N : Offset_Type; X : Instance) return Instance
is (X / (Base ** Natural (N))); -- [2.2.2], [3.2]

function Rot_L (N : Offset_Type; X : Instance) return Instance
is (Shl (N, X) or Shr (Width - N, X)); -- [2.2.2], [3.2]

function Rot_R (N : Offset_Type; X : Instance) return Instance
is (Rot_L (Width - N, X)); -- [2.2.2], [3.2]

end Words;

-- Widest binary type
-- ========================================================================

package Widest_Binaries is

-- A modular type.

Base : constant := Constraints.Bit_Base;
Width : constant := System.Word_Size;
-- May be tweaked and manually edited on a per-plateform basis. Choose
-- it so that `Base ** Width` is not greater than `Max_Binary_Modulus`
-- and not greater than the biggest universal integer. Compiler bugs may
-- force you to lower the usable value. A safe value, is that of the
-- natural word size on your plateform, that is, 32 on 32 bits machines
-- and 64 on 64 bits machines. A portable value is 32, as Ada's standard
-- ensures a minimum available width of 32 bits.
pragma Assert (Base ** Width <= System.Max_Binary_Modulus);
pragma Assert (Base ** Width <= System.Max_Int);

type Instance is mod Base ** Width;
type Bit_Count_Type is range 0 .. Width;
type Offset_Type is range 0 .. Width; -- Offset in bits

function Offset (Bit_Count : Bit_Count_Type) return Offset_Type
is (Offset_Type (Bit_Count));

function Shl (N : Offset_Type; X : Instance) return Instance
is (X * (Base ** Natural (N)));

function Shr (N : Offset_Type; X : Instance) return Instance
is (X / (Base ** Natural (N)));

function Rot_L (N : Offset_Type; X : Instance) return Instance
is (Shl (N, X) or Shr (Width - N, X));

function Rot_R (N : Offset_Type; X : Instance) return Instance
is (Rot_L (Width - N, X));

end Widest_Binaries;

-- Parsed message blocks
-- ========================================================================

package Message_Blocks is

-- A fixed capacity block of modular type elements.
-- After initialization, it is filled with an append method.
-- Used as the successive inputs of the incremental hash algorithm.

package Elements renames Words;

Bit_Count : constant := Constraints.Block_Bit_Count;
Element_Count : constant := Bit_Count / Elements.Width;
pragma Assert ((Bit_Count mod Elements.Width) = 0);

subtype Element_Type is Elements.Instance;
subtype Bit_Type is Bits.Instance;

type Instance is private; -- {1}
type Index_Type is range 0 .. Element_Count - 1; -- {2}
type Bit_Count_Type is range 0 .. Bit_Count;

-- 1. There cannot be a public view of an array type. It is written into
-- as an array of bits, and read from as an array of words whose
-- size is defined by the specification. Thus, it is made a private
-- type and is given a public interface with write and read
-- operation using their proper item types.
-- 2. [2.2.1], [2.2.1] and [6.1] for 0 based index

procedure Initialize (Target : out Instance)
with Post => Length (Target) = 0;

function Length (This : Instance) return Bit_Count_Type;

function Is_Full (This : Instance) return Boolean
is (Length (This) = Bit_Count_Type'Last);

function Item
(This : Instance; I : Index_Type)
return Element_Type -- {3}
with Pre => Is_Full (This);

-- 3. [3.1 4 a], [2.2.1] for 0 is left-most byte

procedure Append (Target : in out Instance; Item : Bit_Type)
with
Pre => not Is_Full (Target),
Post => (Length (Target) = Length (Target)'Old + 1);

procedure Append_Zeros
(Target : in out Instance;
Bound : Bit_Count_Type)
with
Pre => Length (Target) <= Bound,
Post => Length (Target) = Bound;

private

type Content_Type is array (Index_Type) of Element_Type;

type Instance is record
M : Content_Type; -- Parsed message part
L : Bit_Count_Type; -- Length occupied
end record;

function Length (This : Instance) return Bit_Count_Type
is (This.L);

function Item (This : Instance; I : Index_Type) return Element_Type
is (This.M (I));

end Message_Blocks;

-- Unparsed (overall) message lengths
-- ========================================================================

package Message_Lengths is

-- A scalar as the overall length of a streamed message while parsed as
-- blocks.

package Blocks renames Message_Blocks;

Bit_Count : constant := Constraints.Length_Bit_Count;

subtype Increment_Type is Blocks.Bit_Count_Type;
use type Increment_Type;

type Instance is private;
-- There cannot be a simple type suitable for a public view. It is
-- updated as a scalar type and needs to be seen as an array of bits
-- to be appended to a message block. Thus it is made a private type
-- with proper methods on proper types provided for each cases.

procedure Initialize (Target : out Instance)
with Post => not Overflowed (Target);

procedure Increment
(Target : in out Instance; Increase : Increment_Type := 1)
with Pre => not Overflowed (Target);

function Overflowed (This : Instance) return Boolean;

procedure Append
(Target : in out Blocks.Instance;
This : in Instance)
with
Pre => Blocks.Length (Target) = Blocks.Bit_Count - Bit_Count,
Post => Blocks.Is_Full (Target);
-- Appends `This` to complete `Target`.

private

-- A polynomial made of the same element type as a parsed message block
-- is; a choice among others, as this was not the only available option
-- to manipulate a minimum of 64 bits width scalar on 32 bits machines.
-- This will work as well on 64 bits machines.
-- After initialization, it is updated with an increment method.

package Elements renames Blocks.Elements;

subtype Element_Type is Elements.Instance;

type Index_Type is range 0 .. (Bit_Count / Elements.Width) - 1;
pragma Assert ((Bit_Count mod Elements.Width) = 0);

type Polynomial_Type is array (Index_Type) of Element_Type;

type Instance is record
P : Polynomial_Type;
Overflowed : Boolean;
end record;

function Overflowed (This : Instance) return Boolean
is (This.Overflowed);

end Message_Lengths;

-- Message hashes
-- ========================================================================

package Hashes is

-- An array of modular type elements.
-- After initialization, it is updated with an external algorithm.
-- Used as the incrementally computed hash of an overall message flow.

package Elements renames Words;

Bit_Count : constant := Constraints.Hash_Bit_Count;
Width : constant := Bit_Count / Elements.Width;
pragma Assert ((Bit_Count mod Elements.Width) = 0);

subtype Element_Type is Elements.Instance;

use type Element_Type;

type Index_Type is range 0 .. Width - 1; -- {1}
type Instance is array (Index_Type) of Element_Type; -- {2}
-- Need not be a private type, and can be exposed as an array,
-- as this is an array per specification and is seen as an array with
-- regards to all operations.

subtype Registers_Type is Instance; -- {3}

-- 1. [2.2.1], [2.2.1] and [5.3.1] for 0 based index
-- 2. [2.2.1], [5.3.1], [2.2.1] for 0 is left-most element
-- 3. [2.2.1], [6.1], [6.1.2]

Initial_Value : constant Instance := -- [5.3.1]
(0 => 16#67452301#,
1 => 16#efcdab89#,
2 => 16#98badcfe#,
3 => 16#10325476#,
4 => 16#c3d2e1f0#);

procedure Initialize (Target : out Instance)
with Post =>
(for all I in Target'Range =>
Target (I) = Initial_Value (I));

-- Algorithm's internal message shedules
-- ---------------------------------------------------------------------

package Message_Shedules is

-- An array of elements of the same type as that of an hash.
-- Used in the hash algorithm (does not persist outside of it).

package Elements renames Hashes.Elements;

Element_Count : constant := Constraints.Shedule_Element_Count;

subtype Element_Type is Elements.Instance;
-- Need not be a private type, and can be exposed as an array,
-- as this is an array per specification and is seen as an array with
-- regards to all operations.

type Index_Type is range 0 .. Element_Count - 1;
type Instance is array (Index_Type) of Element_Type; -- {1}

-- 1. [6.1], [6.1.2]

-- Partition of `Index_Type` range.
subtype Range_1_Type is Index_Type range 0 .. 19;
subtype Range_2_Type is Index_Type range 20 .. 39;
subtype Range_3_Type is Index_Type range 40 .. 59;
subtype Range_4_Type is Index_Type range 60 .. 79;

K_Of_Range_1 : constant Element_Type := 16#5a827999#; -- [4.2.1]
K_Of_Range_2 : constant Element_Type := 16#6ed9eba1#; -- [4.2.1]
K_Of_Range_3 : constant Element_Type := 16#8f1bbcdc#; -- [4.2.1]
K_Of_Range_4 : constant Element_Type := 16#ca62c1d6#; -- [4.2.1]

function K (T : Index_Type) return Element_Type -- [2.2.1], [4.2.1]
is
(case T is
when Range_1_Type => K_Of_Range_1,
when Range_2_Type => K_Of_Range_2,
when Range_3_Type => K_Of_Range_3,
when Range_4_Type => K_Of_Range_4);

end Message_Shedules;

-- Algorithm's functions and incremental method
-- ---------------------------------------------------------------------

package Algorithm is

-- The hash algorithm.
-- Does not defines any type, only a number of functions and a
-- single procedure (an update procedure).

package Blocks renames Message_Blocks;
package Shedules renames Message_Shedules;

subtype Block_Type is Blocks.Instance;
subtype Index_Type is Shedules.Index_Type;
subtype Argument_Type is Hashes.Element_Type;
subtype Result_Type is Hashes.Instance;

use type Argument_Type;

function Ch (X, Y, Z : Argument_Type) return Argument_Type
is ((X and Y) xor ((not X) and Z)); -- [4.1.1]

function Parity (X, Y, Z : Argument_Type) return Argument_Type
is (X xor Y xor Z); -- [4.1.1]

function Maj (X, Y, Z : Argument_Type) return Argument_Type
is ((X and Y) xor (X and Z) xor (Y and Z)); -- [4.1.1]

function F -- [4.1.1]
(T : Index_Type; X, Y, Z : Argument_Type)
return Argument_Type
is
(case T is
when Shedules.Range_1_Type => Ch (X, Y, Z),
when Shedules.Range_2_Type => Parity (X, Y, Z),
when Shedules.Range_3_Type => Maj (X, Y, Z),
when Shedules.Range_4_Type => Parity (X, Y, Z));

procedure Hash -- [6.1.2]
(Target : in out Result_Type; Block : Block_Type)
with Pre => Blocks.Is_Full (Block);

end Algorithm;

-- Texts
-- ---------------------------------------------------------------------

package Texts is

package Base renames Nibbles; -- “Physical” base.
pragma Assert (Base.Instance'Modulus = Constraints.Text_Base);

Digit : constant array (Base.Instance) of Digit_Type :=
('0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f');
-- [3.1] Note it's lowercase, not uppercase.

procedure Write (Target : out Text_Type; Item : Instance);

end Texts;

end Hashes;

-- Parsers (turns a message stream into blocks in sequence)
-- ========================================================================

package Parsers is

-- A message block with its incrementally computed hash and length.
-- After initialization, it is updated with an append method.
-- The hash may be retrieved after finalization.

package Blocks renames Message_Blocks;
package Lengths renames Message_Lengths;

subtype State_Type is SHA1.State_Type;

type Instance is private;

function State (This : Instance) return State_Type;

function Overflowed (This : Instance) return Boolean;

function Result (This : Instance) return Hashes.Instance
with Pre => State (This) = Valid;

procedure Initialize (Target : out Instance)
with Post => State (Target) = Initialized;

procedure Append (Target : in out Instance; Item : Bits.Instance)
with
Pre => State (Target) in Initialized | Running,
Post => State (Target) = Running;

procedure Finalize (Target : in out Instance)
with
Pre => State (Target) in Initialized | Running,
Post => State (Target) in Valid | Invalid;

private

type Instance is record
Hash : Hashes.Instance;
Block : Message_Blocks.Instance;
Length : Lengths.Instance;
State : State_Type := Undefined;
end record;

function State (This : Instance) return State_Type
is (This.State);

function Overflowed (This : Instance) return Boolean
is (Lengths.Overflowed (This.Length));

function Result (This : Instance) return Hashes.Instance
is (This.Hash);

end Parsers;

-- Message blocks implementation
-- ========================================================================

package body Message_Blocks is

type Index_Bit_Type is range 0 .. Bit_Count - 1;

procedure Initialize (Target : out Instance)
is begin
Target.L := 0;
end;

pragma Assert
(Index_Bit_Type'Pos (Index_Bit_Type'First) <=
Bit_Count_Type'Pos (Bit_Count_Type'First)); -- H1

pragma Assert
((Bit_Count_Type'Pos (Bit_Count_Type'Last) - 1) <=
Index_Bit_Type'Pos (Index_Bit_Type'Last)); -- H2

pragma Assert (Index_Type'First <= 0); -- H3

pragma Assert
(Index_Bit_Type'Pos (Index_Bit_Type'Last / Elements.Width) <=
Index_Type'Last); -- H4

subtype Offset_Type is Elements.Offset_Type;
use type Offset_Type;

procedure Append (Target : in out Instance; Item : Bit_Type)
is
use type Element_Type;

I : Index_Bit_Type := Index_Bit_Type (Target.L); -- {1}
J : Index_Type := Index_Type (I / Elements.Width); -- {2}
K : Offset_Type :=
Elements.Offset
(Elements.Bit_Count_Type (I mod Elements.Width)); -- {3}
N : Offset_Type := Elements.Width - 1 - K; -- K from left. {4}
M : Content_Type renames Target.M;

-- 1. Valid by H1,H2. As the precondition requires
-- Target.L < Bit_Count_Type'Last, Target.L cannot be more than
-- Bit_Count_Type'Last - 1.
-- 2. Valid by H3,H4
-- 3. Valid by definition (A mod B < B)
-- 4. Valid by K < Elements.Width
begin
if K = 0 then
-- New element to be initialized.
M (J) := Elements.Shl (N => N, X => Item);
else
-- Existing element to be written a bit to.
M (J) := M (J) or Elements.Shl (N => N, X => Item);
end if;
Target.L := Target.L + 1; -- Valid by precondition
end;

procedure Append_Zeros
(Target : in out Instance;
Bound : Bit_Count_Type)
is begin
while Target.L < Bound loop
Append (Target => Target, Item => Bits.Instance (0));
end loop;
end;

end Message_Blocks;

-- Message lengths implementation
-- ========================================================================

package body Message_Lengths is

procedure Initialize (Target : out Instance)
is begin
Target.P := (others => 0);
Target.Overflowed := False;
end;

pragma Assert
(Element_Type'Pos (Element_Type'First) <=
Increment_Type'Pos (Increment_Type'First)); -- H1

pragma Assert
(Increment_Type'Pos (Increment_Type'Last) <=
Element_Type'Pos (Element_Type'Last)); -- H2

procedure Increment
(Target : in out Instance; Increase : Increment_Type := 1)
is
use type Element_Type;
C : Element_Type; -- Carry
Y : Element_Type := Element_Type (Increase); -- Valid by H1,H2
P : Polynomial_Type renames Target.P;
begin
for I in reverse P'Range loop
-- Reverse: remember last index is left-most, thus lower element.
C := (if (Y <= Element_Type'Last - P (I)) then 0 else 1);
P (I) := P (I) + Y;
exit when C = 0;
Y := C;
end loop;
if C /= 0 then
Target.Overflowed := True;
end if;
end;

procedure Append
(Target : in out Blocks.Instance;
This : in Instance)
is begin
for I in Index_Type loop
declare
use type Blocks.Element_Type;
Item : Blocks.Element_Type :=
Blocks.Elements.Rot_R
(N => Blocks.Elements.Width,
X => This.P (I));
begin
for J in 1 .. Blocks.Elements.Width loop
Item := Blocks.Elements.Rot_L (N => 1, X => Item);
Blocks.Append
(Target => Target,
Item => Bits.Instance (Item and 1));
end loop;
end;
end loop;
end;

end Message_Lengths;

-- Message hashes implementation
-- ========================================================================

package body Hashes is

procedure Initialize (Target : out Instance)
is begin
Target := Initial_Value;
end;

-- Algorithm implementation
-- ---------------------------------------------------------------------

package body Algorithm is

use type Blocks.Index_Type;

pragma Assert (Blocks.Index_Type'First <= 0); -- H1
pragma Assert (15 <= Blocks.Index_Type'Last); -- H2

procedure Hash (Target : in out Result_Type; Block : Block_Type)
is
use type Shedules.Index_Type;
function M (I : Blocks.Index_Type) return Blocks.Element_Type
is (Blocks.Item (This => Block, I => I));
W : Shedules.Instance; -- [6.1]
V : Hashes.Registers_Type; -- {1}
-- 1. I-th working variables [2.2.1], [6.1]
begin
-- The outermost for-loop is omitted, as this outer iteration is
-- handled by the parser feeding the current message block `M`,
-- and which invoks this method.

W := (others => 0); -- Not required, but avoid a warning.

Step_1: for T in W'Range loop

W (T) :=
(case T is
when 0 .. 15 =>
M (Blocks.Index_Type (T)), -- Valid by H1,H2
when 16 .. 79 =>
Hashes.Elements.Rot_L
(N => 1,
X =>
W (T - 3) xor
W (T - 8) xor
W (T - 14) xor
W (T - 16)));

end loop Step_1;

-- Assertions before iterations:
-- M(0..15) valid (was assigned a value) {1}.
--
-- During each iteration:
-- T in 16..79 => T - 16..3 valid indexes in W {2}.
--
-- At the end of each iteration:
-- T in 0..15 and M(T) is valid => W(0..T) valid {3}.
-- (15 <= T) => W(0..15) valid.
-- T = 16 and W(0..15) valid => W(0..15) and W(T) valid {4}.
-- T = 16 and W(0..15) valid => W(0..16) valid {5}.
-- T+0 = 16+0 and W(0..15+0) valid => W(0..15+0+1) valid {5}.
-- T+N = 16+N and W(0..15+N) valid => W(0..15+N+1) valid {6}.
-- … which allows to prove it for all T in 16 .. 78.
--
-- Conclusion:
-- No out of bounds index and all W items are assigned valid
-- values. For this reason, initialization of W prior to the loop
-- is not needed (but some compilers may not guess it).
--
-- 1. If the caller is not wrong, or else, there are garbages in M
-- and the hash is not meaningful. The precondition tries to
-- ensure Input (and thus M) was fully filled up.
-- 2. No index out of bounds during iterations.
-- 3. By copy from M(T) which is known (or supposed) to be valid.
-- 4. W(T) is computed from values which are known to be valid and
-- so is W(T).
-- 5. Rewording of the same.
-- 6. Inductive transition.

Step_2: begin

V := Target;

end Step_2;

Step_3: declare

Temp : Hashes.Element_Type; -- {1}
A : Hashes.Element_Type renames V (0);
B : Hashes.Element_Type renames V (1);
C : Hashes.Element_Type renames V (2);
D : Hashes.Element_Type renames V (3);
E : Hashes.Element_Type renames V (4);

-- 1. Temporary w-bit element [2.2.1], [6.1]
begin

for T in Message_Shedules.Index_Type loop

Temp :=
(Hashes.Elements.Rot_L (N => 5, X => A) +
F (T => T, X => B, Y => C, Z => D) +
E + Shedules.K (T) + W (T));

E := D;
D := C;
C := Hashes.Elements.Rot_L (N => 30, X => B);
B := A;
A := Temp;

end loop;

end Step_3;

Step_4: begin

-- New value is written in place of the old one, because the
-- old one is not referred to any more passed this point and
-- the new value is the future old value.

for I in Target'Range loop
Target (I) := Target (I) + V (I);
end loop;

end Step_4;

end Hash;

end Algorithm;

-- Texts implementation
-- ---------------------------------------------------------------------

package body Texts is

pragma Assert ((Elements.Width mod Base.Width) = 0); -- H1

pragma Assert
(((Text_Type'Last - Text_Type'First + 1) mod
(Elements.Width / Base.Width)) = 0); -- H2

pragma Assert
(((Index_Type'Pos (Instance'Last) -
Index_Type'Pos (Instance'First) + 1) *
(Elements.Width / Base.Width)) =
(Text_Type'Last - Text_Type'First + 1)); -- H3

procedure Write (Target : out Text_Type; Item : Instance)
is
subtype Base_Type is Base.Instance;
W : constant := Elements.Width / Base.Width; -- Valid by H1
M : constant := Base_Type'Modulus;
S : Text_Type renames Target;
P : Instance renames Item;
C : Element_Type;
K : Text_Index_Type := S'First;
begin
for I in P'Range loop -- Valid by H3
C := P (I);
declare
N : Base_Type;
begin
K := K + (W - 1); -- Valid by H2,H3
for J in 1 .. W loop -- Valid by H2
N := Base_Type (C mod M); -- Valid as M is type's modulus
C := C / M;
S (K) := Digit (N);
if not (J = W) then
K := K - 1;
end if;
end loop;
if not (I = P'Last) then
K := K + W; -- Valid by H2,H3
end if;
end;
end loop;
end Write;

end Texts;

end Hashes;

-- Message parsers implementation
-- ========================================================================

package body Parsers is

package Algorithm renames Hashes.Algorithm;

-- State and resources management
-- ---------------------------------------------------------------------

function Block_Length (This : Instance) return Blocks.Bit_Count_Type
is (Blocks.Length (This.Block));

procedure Next_Block (Target : in out Instance)
with Pre => Blocks.Is_Full (Target.Block);

procedure Last_Block (Target : in out Instance)
with Pre => Blocks.Is_Full (Target.Block);

procedure Release_Block (Target : in out Instance);

procedure Initialize (Target : out Instance)
is begin
Hashes.Initialize (Target.Hash);
Lengths.Initialize (Target.Length);
Blocks.Initialize (Target.Block);
Target.State := Initialized;
end;

procedure Next_Block (Target : in out Instance)
is begin
Algorithm.Hash (Target => Target.Hash, Block => Target.Block);
Blocks.Initialize (Target.Block);
end;

procedure Last_Block (Target : in out Instance)
is begin
Algorithm.Hash (Target => Target.Hash, Block => Target.Block);
Release_Block (Target);
end;

procedure Release_Block (Target : in out Instance)
is begin
-- Placeholder for potential future implementation variations.
null;
end;

-- Message parsing methods
-- ---------------------------------------------------------------------

use type Lengths.Instance;
use type Hashes.Instance;
use type Blocks.Bit_Count_Type;

procedure Append_Message_Bit
(Target : in out Instance;
Item : Bits.Instance)
with Post =>
(if Blocks.Is_Full (Target.Block'Old) and not Overflowed (Target)
then
Target.Hash /= Target.Hash'Old and -- {1}
Block_Length (Target) = 1 -- {2}
else Target.Hash = Target.Hash'Old);

-- 1. This means the block was hashed.
-- 2. This means a new block was started and the data appended to it.

procedure Append (Target : in out Instance; Item : Bits.Instance)
renames Append_Message_Bit;

use type Blocks.Bit_Count_Type;
use type Blocks.Index_Type;

procedure Append_Message_Bit
(Target : in out Instance;
Item : Bits.Instance)
is begin
-- When the current block appears to be full, we will hash this
-- current block and start a new one.

Target.State := Running;

if Lengths.Overflowed (Target.Length) then
return;
end if;

if Blocks.Is_Full (Target.Block) then
Next_Block (Target);
end if;

Blocks.Append (Target => Target.Block, Item => Item);

Lengths.Increment (Target => Target.Length, Increase => 1);
end;

-- Last block(s) finalization methods
-- ---------------------------------------------------------------------

procedure Pad_With_One (Target : in out Instance)
with
Pre => not Blocks.Is_Full (Target.Block),
Post => Block_Length (Target) = Block_Length (Target)'Old + 1;

procedure Pad_Width_Zeros
(Target : in out Instance;
Bound : Blocks.Bit_Count_Type)
with
Pre => Block_Length (Target) <= Bound,
Post => Block_Length (Target) = Bound;

procedure Pad_With_Length (Target : in out Instance)
with
Pre =>
Block_Length (Target) =
(Blocks.Bit_Count - Lengths.Bit_Count),
Post =>
Blocks.Is_Full (Target.Block);

procedure Pad_With_One (Target : in out Instance)
is begin
Blocks.Append (Target => Target.Block, Item => Bits.Instance (1));
end;

procedure Pad_Width_Zeros
(Target : in out Instance;
Bound : Blocks.Bit_Count_Type)
is begin
Blocks.Append_Zeros (Target => Target.Block, Bound => Bound);
end;

procedure Pad_With_Length (Target : in out Instance)
is begin
Lengths.Append (Target => Target.Block, This => Target.Length);
end;

pragma Assert (Lengths.Bit_Count < Blocks.Bit_Count); -- H1

procedure Finalize (Target : in out Instance) -- [5.1.1], [6.1.2]
is
Limit : constant := Blocks.Bit_Count - Lengths.Bit_Count;
-- Positive, by H1
begin

-- [5.1.1] says L + 1 + k ≡ 448 mod 512.
-- We won't even compute k, and instead add bits as necessary. This
-- will achieve the same: filling up to a length so that there
-- remains exactly the room to write the length, which will in turn
-- be the final padding to fill exactly up to the end of the block.
-- This is finally the purpose of the calculation of l, which needs
-- not be necessary calculated the way the specification suggest it.

if Lengths.Overflowed (Target.Length) then
Release_Block (Target);
Target.State := Invalid;
return;
end if;

if Blocks.Is_Full (Target.Block) then
Next_Block (Target);
end if;

Pad_With_One (Target => Target);

if Block_Length (Target) < Limit then
Pad_Width_Zeros (Target => Target, Bound => Limit);
elsif Block_Length (Target) = Limit then
null;
else -- if Target.B.L > Limit
Pad_Width_Zeros (Target => Target, Bound => Blocks.Bit_Count);
-- Block is full
Next_Block (Target);
Pad_Width_Zeros (Target => Target, Bound => Limit);
end if;

pragma Assert (Block_Length (Target) = Limit);

Pad_With_Length (Target);

pragma Assert (Blocks.Is_Full (Target.Block));

Last_Block (Target);

Target.State := Valid;
end;

end Parsers;

-- Items implementation

package body Items is

-- Single parser instance implementing the package's interface
-- ---------------------------------------------------------------------

Parser : Parsers.Instance;

function State return State_Type
is (Parsers.State (Parser));

procedure Initialize -- [5]
is begin
Parsers.Initialize (Parser);
end Initialize;

function Overflowed return Boolean
is (Parsers.Overflowed (Parser));

procedure Finalize
is begin
Parsers.Finalize (Parser);
end;

-- Static implementation for the generic Processors's `Append` methods
-- ---------------------------------------------------------------------

procedure Append
(Item : Widest_Binaries.Instance;
Length : Widest_Binaries.Bit_Count_Type)
is
package WB renames Widest_Binaries;
subtype Binary_Type is WB.Instance;
use type Binary_Type;
Binary : Binary_Type :=
(WB.Rot_R
(N => WB.Offset (Length),
X => Item));
begin
for I in 1 .. Length loop
Binary := WB.Rot_L (N => 1, X => Binary);
Parsers.Append
(Target => Parser,
Item => Bits.Instance (Binary and 1));
end loop;
end;

-- Processors implementation
-- ---------------------------------------------------------------------

package body Streamers is

package WB renames Widest_Binaries;

pragma Assert
(Bit_Count_Type'Pos (Bit_Count_Type'Last) <=
WB.Bit_Count_Type'Pos (WB.Bit_Count_Type'Last)); -- H1

pragma Assert
((Element_Type'Pos (Element_Type'Last) -
Element_Type'Pos (Element_Type'First)) <=
(WB.Instance'Pos (WB.Instance'Last))); -- H2

procedure Append
(Item : Element_Type;
Length : Bit_Count_Type := Bit_Count_Type'Last)
is
subtype Binary_Type is WB.Instance;
subtype Bit_Count_Type is WB.Bit_Count_Type;

Binary : constant Binary_Type :=
Binary_Type
(Element_Type'Pos (Item) -
Element_Type'Pos (Element_Type'First));
-- Valid by H2
begin
if 0 < Length then
Append
(Item => Binary,
Length => Bit_Count_Type (Length)); -- Valid by H1
end if;
end;

procedure Append
(Item : Array_Type;
First : Index_Type;
Last : Index_Type)
is begin
for I in First .. Last loop
Append (Item (I));
end loop;
end;

end Streamers;

-- Text methods implementation
-- ---------------------------------------------------------------------

procedure Write (Target : out Text_Type)
is begin
Hashes.Texts.Write
(Target => Target,
Item => Parsers.Result (Parser));
end;

function Text return Text_Type
is begin
return Result : Text_Type do
Write (Result);
end return;
end;

end Items;

end SHA1;


Paquet offrant des instanciations typiques directement utilisables


Source Ada : 

-- Readily provided typical instantiations of `SHA1.Processors`.

with SHA1; -- For the generic package to be instantiated.
with Ada.Streams; -- For hashed items type
with System.Storage_Elements; -- For hashed items type

generic
-- Parameterless
package SHA1_Processors is

package Item is new SHA1.Items;

-- Incrementally hashing from stream elements
-- ========================================================================

type Stream_Element_Bit_Count_Type
is range 0 .. Ada.Streams.Stream_Element'Size;

package Stream_Streamer
is new Item.Streamers
(Bit_Count_Type => Stream_Element_Bit_Count_Type,
Element_Type => Ada.Streams.Stream_Element,
Index_Type => Ada.Streams.Stream_Element_Offset,
Array_Type => Ada.Streams.Stream_Element_Array);

-- Incrementally hashing from storage (memory) elements
-- ========================================================================

type Storage_Element_Bit_Count_Type
is range 0 .. System.Storage_Elements.Storage_Element'Size;

package Storage_Streamer
is new Item.Streamers
(Bit_Count_Type => Storage_Element_Bit_Count_Type,
Element_Type => System.Storage_Elements.Storage_Element,
Index_Type => System.Storage_Elements.Storage_Offset,
Array_Type => System.Storage_Elements.Storage_Array);

-- Incrementally hashing from characters (strings)
-- ========================================================================

type Character_Bit_Count_Type
is range 0 .. Character'Size;

package Character_Streamer
is new Item.Streamers
(Bit_Count_Type => Character_Bit_Count_Type,
Element_Type => Character,
Index_Type => Positive,
Array_Type => String);

end SHA1_Processors;

Image
Hibou57

« La perversion de la cité commence par la fraude des mots » [Platon]
Profil Site Internet
Administrateur
Avatar de l’utilisateur
  • Genre : Télétubbie
  • Messages : 22197
Dim 28 Juil 2013 02:22
Message Re: Hachage SHA‑1 en Ada
Il y a deux modifications importantes en plus de quelques corrections de style (le message où les sources ont été postés tels‑quels, a été mis à jour). D’abord un paquets générique dans SHA1, a été substitué aux méthodes statiques, Append, précédemment présentes. Ensuite, un paquet a été ajouté, contenant des instanciations du paquet générique introduit, correspondantes aux fonctionnalités de ces anciennes méthodes statiques.

Il est préférable d’utiliser un générique, parce qu’il est prévisible que sans cela, le corps de SHA1 devrait être trop fréquemment modifié pour lui ajouter des méthodes applicables à des types qui ne sont pas nécessairement Storage_Element, Stream_Element ou Character, même si dans les cas typiques d’utilisation, des méthodes pour ces types de données devrait suffire.

L’implémentation du générique à tout de même une limite, mais raisonnable : il ne supporte que les types discrets dont la cardinalité est la même que celle de l’octet. Je me demande si je ne vais pas résoudre cette limitation J’en sais rien , parce qu’elle est difficilement compatible avec l’idée d’une implémentation de référence… Je vais y réfléchir Lecture studieuse .

Image
Hibou57

« La perversion de la cité commence par la fraude des mots » [Platon]
Profil Site Internet
Administrateur
Avatar de l’utilisateur
  • Genre : Télétubbie
  • Messages : 22197
Dim 28 Juil 2013 04:05
Message Re: Hachage SHA‑1 en Ada
Hibou a écrit : 
L’implémentation du générique à tout de même une limite, mais raisonnable : il ne supporte que les types discrets dont la cardinalité est la même que celle de l’octet. Je me demande si je ne vais pas résoudre cette limitation J’en sais rien , parce qu’elle est difficilement compatible avec l’idée d’une implémentation de référence… Je vais y réfléchir Lecture studieuse .

Voilà, c’est décidé, cette limitation n’existe plus. Il y a bien sûr toujours une limite, mais elle est moins arbitraire, elle dépend de la plateforme sous‑jacente, ce qui est bien plus acceptable pour une implémentation de référence.

Image
Hibou57

« La perversion de la cité commence par la fraude des mots » [Platon]
Profil Site Internet
Administrateur
Avatar de l’utilisateur
  • Genre : Télétubbie
  • Messages : 22197
Mer 31 Juil 2013 05:08
Message Re: Hachage SHA‑1 en Ada
Dans le corps du paquet SHA1, des types ont été transformés en types opaques. Il en reste encore, mais ceux restant posent des problèmes d’organisation (des choses que je voudrais laisser dans leurs partis publiques, mais se retrouveraient dans leurs parties privés… des paradoxes ou des souhaits contradictoires à résoudre), alors ce n’est pas pour tout de suite.

Le paquet SHA1 ne s’utilise plus directement. Il défini maintenant un paquet imbriqué générique et sans paramètre, nommé Items. C’est un générique utilisé comme un objet, ce que signifie que chaque instanciation de ce générique est considérée comme une instance d’un type implicite (ou plutôt, le type, est le paquet générique lui‑même). C’est un pattern important, et j’y reviendrai avec la documentation.

Le message présentant les trois sources a été mis à jour.

Comme ce n’est pas encore la version définitive, la documentation n’est pas encore pour maintenant non‑plus.

Image
Hibou57

« La perversion de la cité commence par la fraude des mots » [Platon]
Profil Site Internet
Administrateur
Avatar de l’utilisateur
  • Genre : Télétubbie
  • Messages : 22197
Jeu 15 Aoû 2013 12:14
Message Re: Hachage SHA‑1 en Ada
Hibou a écrit : 
Dans le corps du paquet SHA1, des types ont été transformés en types opaques. Il en reste encore, mais ceux restant posent des problèmes d’organisation (des choses que je voudrais laisser dans leurs partis publiques, mais se retrouveraient dans leurs parties privés… des paradoxes ou des souhaits contradictoires à résoudre), alors ce n’est pas pour tout de suite.

Je ne m’explique pas où je voyais un problème, et il me semble qu’il était évident que `SHA1.Parsers.Instance` devait être un type privé.

Il est parfois bon de prendre de la distance et de revenir sur une chose après un certain temps, pour avoir une vue plus claire, sans avoir les pensées embrouillé par tout ce à quoi on peut penser quand on a la tête dans seau; c’est sûrement un n‑ième exemple de ce phénomène.

J’ai un instant pensé à sortir `Hashes.Instance` de `SHA1.Parsers.Instance`, pensant qu’un `Parser` n’avait pas à posséder lui‑même une instance de `SHA1.Hashes.Instance`.

Mais c’est finalement naturel qu’il le possède, pour deux raisons. La première, parce qu’il n’y a aucune raison de partager l’instance, et ça ne doit même surtout pas être possible; la seconde, parce qu’une vue fonctionnelle de ce qui est ici procédural, suggère bien que `SHA1.Hashes.Instance` est le résultat d’une fonction implicite `Parsed`, et que donc cette instance et entièrement crée à l’intérieur de cette fonction implicite `Parsed`.

Le second message a été mise à jour en conséquence.

En parlant de ce message, je crois que j’aurais dut faire trois messages séparée, un pour chacun des sources, mais c’est trop tard maintenant, car il n’est pas possible de re‑insérer un message avant un autre, et faudrait les anti‑dater, et ça n’est pas possible.

Je ne suis pas encore satisfait de la forme actuelle, à cause des paramètres nommés `Target`, pour désigner les paramètres `in out`. Autant le `This` pour les paramètres `in` semble couler de source, autant `Target` fait référence à tout autre chose, et ne convient finalement pas. Je n’ai pas d’idée d’un autre nom pour l’instant (je sais seulement que le nom actuel ne convient pas et désigne mal la chose), alors je ne peux pas encore corriger ce défaut.

Je reste également encore dans le doute sur le choix de `Instance` ou `Instance_Type` pour désigner le type défini par les paquets dont le sujet central est un type. `Instance_Type` a l’avantage de suivre la convention de nommage de tous les autres types, qui est de leur donner le suffixe `_Type` , mais remplacer tous les `Instance` par `Instance_Type` ajoute beaucoup du bruit aux sources, j’en ai l’impression. Je suis en même temps conscient que le premier point pèse plus que le second qui est moins un problème, et alors il y a de bonnes chances pour que les `Instance` soient tous renommés en `Instance_Type` dans une prochaine révision.

Image
Hibou57

« La perversion de la cité commence par la fraude des mots » [Platon]
Profil Site Internet