203 lines
7.7 KiB
Ada
203 lines
7.7 KiB
Ada
-- Copyright (C) 2015 Daniel Otte <bg@nerilex.org>
|
|
--
|
|
-- This program is free software: you can redistribute it and/or modify
|
|
-- it under the terms of the GNU General Public License as published by
|
|
-- the Free Software Foundation, either version 3 of the License, or
|
|
-- (at your option) any later version.
|
|
--
|
|
-- 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 General Public License for more details.
|
|
--
|
|
-- You should have received a copy of the GNU General Public License
|
|
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
with Crypto_Types; use Crypto_Types;
|
|
use Crypto_Types.Crypto_Utils_u8;
|
|
use Crypto_Types.Crypto_Utils_u32;
|
|
use Crypto_Types.Crypto_Utils_u64;
|
|
with System; use System;
|
|
|
|
with Ada.Text_IO; use Ada.Text_IO;
|
|
|
|
package body GCM128_Spec is
|
|
|
|
function GFmul(X,Y : Block_128_Bit) return Block_128_Bit is
|
|
Mask : constant array(Bit) of u8 := (0, 2#1110_0001#);
|
|
V : array (Bit) of Block_128_Bit := ( (others => 0), Y);
|
|
Z : Block_128_Bit := (others => 0);
|
|
Temp : Bit;
|
|
begin
|
|
for i in 0 .. 127 loop
|
|
Z := Z xor V(Bit_Get(X, i, High_Order_First));
|
|
Temp := Bit_Get(V(1), 127, High_Order_First);
|
|
V(1) := Shift_be(V(1), -1);
|
|
V(1)(1) := V(1)(1) xor Mask(Temp);
|
|
end loop;
|
|
return Z;
|
|
end GFmul;
|
|
|
|
function GHash(Key : in Block_128_Bit; Data : in u8_Array; Seed : Block_128_Bit := (others => 0)) return Block_128_Bit is
|
|
A : Block_128_Bit := Seed;
|
|
Index : Integer := Data'First;
|
|
Length : Integer := Data'Length * 8;
|
|
begin
|
|
-- for each complete block
|
|
for i in 1 .. Length / Block_Size_Bits loop
|
|
A := A xor Data(Index .. Index + Block_Size_Bytes - 1);
|
|
Index := Index + Block_Size_Bytes;
|
|
Length := Length - Block_Size_Bits;
|
|
A := GFmul(A, Key);
|
|
end loop;
|
|
if Length > 0 then
|
|
A(1 .. Length / 8) := A(1 .. Length / 8) xor Data(Index .. Index + Length / 8 - 1);
|
|
if Length mod 8 /= 0 then
|
|
A(Length / 8 + 1) := A(Length / 8 + 1) xor (Data(Index + Length / 8) and not Shift_Right(u8'(16#FF#), Length mod 8));
|
|
end if;
|
|
A := GFmul(A, Key);
|
|
end if;
|
|
return A;
|
|
end;
|
|
|
|
procedure Inc_32(A : in out Block_128_Bit) is
|
|
c : u32;
|
|
begin
|
|
c := Load_be(A(13 .. 16));
|
|
c := c + 1;
|
|
Store_be(A(13 .. 16), c);
|
|
end Inc_32;
|
|
|
|
procedure Initialize(Context : out Context_T; Key : in u8_Array; IV : in u8_Array) is
|
|
begin
|
|
Cipher.Initialize(Context.Cipher_Ctx, Key);
|
|
Context.Header_Length := 0;
|
|
Context.Plaintext_Length := 0;
|
|
Context.Y := (others => 0);
|
|
Context.H := (others => 0);
|
|
Cipher.Encrypt(Context.Cipher_Ctx, Context.H);
|
|
if IV'Length = 96 / 8 then
|
|
Context.J0( 1 .. 12) := IV;
|
|
Context.J0(13 .. 16) := (0, 0, 0, 1);
|
|
else
|
|
declare
|
|
IV_Length_Block : Block_64_Bit;
|
|
begin
|
|
Context.J0 := GHash(Context.H, IV);
|
|
Store_be(IV_Length_Block, u64(IV'Length * 8));
|
|
Context.J0(9 .. 16) := Context.J0(9 .. 16) xor IV_Length_Block;
|
|
Context.J0 := GHash(Context.H, Context.J0);
|
|
end;
|
|
end if;
|
|
Context.ICB := Context.J0;
|
|
-- encrypt J0 for final XOR on tag
|
|
Cipher.Encrypt(Context.Cipher_Ctx, Context.J0);
|
|
end Initialize;
|
|
|
|
procedure Header_Next_Block(Context : in out Context_T; Header : in u8_Array) is
|
|
begin
|
|
if Header'Length mod Block_Size_Bytes /= 0 then
|
|
raise Format_Violation;
|
|
end if;
|
|
Context.Y := GHash(Context.H, Header, Seed => Context.Y);
|
|
Context.Header_Length := Context.Header_Length + Header'Length * 8;
|
|
end;
|
|
|
|
procedure Header_Last_Block(Context : in out Context_T; Header : in u8_Array) is
|
|
begin
|
|
Context.Y := GHash(Context.H, Header, Seed => Context.Y);
|
|
Context.Header_Length := Context.Header_Length + Header'Length * 8;
|
|
end;
|
|
|
|
procedure Encrypt_Next_Block(Context : in out Context_T; Block : in out u8_Array) is
|
|
Temp : Block_128_Bit;
|
|
begin
|
|
if Block'Length /= Block_Size_Bytes then
|
|
raise Format_Violation;
|
|
end if;
|
|
Inc_32(Context.ICB);
|
|
Temp := Context.ICB;
|
|
Cipher.Encrypt(Context.Cipher_Ctx, Temp);
|
|
Block := Block xor Temp;
|
|
Context.Y := GHash(Context.H, Block, Seed => Context.Y);
|
|
Context.Plaintext_Length := Context.Plaintext_Length + u64(Block_Size_Bits);
|
|
end;
|
|
|
|
procedure Encrypt_Last_Block(Context : in out Context_T; Block : in out u8_Array) is
|
|
Length : Integer := Block'Length * 8;
|
|
Index : Integer := Block'First;
|
|
Temp : Block_128_Bit := (others => 0);
|
|
Fin_Block : Block_128_Bit;
|
|
begin
|
|
while Length >= Block_Size_Bits loop
|
|
Encrypt_Next_Block(Context, Block(Index .. Index + Block_Size_Bytes - 1));
|
|
Length := Length - Block_Size_Bits;
|
|
Index := Index + Block_Size_Bytes;
|
|
end loop;
|
|
if Length > 0 then
|
|
Inc_32(Context.ICB);
|
|
Temp := Context.ICB;
|
|
Cipher.Encrypt(Context.Cipher_Ctx, Temp);
|
|
Block(Index .. Index + (Length + 7) / 8 - 1) := Block(Index .. Index + (Length + 7) / 8 - 1) xor Temp(1 .. 1 + (Length + 7) / 8 - 1);
|
|
Context.Y := GHash(Context.H, Block(Index .. Index + (Length + 7) / 8 - 1), Seed => Context.Y);
|
|
Context.Plaintext_Length := Context.Plaintext_Length + u64(Length);
|
|
Put_Line("DBG: Trailing Length: " & Integer'Image(Length));
|
|
end if;
|
|
Store_be(Fin_Block(1 .. 8), Context.Header_Length);
|
|
Store_be(Fin_Block(9 .. 16), Context.Plaintext_Length);
|
|
Context.Y := GHash(Context.H, Fin_Block, Seed => Context.Y);
|
|
end;
|
|
|
|
procedure Decrypt_Next_Block(Context : in out Context_T; Block : in out u8_Array) is
|
|
Temp : Block_128_Bit;
|
|
begin
|
|
if Block'Length /= Block_Size_Bytes then
|
|
raise Format_Violation;
|
|
end if;
|
|
Context.Y := GHash(Context.H, Block, Seed => Context.Y);
|
|
Context.Plaintext_Length := Context.Plaintext_Length + u64(Block_Size_Bits);
|
|
Inc_32(Context.ICB);
|
|
Temp := Context.ICB;
|
|
Cipher.Encrypt(Context.Cipher_Ctx, Temp);
|
|
Block := Block xor Temp;
|
|
end;
|
|
|
|
procedure Decrypt_Last_Block(Context : in out Context_T; Block : in out u8_Array) is
|
|
Length : Integer := Block'Length * 8;
|
|
Index : Integer := Block'First;
|
|
Temp : Block_128_Bit := (others => 0);
|
|
Fin_Block : Block_128_Bit;
|
|
begin
|
|
while Length >= Block_Size_Bits loop
|
|
Decrypt_Next_Block(Context, Block(Index .. Index + Block_Size_Bytes - 1));
|
|
Length := Length - Block_Size_Bits;
|
|
Index := Index + Block_Size_Bytes;
|
|
end loop;
|
|
if Length > 0 then
|
|
Context.Y := GHash(Context.H, Block(Index .. Index + (Length + 7) / 8 - 1), Seed => Context.Y);
|
|
Context.Plaintext_Length := Context.Plaintext_Length + u64(Length);
|
|
Inc_32(Context.ICB);
|
|
Temp := Context.ICB;
|
|
Cipher.Encrypt(Context.Cipher_Ctx, Temp);
|
|
Block(Index .. Index + (Length + 7) / 8 - 1) := Block(Index .. Index + (Length + 7) / 8 - 1) xor Temp(1 .. 1 + (Length + 7) / 8 - 1);
|
|
end if;
|
|
Store_be(Fin_Block(1 .. 8), Context.Header_Length);
|
|
Store_be(Fin_Block(9 .. 16), Context.Plaintext_Length);
|
|
Context.Y := GHash(Context.H, Fin_Block, Seed => Context.Y);
|
|
end;
|
|
|
|
procedure Get_Tag(Context : in Context_T; Tag : out u8_Array) is
|
|
begin
|
|
Tag := Context.J0(1 .. Tag'Length) xor Context.Y(1 .. Tag'Length);
|
|
end Get_Tag;
|
|
|
|
function Is_Valid(Context : in Context_T; Tag : in u8_Array) return Boolean is
|
|
Is_Tag : u8_Array(Tag'Range);
|
|
begin
|
|
Get_Tag(Context, Is_Tag);
|
|
return Is_Tag = Tag;
|
|
end;
|
|
|
|
|
|
end GCM128_Spec;
|