BSOne.SFC/Tocsg.Lib/VCL/EncLib/AES/aes_gcm.pas

1018 lines
35 KiB
Plaintext

unit AES_GCM;
(*************************************************************************
DESCRIPTION : AES GCM mode functions
REQUIREMENTS : TP5-7, D1-D7/D9-D12/D17-D18/D25S, FPC, VP, WDOSX
EXTERNAL DATA : ---
MEMORY USAGE : ---
DISPLAY MODE : ---
WARNING : GCM mode (as all CTR modes) demands that the same key / IV pair
is never reused for encryption.
REFERENCES : [1] D. McGrew, J. Viega, The Galois/Counter Mode of Operation (GCM)
http://www.csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-revised-spec.pdf
[2] B. Gladman, source code archives aes-modes-src-23-07-09.zip and aes-modes-vs2008-07-10-08.zip
http://gladman.plushost.co.uk/oldsite/AES/index.php, source code archives
[3] M. Dworkin, Recommendation for Block Cipher Modes of Operation: Galois/Counter Mode (GCM) and GMAC
http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf
REMARKS : This implementation uses a GCM version with 4KB table.
[A table-less version can be generated if gf_t4k is removed
and gf_mul_h simply uses gf_mul(a, ctx.ghash_h).
Using a 256 byte table is slower than a table-less version,
a 64KB table version is incompatible with 16-bit code.]
See [3] for recommendations on IV and tag length
Version Date Author Modification
------- -------- ------- ------------------------------------------
0.10 20.09.10 W.Ehrhardt Initial version: mul_x
0.11 20.09.10 we Make4K_Table
0.12 20.09.10 we AES_GCM_Init
0.13 20.09.10 we gf_mul_h
0.14 20.09.10 we AES_GCM_Reset_IV
0.15 21.09.10 we basic gf_mul
0.16 21.09.10 we AES_GCM_Final
0.17 21.09.10 we AES_GCM_Encrypt
0.18 21.09.10 we AES_GCM_Add_AAD
0.19 21.09.10 we Fix quirk with byte-wise encryption
0.20 21.09.10 we Fix quirk with IV_len=16
0.21 22.09.10 we AES_GCM_Decrypt
0.22 22.09.10 we $ifdef GCM4KTab, improved gf_mul, mul_x
0.23 22.09.10 we $ifdef inline_xorblock in gf_mul
0.24 23.09.10 we word IV_Len, more argument checks
0.25 23.09.10 we AES_GCM_Enc_Auth, AES_GCM_Dec_Veri
0.26 24.09.10 we AES_Err_GCM_Auth_After_Final
0.27 24.09.10 we Fix for aad_cnt >= 2^29 or txt_acnt >= 2^29
0.28 26.09.10 we 64 bit counter for aad and auth. text
0.29 26.09.10 we use int64 if available
0.30 02.07.12 we 64-bit adjustments
0.31 21.11.12 we 64-bit fixes (use 32-bit not 16-bit code)
0.32 31.08.15 we constant time compare in Internal_Dec_Veri
0.33 08.08.17 we RB for CPUARM
**************************************************************************)
(*-------------------------------------------------------------------------
Pascal Implementation (C) Copyright 2010-2017 Wolfgang Ehrhardt
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from
the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software in
a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
----------------------------------------------------------------------------*)
{$i STD.INC}
interface
uses
BTypes, AES_Type, AES_Base, AES_Encr;
type
TGCM_Tab4K = array[0..255] of TAESBlock; {64 KB gf_mul_h table }
type
TBit64 = packed array[0..1] of longint; {64 bit counter }
type
TAES_GCMContext = packed record
actx : TAESContext; {Basic AES context }
aad_ghv : TAESBlock; {ghash value AAD }
txt_ghv : TAESBlock; {ghash value ciphertext}
ghash_h : TAESBlock; {ghash H value }
gf_t4k : TGCM_Tab4K; {gf_mul_h table }
aad_cnt : TBit64; {processed AAD bytes }
atx_cnt : TBit64; {authent. text bytes }
y0_val : longint; {initial 32-bit ctr val}
end;
{$ifdef CONST}
function AES_GCM_Init(const Key; KeyBits: word; var ctx: TAES_GCMContext): integer;
{-Init context, calculate key-dependent GF(2^128) element H=E(K,0) and mul tables}
{$ifdef DLL} stdcall; {$endif}
{$else}
function AES_GCM_Init(var Key; KeyBits: word; var ctx: TAES_GCMContext): integer;
{-Init context, calculate key-dependent GF(2^128) element H=E(K,0) and gf_mul tables}
{$endif}
function AES_GCM_Reset_IV(pIV: pointer; IV_len: word; var ctx: TAES_GCMContext): integer;
{-Reset: keep key but start new encryption with given IV}
{$ifdef DLL} stdcall; {$endif}
function AES_GCM_Encrypt(ptp, ctp: Pointer; ILen: longint; var ctx: TAES_GCMContext): integer;
{-Encrypt ILen bytes from ptp^ to ctp^ in CTR mode, update auth data}
{$ifdef DLL} stdcall; {$endif}
function AES_GCM_Decrypt(ctp, ptp: Pointer; ILen: longint; var ctx: TAES_GCMContext): integer;
{-Decrypt ILen bytes from ctp^ to ptp^ in CTR mode, update auth data}
{$ifdef DLL} stdcall; {$endif}
function AES_GCM_Add_AAD(pAAD: pointer; aLen: longint; var ctx: TAES_GCMContext): integer;
{-Add additional authenticated data (will not be encrypted)}
{$ifdef DLL} stdcall; {$endif}
function AES_GCM_Final(var tag: TAESBlock; var ctx: TAES_GCMContext): integer;
{-Compute GCM tag from context}
{$ifdef DLL} stdcall; {$endif}
function AES_GCM_Enc_Auth(var tag: TAESBlock; {Tag record}
{$ifdef CONST}const{$else}var{$endif} Key; KBits: word; {key and bitlength of key}
pIV: pointer; IV_len: word; {IV: address / length}
pAAD: pointer; aLen: word; {AAD: address / length}
ptp: pointer; pLen: longint; {plaintext: address / length}
ctp: pointer; {ciphertext: address}
var ctx: TAES_GCMContext {context, will be cleared}
): integer;
{-All-in-one call to encrypt/authenticate}
{$ifdef DLL} stdcall; {$endif}
function AES_GCM_Dec_Veri( ptag: pointer; tLen: word; {Tag: address / length (0..16)}
{$ifdef CONST}const{$else}var{$endif} Key; KBits: word; {key and bitlength of key}
pIV: pointer; IV_len: word; {IV: address / length}
pAAD: pointer; aLen: word; {AAD: address / length}
ctp: pointer; cLen: longint; {ciphertext: address / length}
ptp: pointer; {plaintext: address}
var ctx: TAES_GCMContext {context, will be cleared}
): integer;
{-All-in-one call to decrypt/verify. Decryption is done only if ptag^ is verified}
{$ifdef DLL} stdcall; {$endif}
{---------------------------------------------------------------------------}
{---------------------------------------------------------------------------}
{---------------------------------------------------------------------------}
{internal/testing}
procedure gf_mul_h(var a: TAESBlock; {$ifdef CONST}const{$else}var{$endif} ctx: TAES_GCMContext);
{-Multiply a by ctx.ghash_h in GF(2^128}
procedure gf_mul(var a: TAESBlock; {$ifdef CONST}const{$else}var{$endif} b: TAESBlock);
{-multiply two GF(2**128) field elements, a := a*b}
implementation
(* This implementation is based on Brian Gladman's source codes [2] which are
---------------------------------------------------------------------------
Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved.
LICENSE TERMS
The redistribution and use of this software (with or without changes)
is allowed without the payment of fees or royalties provided that:
1. source code distributions include the above copyright notice, this
list of conditions and the following disclaimer;
2. binary distributions include the above copyright notice, this list
of conditions and the following disclaimer in their documentation;
3. the name of the copyright holder is not used to endorse products
built using this software without specific written permission.
DISCLAIMER
This software is provided 'as is' with no explicit or implied warranties
in respect of its properties, including, but not limited to, correctness
and/or fitness for purpose.
---------------------------------------------------------------------------
*)
const
CTR_POS = 12;
BLK_MASK = AESBLKSIZE-1;
const
gft_le: array[0..255] of word = ( {Table of 'carries' in mulx8 operation}
$0000,$c201,$8403,$4602,$0807,$ca06,$8c04,$4e05,$100e,$d20f,$940d,$560c,$1809,$da08,$9c0a,$5e0b,
$201c,$e21d,$a41f,$661e,$281b,$ea1a,$ac18,$6e19,$3012,$f213,$b411,$7610,$3815,$fa14,$bc16,$7e17,
$4038,$8239,$c43b,$063a,$483f,$8a3e,$cc3c,$0e3d,$5036,$9237,$d435,$1634,$5831,$9a30,$dc32,$1e33,
$6024,$a225,$e427,$2626,$6823,$aa22,$ec20,$2e21,$702a,$b22b,$f429,$3628,$782d,$ba2c,$fc2e,$3e2f,
$8070,$4271,$0473,$c672,$8877,$4a76,$0c74,$ce75,$907e,$527f,$147d,$d67c,$9879,$5a78,$1c7a,$de7b,
$a06c,$626d,$246f,$e66e,$a86b,$6a6a,$2c68,$ee69,$b062,$7263,$3461,$f660,$b865,$7a64,$3c66,$fe67,
$c048,$0249,$444b,$864a,$c84f,$0a4e,$4c4c,$8e4d,$d046,$1247,$5445,$9644,$d841,$1a40,$5c42,$9e43,
$e054,$2255,$6457,$a656,$e853,$2a52,$6c50,$ae51,$f05a,$325b,$7459,$b658,$f85d,$3a5c,$7c5e,$be5f,
$00e1,$c2e0,$84e2,$46e3,$08e6,$cae7,$8ce5,$4ee4,$10ef,$d2ee,$94ec,$56ed,$18e8,$dae9,$9ceb,$5eea,
$20fd,$e2fc,$a4fe,$66ff,$28fa,$eafb,$acf9,$6ef8,$30f3,$f2f2,$b4f0,$76f1,$38f4,$faf5,$bcf7,$7ef6,
$40d9,$82d8,$c4da,$06db,$48de,$8adf,$ccdd,$0edc,$50d7,$92d6,$d4d4,$16d5,$58d0,$9ad1,$dcd3,$1ed2,
$60c5,$a2c4,$e4c6,$26c7,$68c2,$aac3,$ecc1,$2ec0,$70cb,$b2ca,$f4c8,$36c9,$78cc,$bacd,$fccf,$3ece,
$8091,$4290,$0492,$c693,$8896,$4a97,$0c95,$ce94,$909f,$529e,$149c,$d69d,$9898,$5a99,$1c9b,$de9a,
$a08d,$628c,$248e,$e68f,$a88a,$6a8b,$2c89,$ee88,$b083,$7282,$3480,$f681,$b884,$7a85,$3c87,$fe86,
$c0a9,$02a8,$44aa,$86ab,$c8ae,$0aaf,$4cad,$8eac,$d0a7,$12a6,$54a4,$96a5,$d8a0,$1aa1,$5ca3,$9ea2,
$e0b5,$22b4,$64b6,$a6b7,$e8b2,$2ab3,$6cb1,$aeb0,$f0bb,$32ba,$74b8,$b6b9,$f8bc,$3abd,$7cbf,$bebe);
{$ifndef BIT16}
{32/64-bit code}
{$ifdef BIT64}
{---------------------------------------------------------------------------}
function RB(A: longint): longint; {$ifdef HAS_INLINE} inline; {$endif}
{-reverse byte order in longint}
begin
RB := ((A and $FF) shl 24) or ((A and $FF00) shl 8) or ((A and $FF0000) shr 8) or ((A and longint($FF000000)) shr 24);
end;
{$else}
{$ifdef CPUARM}
{---------------------------------------------------------------------------}
function RB(A: longint): longint; {$ifdef HAS_INLINE} inline; {$endif}
{-reverse byte order in longint}
begin
RB := ((A and $FF) shl 24) or ((A and $FF00) shl 8) or ((A and $FF0000) shr 8) or ((A and longint($FF000000)) shr 24);
end;
{$else}
{---------------------------------------------------------------------------}
function RB(A: longint): longint; assembler; {&frame-}
{-reverse byte order in longint}
asm
{$ifdef LoadArgs}
mov eax,[A]
{$endif}
xchg al,ah
rol eax,16
xchg al,ah
end;
{$endif}
{$endif}
{$ifndef HAS_INT64}
{---------------------------------------------------------------------------}
procedure Inc64(var whi, wlo: longint; BLen: longint);
{-Add BLen to 64 bit value (wlo, whi)}
begin
asm
mov edx, wlo
mov ecx, whi
mov eax, Blen
add [edx], eax
adc dword ptr [ecx], 0
end;
end;
{$endif}
{---------------------------------------------------------------------------}
procedure mul_x(var a: TAESBlock; const b: TAESBlock);
{-Multiply a = b*x in GF(2^128)}
var
t: longint;
y: TWA4 absolute b;
const
MASK_80 = longint($80808080);
MASK_7F = longint($7f7f7f7f);
begin
t := gft_le[(y[3] shr 17) and MASK_80];
TWA4(a)[3] := ((y[3] shr 1) and MASK_7F) or (((y[3] shl 15) or (y[2] shr 17)) and MASK_80);
TWA4(a)[2] := ((y[2] shr 1) and MASK_7F) or (((y[2] shl 15) or (y[1] shr 17)) and MASK_80);
TWA4(a)[1] := ((y[1] shr 1) and MASK_7F) or (((y[1] shl 15) or (y[0] shr 17)) and MASK_80);
TWA4(a)[0] := (((y[0] shr 1) and MASK_7F) or ( (y[0] shl 15) and MASK_80)) xor t;
end;
{$else}
{---------------------------------------------------------------------------}
function RB(A: longint): longint;
{-reverse byte order in longint}
inline(
$58/ { pop ax }
$5A/ { pop dx }
$86/$C6/ { xchg dh,al}
$86/$E2); { xchg dl,ah}
{---------------------------------------------------------------------------}
procedure Inc64(var whi, wlo: longint; BLen: longint);
{-Add BLen to 64 bit value (wlo, whi)}
inline(
$58/ {pop ax }
$5A/ {pop dx }
$5B/ {pop bx }
$07/ {pop es }
$26/$01/$07/ {add es:[bx],ax }
$26/$11/$57/$02/ {adc es:[bx+02],dx}
$5B/ {pop bx }
$07/ {pop es }
$26/$83/$17/$00/ {adc es:[bx],0 }
$26/$83/$57/$02/$00);{adc es:[bx+02],0 }
{---------------------------------------------------------------------------}
procedure mul_x(var a: TAESBlock; {$ifdef CONST}const{$else}var{$endif} b: TAESBlock);
{-Multiply a = b*x in GF(2^128)}
var
x: TWA4;
t: longint;
const
hibit : array[0..1] of longint = (0, longint($80000000));
gf_poly: array[0..1] of longint = (0, longint($e1000000));
begin
x[0] := RB(TWA4(b)[0]);
x[1] := RB(TWA4(b)[1]);
x[2] := RB(TWA4(b)[2]);
x[3] := RB(TWA4(b)[3]);
t := gf_poly[x[3] and 1];
x[3] := (x[3] shr 1) or hibit[x[2] and 1];
x[2] := (x[2] shr 1) or hibit[x[1] and 1];
x[1] := (x[1] shr 1) or hibit[x[0] and 1];
TWA4(a)[0] := RB((x[0] shr 1) xor t);
TWA4(a)[1] := RB(x[1]);
TWA4(a)[2] := RB(x[2]);
TWA4(a)[3] := RB(x[3]);
end;
{$endif}
{Note: At least on my machine inlining AES_Xorblock in gf_mul is slower for}
{32-bit Delphi! The conditional define can be adjusted on other machines,}
{$ifdef DELPHI}
{$undef inline_xorblock}
{$else}
{$define inline_xorblock}
{$endif}
{---------------------------------------------------------------------------}
procedure gf_mul(var a: TAESBlock; {$ifdef CONST}const{$else}var{$endif} b: TAESBlock);
{-multiply two GF(2**128) field elements, a := a*b}
var
p: array[0..7] of TAESBlock;
r: TAESBlock;
x: TWA4 absolute r;
{$ifndef BIT16}
t: longint;
{$else}
w: word;
{$endif}
{$ifdef inline_xorblock}
j: integer;
{$endif}
i: integer;
c: byte;
begin
p[0] := b;
for i:=1 to 7 do mul_x(p[i], p[i-1]);
fillchar(r,sizeof(r),0);
for i:=0 to 15 do begin
c := a[15-i];
if i>0 then begin
{this is the inline code of the mul_x8 procedure}
{$ifndef BIT16}
t := gft_le[x[3] shr 24];
x[3] := ((x[3] shl 8) or (x[2] shr 24));
x[2] := ((x[2] shl 8) or (x[1] shr 24));
x[1] := ((x[1] shl 8) or (x[0] shr 24));
x[0] := ((x[0] shl 8) xor t);
{$else}
w := gft_le[r[15]];
r[15] := r[14];
r[14] := r[13];
r[13] := r[12];
r[12] := r[11];
r[11] := r[10];
r[10] := r[09];
r[09] := r[08];
r[08] := r[07];
r[07] := r[06];
r[06] := r[05];
r[05] := r[04];
r[04] := r[03];
r[03] := r[02];
r[02] := r[01];
r[01] := r[00] xor hi(w);
r[00] := byte(w);
{$endif}
end;
{$ifdef inline_xorblock}
for j:=0 to 7 do begin
if c and ($80 shr j) <> 0 then begin
x[3] := x[3] xor TWA4(p[j])[3];
x[2] := x[2] xor TWA4(p[j])[2];
x[1] := x[1] xor TWA4(p[j])[1];
x[0] := x[0] xor TWA4(p[j])[0];
end;
end;
{$else}
if c and $80 <> 0 then AES_Xorblock(r,p[0],r);
if c and $40 <> 0 then AES_Xorblock(r,p[1],r);
if c and $20 <> 0 then AES_Xorblock(r,p[2],r);
if c and $10 <> 0 then AES_Xorblock(r,p[3],r);
if c and $08 <> 0 then AES_Xorblock(r,p[4],r);
if c and $04 <> 0 then AES_Xorblock(r,p[5],r);
if c and $02 <> 0 then AES_Xorblock(r,p[6],r);
if c and $01 <> 0 then AES_Xorblock(r,p[7],r);
{$endif}
end;
a := r;
end;
{---------------------------------------------------------------------------}
procedure Make4K_Table(var ctx: TAES_GCMContext);
{-Build 4K gf_mul table from ctx.ghash_h. Assumes gf_t4k is zero-filled}
var
j,k: integer;
begin
with ctx do begin
gf_t4k[128] := ghash_h;
j := 64;
while j>0 do begin
mul_x(gf_t4k[j], gf_t4k[j+j]);
j := j shr 1;
end;
j := 2;
while j<256 do begin
for k:=1 to j-1 do aes_xorblock(gf_t4k[k], gf_t4k[j], gf_t4k[j+k]);
j := j+j;
end;
end;
end;
{---------------------------------------------------------------------------}
procedure gf_mul_h(var a: TAESBlock; {$ifdef CONST}const{$else}var{$endif} ctx: TAES_GCMContext);
{-Multiply a by ctx.ghash_h in GF(2^128}
var
r: TAESBlock;
x: TWA4 absolute r;
i: integer;
t: longint;
p: pointer;
begin
with ctx do begin
r := gf_t4k[a[15]];
for i:=14 downto 0 do begin
p := @gf_t4k[a[i]];
t := gft_le[x[3] shr 24];
{preform mul_x8 and xor in pre-computed table entries}
{$ifndef BIT16}
x[3] := ((x[3] shl 8) or (x[2] shr 24)) xor TWA4(p^)[3];
x[2] := ((x[2] shl 8) or (x[1] shr 24)) xor TWA4(p^)[2];
x[1] := ((x[1] shl 8) or (x[0] shr 24)) xor TWA4(p^)[1];
x[0] := ((x[0] shl 8) xor t) xor TWA4(p^)[0];
{$else}
{$ifdef BASM16}
asm
les di, [p]
db $66; mov bx, word ptr x[3*4]
db $66; mov cx, word ptr x[2*4]
db $66; mov dx, word ptr x[1*4]
db $66; mov si, word ptr x[0]
db $66; mov ax, cx
db $66; shr ax, 24
db $66; shl bx, 8
db $66; or ax, bx
db $66; xor ax, es:[di+12]
db $66; mov word ptr x[3*4],ax
db $66; mov ax, dx
db $66; shr ax, 24
db $66; shl cx, 8
db $66; or ax, cx
db $66; xor ax, es:[di+8]
db $66; mov word ptr x[2*4],ax
db $66; mov ax, si
db $66; shr ax, 24
db $66; shl dx, 8
db $66; or ax, dx
db $66; xor ax, es:[di+4]
db $66; mov word ptr x[1*4],ax
db $66; shl si, 8
db $66; xor si, word ptr t
db $66; xor si, es:[di]
db $66; mov word ptr x[0],si
end;
{$else}
t := gft_le[r[15]];
r[15] := r[14];
r[14] := r[13];
r[13] := r[12];
r[12] := r[11];
r[11] := r[10];
r[10] := r[09];
r[09] := r[08];
r[08] := r[07];
r[07] := r[06];
r[06] := r[05];
r[05] := r[04];
r[04] := r[03];
r[03] := r[02];
r[02] := r[01];
r[01] := r[00] xor TBA4(t)[1];
r[00] := byte(t);
x[3] := x[3] xor TWA4(p^)[3];
x[2] := x[2] xor TWA4(p^)[2];
x[1] := x[1] xor TWA4(p^)[1];
x[0] := x[0] xor TWA4(p^)[0];
{$endif}
{$endif}
end;
a := r;
end;
end;
(*
{Use this for table-less versions}
{---------------------------------------------------------------------------}
procedure gf_mul_h(var a: TAESBlock; {$ifdef CONST}const{$else}var{$endif} ctx: TAES_GCMContext);
{-Multiply a by ctx.ghash_h in GF(2^128}
begin
{Simply compute a*ghash_h, pre-computing the p-array for ghash_h}
{does not hurt, but does not give significant speed improvments.}
gf_mul(a, ctx.ghash_h);
end;
*)
{---------------------------------------------------------------------------}
function AES_GCM_Init({$ifdef CONST}const{$else}var{$endif} Key; KeyBits: word; var ctx: TAES_GCMContext): integer;
{-Init context, calculate key-dependent GF(2^128) element H=E(K,0) and gf_mul tables}
var
err: integer;
begin
fillchar(ctx, sizeof(ctx), 0);
with ctx do begin
err := AES_Init_Encr(Key, KeyBits, actx);
if err=0 then begin
AES_Encrypt(actx, ghash_h, ghash_h);
Make4K_Table(ctx);
end;
end;
AES_GCM_Init := err;
end;
{---------------------------------------------------------------------------}
procedure GCM_IncCtr(var x: TAESBlock);
{-GCM IncProc, only 32 LSB bits are changed (big-endian notation)}
var
j: integer;
begin
for j:=15 downto CTR_POS do begin
if x[j]=$FF then x[j] := 0
else begin
inc(x[j]);
exit;
end;
end;
end;
{---------------------------------------------------------------------------}
function AES_GCM_Reset_IV(pIV: pointer; IV_len: word; var ctx: TAES_GCMContext): integer;
{-Reset: keep key but start new encryption with given IV}
var
n_pos: longint;
i: integer;
begin
AES_GCM_Reset_IV := 0;
if (pIV=nil) and (IV_Len<>0) then begin
AES_GCM_Reset_IV := AES_Err_NIL_Pointer;
exit;
end;
with ctx do begin
{compute initial IV counter value Y0}
if IV_len=CTR_POS then begin
{if bitlen(IV)=96 then Y0=IV||1}
move(pIV^, actx.IV, CTR_POS);
TWA4(actx.IV)[3] := $01000000; {big-endian 32-bit 1}
end
else begin
{Y0 = GHASH(IV, H)}
n_pos := IV_len;
fillchar(actx.IV, sizeof(actx.IV),0);
while n_pos >= AESBLKSIZE do begin
AES_Xorblock(actx.IV, PAESBlock(pIV)^, actx.IV);
inc(Ptr2Inc(pIV), AESBLKSIZE);
dec(n_pos, AESBLKSIZE);
gf_mul_h(actx.IV, ctx);
end;
if n_pos>0 then begin
for i:=0 to n_pos-1 do begin
actx.IV[i] := actx.IV[i] xor pByte(pIV)^;
inc(Ptr2Inc(pIV));
end;
gf_mul_h(actx.IV, ctx);
end;
n_pos := longint(IV_len) shl 3;
i := 15;
while n_pos>0 do begin
actx.IV[i] := actx.IV[i] xor byte(n_pos);
n_pos := n_pos shr 8;
dec(i);
end;
gf_mul_h(actx.IV, ctx);
end;
{save initial 32-bit ctr val for final operation}
y0_val := TWA4(actx.IV)[3];
{Reset other data}
fillchar(aad_ghv, sizeof(aad_ghv),0);
fillchar(txt_ghv, sizeof(txt_ghv),0);
actx.Flag := 0;
actx.bLen := 0;
aad_cnt[0] := 0;
aad_cnt[1] := 0;
atx_cnt[0] := 0;
atx_cnt[1] := 0;
end;
end;
{---------------------------------------------------------------------------}
function AES_GCM_Final(var tag: TAESBlock; var ctx: TAES_GCMContext): integer;
{-Compute GCM tag from context}
var
tbuf: TAESBlock;
x: TWA4 absolute tbuf;
{$ifdef HAS_INT64}
ln: int64;
{$else}
ln: TBit64;
{$endif}
begin
with ctx do begin
if actx.Flag and $02 <> 0 then begin
AES_GCM_Final := AES_Err_GCM_Auth_After_Final;
exit;
end;
actx.Flag := actx.Flag or $02;
{compute GHASH(H, AAD, ctp)}
gf_mul_h(aad_ghv, ctx);
gf_mul_h(txt_ghv, ctx);
{Compute len(AAD) || len(ctp) with each len as 64-bit big-endian }
{Note: Tag calculation with Brian Gladman's original code will be}
{incorrect if either of the following shifts produces carries: }
{(ctx->txt_acnt << 3) or (ctx->hdr_cnt << 3)}
{$ifdef HAS_INT64}
{Gladman's code with 64-bit counters}
ln := (int64(atx_cnt) + AESBLKSIZE - 1) div AESBLKSIZE;
if (int64(aad_cnt)>0) and (ln<>0) then begin
tbuf := ghash_h;
while ln<>0 do begin
if odd(ln) then gf_mul(aad_ghv, tbuf);
ln := ln shr 1;
if ln<>0 then gf_mul(tbuf, tbuf);
end;
end;
{$else}
{equivalent code for compilers without int64}
ln[0] := atx_cnt[0];
ln[1] := atx_cnt[1];
Inc64(ln[1], ln[0], AESBLKSIZE - 1);
ln[0] := (ln[0] shr 4) or ((ln[1] and $F) shl 28);
ln[1] := ln[1] shr 4;
if (aad_cnt[0] or aad_cnt[1] <> 0) and (ln[0] or ln[1] <> 0) then begin
tbuf := ghash_h;
while (ln[0] or ln[1])<>0 do begin
if odd(ln[0]) then gf_mul(aad_ghv, tbuf);
ln[0] := ln[0] shr 1;
if odd(ln[1]) then ln[0] := ln[0] or longint($80000000);
ln[1] := ln[1] shr 1;
if ln[0] or ln[1] <> 0 then gf_mul(tbuf, tbuf);
end;
end;
{$endif}
x[0] := RB((aad_cnt[0] shr 29) or (aad_cnt[1] shl 3));
x[1] := RB((aad_cnt[0] shl 3));
x[2] := RB((atx_cnt[0] shr 29) or (atx_cnt[1] shl 3));
x[3] := RB((atx_cnt[0] shl 3));
AES_Xorblock(tbuf, txt_ghv, tbuf);
AES_Xorblock(aad_ghv, tbuf, aad_ghv);
gf_mul_h(aad_ghv, ctx);
{compute E(K,Y0)}
tbuf := actx.IV;
x[3] := y0_val;
AES_Encrypt(actx, tbuf, tbuf);
{tag = GHASH(H, AAD, ctp) xor E(K,Y0)}
AES_Xorblock(aad_ghv, tbuf, tag);
end;
AES_GCM_Final := 0;
end;
{---------------------------------------------------------------------------}
function crypt_data(ptp, ctp: Pointer; ILen: longint; var ctx: TAES_GCMContext): integer;
{-Internal: en/decrypt ILen byte from ptp to ctp}
var
cnt: longint;
b_pos: integer;
begin
crypt_data := 0;
if ILen<=0 then exit;
cnt := 0;
with ctx do begin
b_pos := actx.bLen;
if b_pos=0 then b_pos := AESBLKSIZE
else begin
while (cnt < ILen) and (b_pos < AESBLKSIZE) do begin
pByte(ctp)^ := pByte(ptp)^ xor actx.buf[b_pos];
inc(b_pos);
inc(Ptr2Inc(ptp));
inc(Ptr2Inc(ctp));
inc(cnt);
end;
end;
while cnt + AESBLKSIZE <= ILen do begin
GCM_IncCtr(actx.IV);
AES_Encrypt(actx, actx.IV, actx.buf);
AES_XorBlock(PAESBlock(ptp)^, actx.buf, PAESBlock(ctp)^);
inc(Ptr2Inc(ptp), AESBLKSIZE);
inc(Ptr2Inc(ctp), AESBLKSIZE);
inc(cnt, AESBLKSIZE);
end;
while cnt < ILen do begin
if b_pos=AESBLKSIZE then begin
GCM_IncCtr(actx.IV);
AES_Encrypt(actx, actx.IV, actx.buf);
b_pos := 0;
end;
pByte(ctp)^ := actx.buf[b_pos] xor pByte(ptp)^;
inc(b_pos);
inc(Ptr2Inc(ptp));
inc(Ptr2Inc(ctp));
inc(cnt);
end;
actx.bLen := (actx.bLen + cnt) and BLK_MASK;;
end;
end;
{---------------------------------------------------------------------------}
function auth_data(ctp: pointer; ILen: longint; var ctx: TAES_GCMContext): integer;
{-Internal: add ILen bytes from cipher text to auth ghash}
var
cnt: longint;
b_pos: integer;
begin
auth_data := 0;
if ILen<=0 then exit;
cnt := 0;
with ctx do begin
if actx.Flag and $02 <> 0 then begin
auth_data := AES_Err_GCM_Auth_After_Final;
exit;
end;
b_pos := atx_cnt[0] and BLK_MASK;
if (b_pos=0) and (atx_cnt[0] or atx_cnt[1] <> 0) then gf_mul_h(txt_ghv, ctx);
while (cnt < ILen) and (b_pos < AESBLKSIZE) do begin
txt_ghv[b_pos] := txt_ghv[b_pos] xor pByte(ctp)^;
inc(b_pos);
inc(Ptr2Inc(ctp));
inc(cnt);
end;
while cnt + AESBLKSIZE <= ILen do begin
gf_mul_h(txt_ghv, ctx);
AES_Xorblock(txt_ghv, PAESBlock(ctp)^, txt_ghv);
inc(Ptr2Inc(ctp), AESBLKSIZE);
inc(cnt, AESBLKSIZE);
end;
while cnt < ILen do begin
if b_pos=AESBLKSIZE then begin
gf_mul_h(txt_ghv, ctx);
b_pos := 0;
end;
txt_ghv[b_pos] := txt_ghv[b_pos] xor pByte(ctp)^;
inc(b_pos);
inc(Ptr2Inc(ctp));
inc(cnt);
end;
{$ifdef HAS_INT64}
Inc(int64(atx_cnt), cnt);
{$else}
Inc64(atx_cnt[1], atx_cnt[0],cnt);
{$endif}
end;
end;
{---------------------------------------------------------------------------}
function AES_GCM_Add_AAD(pAAD: pointer; aLen: longint; var ctx: TAES_GCMContext): integer;
{-Add additional authenticated data (will not be encrypted)}
var
cnt: longint;
b_pos: integer;
begin
AES_GCM_Add_AAD := 0;
if aLen <= 0 then exit;
if pAAD=nil then begin
AES_GCM_Add_AAD := AES_Err_NIL_Pointer;
exit;
end;
{$ifdef BIT16}
if (aLen>$FFFF) or (ofs(pAAD^)+aLen>$FFFF) then begin
AES_GCM_Add_AAD := AES_Err_Invalid_16Bit_Length;
exit;
end;
{$endif}
cnt := 0;
with ctx do begin
if actx.Flag and $02 <> 0 then begin
AES_GCM_Add_AAD := AES_Err_GCM_Auth_After_Final;
exit;
end;
b_pos := aad_cnt[0] and BLK_MASK;
if (b_pos=0) and (aad_cnt[0] or aad_cnt[1] <> 0) then gf_mul_h(aad_ghv, ctx);
while (cnt < aLen) and (b_pos < AESBLKSIZE) do begin
aad_ghv[b_pos] := aad_ghv[b_pos] xor pByte(pAAD)^;
inc(b_pos);
inc(Ptr2Inc(pAAD));
inc(cnt);
end;
while cnt + AESBLKSIZE <= aLen do begin
gf_mul_h(aad_ghv, ctx);
AES_Xorblock(aad_ghv, PAESBlock(pAAD)^, aad_ghv);
inc(Ptr2Inc(pAAD), AESBLKSIZE);
inc(cnt, AESBLKSIZE);
end;
while cnt < aLen do begin
if b_pos=AESBLKSIZE then begin
gf_mul_h(aad_ghv, ctx);
b_pos := 0;
end;
aad_ghv[b_pos] := aad_ghv[b_pos] xor pByte(pAAD)^;
inc(b_pos);
inc(Ptr2Inc(pAAD));
inc(cnt);
end;
{$ifdef HAS_INT64}
Inc(int64(aad_cnt),cnt);
{$else}
Inc64(aad_cnt[1],aad_cnt[0],cnt);
{$endif}
end;
end;
{---------------------------------------------------------------------------}
function AES_GCM_Encrypt(ptp, ctp: Pointer; ILen: longint; var ctx: TAES_GCMContext): integer;
{-Encrypt ILen bytes from ptp^ to ctp^ in CTR mode, update auth data}
var
err: integer;
begin
if ILen <= 0 then begin
AES_GCM_Encrypt := 0;
exit;
end;
if (ptp=nil) or (ctp=nil) then begin
AES_GCM_Encrypt := AES_Err_NIL_Pointer;
exit;
end;
{$ifdef BIT16}
if (ofs(ptp^)+ILen>$FFFF) or (ofs(ctp^)+ILen>$FFFF) then begin
AES_GCM_Encrypt := AES_Err_Invalid_16Bit_Length;
exit;
end;
{$endif}
err := crypt_data(ptp, ctp, ILen, ctx);
if err=0 then err := auth_data(ctp, ILen, ctx);
AES_GCM_Encrypt := err;
end;
{---------------------------------------------------------------------------}
function AES_GCM_Decrypt(ctp, ptp: Pointer; ILen: longint; var ctx: TAES_GCMContext): integer;
{-Decrypt ILen bytes from ctp^ to ptp^ in CTR mode, update auth data}
var
err: integer;
begin
if ILen <= 0 then begin
AES_GCM_Decrypt := 0;
exit;
end;
if (ptp=nil) or (ctp=nil) then begin
AES_GCM_Decrypt := AES_Err_NIL_Pointer;
exit;
end;
{$ifdef BIT16}
if (ofs(ptp^)+ILen>$FFFF) or (ofs(ctp^)+ILen>$FFFF) then begin
AES_GCM_Decrypt := AES_Err_Invalid_16Bit_Length;
exit;
end;
{$endif}
err := auth_data(ctp, ILen, ctx);
if err=0 then err := crypt_data(ctp, ptp, ILen, ctx);
AES_GCM_Decrypt := err;
end;
{---------------------------------------------------------------------------}
function Internal_Dec_Veri(var ctx: TAES_GCMContext; ptag: pointer; tLen: word;
ctp: pointer; cLen: longint; ptp: pointer): integer;
{-calculate and verify tLen bytes of ptag^, decrypt if OK}
var
err,i: integer;
diff: byte;
tag: TAESBlock;
begin
if cLen <= 0 then cLen := 0;
if cLen>0 then begin
if (ptp=nil) or (ctp=nil) then begin
Internal_Dec_Veri := AES_Err_NIL_Pointer;
exit;
end;
{$ifdef BIT16}
if (ofs(ptp^)+cLen>$FFFF) or (ofs(ctp^)+cLen>$FFFF) then begin
Internal_Dec_Veri := AES_Err_Invalid_16Bit_Length;
exit;
end;
{$endif}
end;
err := auth_data(ctp, cLen, ctx);
if err=0 then begin
{Compute/verify tag before doing decryption}
err := AES_GCM_Final(tag, ctx);
if err=0 then begin
diff :=0;
for i:=0 to pred(tLen) do begin
diff := diff or (pByte(ptag)^ xor tag[i]);
inc(Ptr2Inc(ptag));
end;
err := (((integer(diff)-1) shr 8) and 1)-1; {0 compare, -1 otherwise}
err := err and AES_Err_GCM_Verify_Tag;
end;
if err=0 then err := crypt_data(ctp, ptp, cLen, ctx);
end;
Internal_Dec_Veri := err;
end;
{---------------------------------------------------------------------------}
function AES_GCM_Enc_Auth(var tag: TAESBlock; {Tag record}
{$ifdef CONST}const{$else}var{$endif} Key; KBits: word; {key and bitlength of key}
pIV: pointer; IV_len: word; {IV: address / length}
pAAD: pointer; aLen: word; {AAD: address / length}
ptp: pointer; pLen: longint; {plaintext: address / length}
ctp: pointer; {ciphertext: address}
var ctx: TAES_GCMContext {context, will be cleared}
): integer;
{-All-in-one call to encrypt/authenticate}
var
err: integer;
begin
err := AES_GCM_Init(Key, KBits, ctx);
if err=0 then err := AES_GCM_Reset_IV(pIV, IV_len, ctx);
if err=0 then err := AES_GCM_Add_AAD(pAAD, aLen, ctx);
if err=0 then err := AES_GCM_Encrypt(ptp, ctp, pLen, ctx);
if err=0 then err := AES_GCM_Final(tag, ctx);
fillchar(ctx, sizeof(ctx), 0);
AES_GCM_Enc_Auth := err;
end;
{---------------------------------------------------------------------------}
function AES_GCM_Dec_Veri( ptag: pointer; tLen: word; {Tag: address / length (0..16)}
{$ifdef CONST}const{$else}var{$endif} Key; KBits: word; {key and bitlength of key}
pIV: pointer; IV_len: word; {IV: address / length}
pAAD: pointer; aLen: word; {AAD: address / length}
ctp: pointer; cLen: longint; {ciphertext: address / length}
ptp: pointer; {plaintext: address}
var ctx: TAES_GCMContext {context, will be cleared}
): integer;
{-All-in-one call to decrypt/verify. Decryption is done only if ptag^ is verified}
var
err: integer;
begin
err := AES_GCM_Init(Key, KBits, ctx);
if err=0 then err := AES_GCM_Reset_IV(pIV, IV_len, ctx);
if err=0 then err := AES_GCM_Add_AAD(pAAD, aLen, ctx);
if err=0 then err := Internal_Dec_Veri(ctx,ptag,tLen,ctp,cLen,ptp);
fillchar(ctx, sizeof(ctx), 0);
AES_GCM_Dec_Veri := err;
end;
end.