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.