unit aes_cprf;

{Variable-length key AES CMAC Pseudo-Random Function-128}

{$i STD.INC}

interface

uses
  AES_Type, AES_OMAC;


(*************************************************************************

 DESCRIPTION   : Variable-length key AES CMAC Pseudo-Random Function-128

 REQUIREMENTS  : TP5-7, D1-D7/D9-D10, FPC, VP

 EXTERNAL DATA : ---

 MEMORY USAGE  : ---

 DISPLAY MODE  : ---

 REFERENCES    : [1] RFC 4615: The Advanced Encryption Standard-Cipher-based
                     Message Authentication Code-Pseudo-Random Function-128
                     (AES-CMAC-PRF-128) Algorithm for the Internet Key
                     Exchange Protocol (IKE)


 Version  Date      Author      Modification
 -------  --------  -------     ------------------------------------------
 0.10     28.05.07  W.Ehrhardt  Initial version
 0.11     28.05.07  we          function returns OMAC results
 0.12     16.06.07  we          AES_CPRF128_selftest stdcall
**************************************************************************)


(*-------------------------------------------------------------------------
 (C) Copyright 2007 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.
----------------------------------------------------------------------------*)


function AES_CPRF128({$ifdef CONST} const Key {$else} var Key {$endif}; KeyBytes: word;
                     msg: pointer; msglen: longint; var PRV: TAESBlock): integer;
  {Calculate variable-length key AES CMAC Pseudo-Random Function-128 for msg}
  {returns AES_OMAC error and 128-bit pseudo-random value PRV}
  {$ifdef DLL} stdcall; {$endif}

function AES_CPRF128_selftest: boolean;
  {-Selftest with RFC 4615 test vectors}
  {$ifdef DLL} stdcall; {$endif}


implementation


{---------------------------------------------------------------------------}
function AES_CPRF128({$ifdef CONST} const Key {$else} var Key {$endif}; KeyBytes: word;
                     msg: pointer; msglen: longint; var PRV: TAESBlock): integer;
  {Calculate variable-length key AES CMAC Pseudo-Random Function-128 for msg}
  {returns AES_OMAC error and 128-bit pseudo-random value PRV}
var
  LK: TAESBlock;    {local 128 bit key}
  ctx: TAESContext;
  err: integer;
const
  ZB: TAESBlock = (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
begin
  if KeyBytes=16 then begin
    {If the key, is exactly 128 bits, then we use it as-is (copy to local)}
    move(Key, LK, 16);
    err := 0;
  end
  else begin
    {If key length is not 128 bits, then we derive the local key LK by
    applying the AES-CMAC algorithm using a 128-bit zero as the CMAC key
    and Key as the input message: LK := AES-CMAC(0, Key, KeyBytes)}
    err := AES_OMAC_Init(ZB, 128, ctx);
    if err=0 then err := AES_OMAC_Update(@Key, KeyBytes, ctx);
    if err=0 then AES_OMAC_Final(LK, ctx);
  end;
  {PRV := AES-CMAC(LK, msg, msglen)}
  if err=0 then err := AES_OMAC_Init(LK, 128, ctx);
  if err=0 then err := AES_OMAC_Update(msg, msglen, ctx);
  if err=0 then AES_OMAC_Final(PRV, ctx);
  AES_CPRF128 := err;
end;


{---------------------------------------------------------------------------}
function AES_CPRF128_selftest: boolean;
  {-Selftest with RFC 4615 test vectors}
var
  PRV: TAESBlock;
  i,j: integer;
const
  {Test vectors from RFC section 4, Message is fix}
  msg: array[0..19] of byte = ($00,$01,$02,$03,$04,$05,$06,$07,$08,$09,
                               $0a,$0b,$0c,$0d,$0e,$0f,$10,$11,$12,$13);
  {Base key is fix, but test three diffenrent length >16, =16, <16}
  key: array[0..17] of byte = ($00,$01,$02,$03,$04,$05,$06,$07,$08,$09,
                               $0a,$0b,$0c,$0d,$0e,$0f,$ed,$cb);
   KL: array[1..3] of word =(18,16,10);
  {PRF outputs}
  PRA: array[1..3] of TAESBlock = (($84,$a3,$48,$a4,$a4,$5d,$23,$5b,$ab,$ff,$fc,$0d,$2b,$4d,$a0,$9a),
                                   ($98,$0a,$e8,$7b,$5f,$4c,$9c,$52,$14,$f5,$b6,$a8,$45,$5e,$4c,$2d),
                                   ($29,$0d,$9e,$11,$2e,$db,$09,$ee,$14,$1f,$cf,$64,$c0,$b7,$2f,$3d));
begin
  AES_CPRF128_selftest := false;
  for i:=1 to 3 do begin
    if AES_CPRF128(Key, KL[i], @msg, sizeof(msg), PRV)<>0 then exit;
    for j:=0 to 15 do if PRV[j]<>PRA[i][j] then exit;
  end;
  AES_CPRF128_selftest := true;
end;


end.
