program TestCrSysAPI;
{
Some tests for the Delphi/FreePascal interface to CryptoSys API
$Id: TestCrSysAPI $
Copyright (C) 2010-23 David Ireland, DI Management Services Pty Limited.
All rights reserved. <www.di-mgt.com.au> <www.cryptosys.net>
Provided as is with no warranties. Use at your own risk.
Last updated:
$Date: 2023-04-14 03:07 $
$Revision: 5.1.0 $
}
{$APPTYPE CONSOLE}
{$mode Delphi}
{$WARN 6058 off : Call to subroutine "$1" marked as inline is not inlined}
uses
SysUtils, diCryptoSys;
const
CRLF = #13 + #10;
type
// Define our own dynamic array type for `Array of Byte`
TByteArr = Array of Byte;
// UTILITIES WE USE IN THE TESTS...
Procedure pr_hexbytes(msg : String; arrbytes : Array of Byte);
// Displays an array of bytes as a hex string
var
i : Integer;
nbytes : Integer;
begin
Write(msg);
nbytes := Length(arrbytes);
For i := 0 to (nbytes - 1) do
Write(IntToHex(arrbytes[i], 2));
WriteLn;
end;
Function bytes_from_hex(strhex : AnsiString) : TByteArr;
var
nbytes : Integer;
begin
nbytes := Length(strhex) div 2;
// Dimension the byte array to receive the output
Result := NIL;
SetLength(Result, nbytes);
// Do the conversion
CNV_BytesFromHexStr(Pointer(Result), nbytes, strhex);
end;
Function bytes_to_hex(bytes : TByteArr) : AnsiString;
var
nchars : Integer;
nbytes : Integer;
begin
nbytes := Length(bytes);
nchars := nbytes * 2;
// Dimension the output
SetLength(Result, nchars);
// Do the conversion
CNV_HexStrFromBytes(Pointer(Result), nchars, Pointer(bytes), nbytes);
end;
Function bytes_from_string(s : AnsiString) : TByteArr;
var
i : Integer;
begin
Result := NIL;
SetLength(Result, Length(s));
for i := 1 to Length(s) do
Result[i-1] := Ord(s[i]);
end;
Function bytes_to_string(bytes : TByteArr) : AnsiString;
var
i : Integer;
begin
SetLength(Result, Length(bytes));
for i := 1 to Length(bytes) do
Result[i] := Chr(bytes[i-1]);
end;
Function bytes_copy(const Input : Array of Byte) : TByteArr;
begin
Result := NIL;
SetLength(Result, Length(Input));
Move(input[0], Result[0], Length(Input));
end;
Procedure create_text_file(fname: String; str : String);
var
myFile : TextFile;
begin
AssignFile(myFile, fname);
ReWrite(myFile);
Write(myFile, str);
CloseFile(myFile);
end;
const
// Correct message digest test vectors for Hash('abc')
OK_MD5_ABC : String = '900150983cd24fb0d6963f7d28e17f72';
OK_SHA1_ABC : String = 'a9993e364706816aba3e25717850c26c9cd0d89d';
OK_SHA256_ABC : String = 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad';
OK_SHA384_ABC : String = 'cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7';
OK_SHA512_ABC : String = 'ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f';
OK_SHA224_ABC : String = '23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7';
OK_RMD160_ABC : String = '8eb208f7e05d987a9b044a8e98c6b087f15a0bfc';
// Byte arrays used to test GCM authenticated encryption
// Test case 6
gcm_key : Array[0..15] of Byte = (
$FE, $FF, $E9, $92, $86, $65, $73, $1C,
$6D, $6A, $8F, $94, $67, $30, $83, $08
);
gcm_pt : Array[0..59] of Byte = (
$D9, $31, $32, $25, $F8, $84, $06, $E5,
$A5, $59, $09, $C5, $AF, $F5, $26, $9A,
$86, $A7, $A9, $53, $15, $34, $F7, $DA,
$2E, $4C, $30, $3D, $8A, $31, $8A, $72,
$1C, $3C, $0C, $95, $95, $68, $09, $53,
$2F, $CF, $0E, $24, $49, $A6, $B5, $25,
$B1, $6A, $ED, $F5, $AA, $0D, $E6, $57,
$BA, $63, $7B, $39
);
gcm_adata : Array[0..19] of Byte = (
$FE, $ED, $FA, $CE, $DE, $AD, $BE, $EF,
$FE, $ED, $FA, $CE, $DE, $AD, $BE, $EF,
$AB, $AD, $DA, $D2
);
gcm_iv : Array[0..59] of Byte = (
$93, $13, $22, $5D, $F8, $84, $06, $E5,
$55, $90, $9C, $5A, $FF, $52, $69, $AA,
$6A, $7A, $95, $38, $53, $4F, $7D, $A1,
$E4, $C3, $03, $D2, $A3, $18, $A7, $28,
$C3, $C0, $C9, $51, $56, $80, $95, $39,
$FC, $F0, $E2, $42, $9A, $6B, $52, $54,
$16, $AE, $DB, $F5, $A0, $DE, $6A, $57,
$A6, $37, $B3, $9B
);
gcm_ok_ct : Array[0..59] of Byte = (
$8C, $E2, $49, $98, $62, $56, $15, $B6,
$03, $A0, $33, $AC, $A1, $3F, $B8, $94,
$BE, $91, $12, $A5, $C3, $A2, $11, $A8,
$BA, $26, $2A, $3C, $CA, $7E, $2C, $A7,
$01, $E4, $A9, $A4, $FB, $A4, $3C, $90,
$CC, $DC, $B2, $81, $D4, $8C, $7C, $6F,
$D6, $28, $75, $D2, $AC, $A4, $17, $03,
$4C, $34, $AE, $E5
);
gcm_ok_tag : Array[0..15] of Byte = (
$61, $9C, $C5, $AE, $FF, $FE, $0B, $FA,
$46, $2A, $F4, $3C, $16, $99, $D0, $50
);
// DO SOME TESTS TO CALL CRYPTOSYS API FUNCTIONS
Procedure do_tests;
var
strhex : AnsiString;
arrbytes : TByteArr;
nbytes : Integer;
ret : Integer;
s : AnsiString;
buf : AnsiString;
ch : Char;
nchars : Integer;
keyhex : AnsiString;
ivhex : AnsiString;
inputhex : AnsiString;
outputhex : AnsiString;
correcthex : ansiString;
abKey : TByteArr;
abIV : TByteArr;
abIn : TByteArr;
abOut : TByteArr;
abCorrect : TByteArr;
digesthex : AnsiString;
fname : AnsiString;
hContext : Integer;
i : Integer;
tag : Array[0..15] of Byte;
isok : Boolean;
abAAD : TByteArr;
niter : Integer;
pthex : AnsiString;
block : TByteArr;
nextblk : TByteArr;
lastblk : TByteArr;
begin
WriteLn('Running ' + ExtractFileName(ParamStr(0)) + ' at ' + DateTimeToStr(Now));
WriteLn(CRLF + 'GENERAL:');
WriteLn('Interrogate the core diCryptoSys DLL:');
WriteLn('API_Version='+(IntToStr(API_Version)));
nchars := API_CompileTime(NIL, 0);
WriteLn('API_CompileTime returns nchars=' + IntToStr(nchars));
buf := AnsiString(StringOfChar(#0,nchars));
nchars := API_CompileTime(Pointer(buf), nchars);
WriteLn('API_CompileTime=' + buf);
Assert (nchars > 0);
ch := Chr(API_LicenceType(0));
WriteLn('API_LicenceType='+ ch);
nchars := API_ModuleName(NIL, 0, 0);
WriteLn('API_ModuleName returns nchars=' + IntToStr(nchars));
buf := AnsiString(StringOfChar(#0, nchars));
API_ModuleName(Pointer(buf), nchars, 0);
WriteLn('API_ModuleName=' + Trim(string(buf)));
ret := API_PowerUpTests(0);
WriteLn('API_PowerUpTests returns ' + IntToStr(ret) + ' (expected 0)');
WriteLn(CRLF + 'HEX TO BYTE ARRAY CONVERSIONS:');
// 1. Convert a hex string to a byte array
strhex := 'deadbeef00010203';
WriteLn('Hex= "' + strhex + '"');
nbytes := Length(strhex) div 2;
// Dimension the byte array to receive the output--IMPORTANT
SetLength(arrbytes, nbytes);
// Do the conversion
ret := CNV_BytesFromHexStr(Pointer(arrbytes), nbytes, strhex);
WriteLn('CNV_BytesFromHexStr returns ' + IntToStr(ret));
pr_hexbytes('Bytes=', arrbytes);
// Now convert this byte array back to a hex string
nchars := nbytes * 2;
// Dimension the string to receive the output--IMPORTANT
s := AnsiString(StringOfChar(#0, nchars));
ret := CNV_HexStrFromBytes(Pointer(s), nchars, Pointer(arrbytes), nbytes);
WriteLn('CNV_HexStrFromBytes returns ' + IntToStr(ret));
WriteLn('Str= "' + s + '"');
WriteLn(CRLF + 'TEST AES128_HexMode:');
keyhex := '0123456789ABCDEFF0E1D2C3B4A59687';
ivhex := 'FEDCBA9876543210FEDCBA9876543210';
// "Now is the time for all good men"
inputhex := '4E6F77206973207468652074696D6520666F7220616C6C20676F6F64206D656E';
correcthex := 'C3153108A8DD340C0BCB1DFE8D25D2320EE0E66BD2BB4A313FB75C5638E9E177';
// Set sOutput to be same length as sInput--IMPORTANT
outputhex := AnsiString(StringOfChar(#0,Length(inputhex)));
WriteLn('KY=' + keyhex);
WriteLn('IV=' + ivhex);
WriteLn('PT=' + inputhex);
// Encrypt hex string in one-off process
ret := AES128_HexMode(Pointer(outputhex), inputhex, keyhex, ENCRYPT, 'CBC', ivhex);
WriteLn('CT=' + string(outputhex) + ' ret=' + IntToStr(ret));
WriteLn('OK=' + correcthex);
// Now decrypt back to plain text
ret := AES128_HexMode(Pointer(outputhex), outputhex, keyhex, DECRYPT, 'cbc', ivhex);
WriteLn('P''=' + string(outputhex) + ' ret=' + IntToStr(ret));
Assert(AnsiCompareStr(string(outputhex), string(inputhex))= 0);
WriteLn(CRLF + 'TEST AES128_BytesMode:');
// NIST SP800-38a F.2.1 CBC-AES128.Encrypt
abKey := bytes_from_hex('2b7e151628aed2a6abf7158809cf4f3c');
abIV := bytes_from_hex('000102030405060708090a0b0c0d0e0f');
abIn := bytes_from_hex('6BC1BEE22E409F96E93D7E117393172A');
abCorrect := bytes_from_hex('7649ABAC8119B246CEE98E9B12E9197D');
pr_hexbytes('KY=', abKey);
pr_hexbytes('IV=', abIV);
pr_hexbytes('PT=', abIn);
// Dimension the output buffer -- IMPORTANT
SetLength(abOut, Length(abIn));
ret := AES128_BytesMode(Pointer(abOut), Pointer(abIn), Length(abIn), Pointer(abKey), ENCRYPT, 'CBC', Pointer(abIV));
Assert(0 = ret);
pr_hexbytes('CT=', abOut);
pr_hexbytes('OK=', abCorrect);
// Now decrypt back to plain text
ret := AES128_BytesMode(Pointer(abOut), Pointer(abOut), Length(abOut), Pointer(abKey), DECRYPT, 'CBC', Pointer(abIV));
pr_hexbytes('P''=', abOut);
Assert(0 = ret);
WriteLn(CRLF + 'TEST TRIPLE DES WITH CONTEXT:');
pthex := '4e6f772069732074';
keyhex := '0123456789abcdef23456789abcdef01456789abcdef0123';
ivhex := '1234567890abcdef';
correcthex := 'cb191f85d1ed8439';
WriteLn('TDEA Monte Carlo TCBC Mode Encrypt:');
WriteLn('KY=' + keyhex);
WriteLn('IV=' + ivhex);
WriteLn('PT=' + pthex);
hContext := TDEA_InitHex(keyhex, ENCRYPT, 'CBC', ivhex);
WriteLn('hContext=0x' + IntToHex(hContext, 8) + ' (expected nonzero)');
If hContext = 0 Then
halt;
// Do 10,000 times, working with byte arrays
niter := 10000;
// Set initial data by reading in from hex strings
nextblk := bytes_from_hex(pthex);
lastblk := bytes_from_hex(ivhex);
For i := 0 To niter-1 do
begin
block := bytes_copy(nextblk);
TDEA_Update(hContext, Pointer(block), Length(block));
nextblk := bytes_copy(lastblk);
lastblk := bytes_copy(block);
end;
WriteLn('After ' + IntToStr(niter) + ' iterations,');
WriteLn('CT=' + bytes_to_hex(block));
WriteLn('OK=' + correcthex);
TDEA_Final(hContext);
isok := (0 = AnsiCompareText(bytes_to_hex(block), string(correcthex)));
if isok then
WriteLn('OK, result is correct.')
else
WriteLn('ERROR: result is wrong!');
assert(isok);
WriteLn(CRLF + 'TEST HASH FUNCTIONS:');
// Pre-dimension with the largest buffer we might need for a hash (then we'll Trim it later)
digesthex := AnsiString(StringOfChar(#0, API_MAX_HASH_CHARS));
s := AnsiString('abc');
// Note how we can pass a pointer to a String even though the function expects a PByte
// (well, it works for us...)
ret := HASH_HexFromBytes(Pointer(digesthex), Length(digesthex), Pointer(s), Length(s), API_HASH_SHA1);
WriteLn('SHA-1('''+ string(s) + ''')="' + Trim(string(digesthex)) + '"');
Assert(ret > 0);
Assert(AnsiCompareText(string(digesthex),OK_SHA1_ABC)=0);
// Use a hex value instead (0x616263 <=> 'abc')
strhex := AnsiString('616263');
digesthex := AnsiString(StringOfChar(#0, API_MAX_HASH_CHARS));
ret := HASH_HexFromHex(Pointer(digesthex), Length(digesthex), strhex, API_HASH_SHA1);
WriteLn('SHA-1(0x'+ string(strhex) + ')="' + Trim(string(digesthex)) + '"');
Assert(ret > 0);
// Create a new text file containing only the 3-letter string 'abc'
fname := 'abc.txt';
create_text_file(string(fname), 'abc');
digesthex := AnsiString(StringOfChar(#0, API_MAX_HASH_CHARS));
ret := HASH_HexFromFile(Pointer(digesthex), Length(digesthex), fname, API_HASH_SHA1);
WriteLn('SHA-1('+ string(fname) + ')="' + Trim(string(digesthex)) + '"');
Assert(ret > 0);
// Use a different hash algorithm on the same text file
digesthex := AnsiString(StringOfChar(#0, API_MAX_HASH_CHARS));
ret := HASH_HexFromFile(Pointer(digesthex), Length(digesthex), fname, API_HASH_SHA512);
WriteLn('SHA-512('+ string(fname) + ')=' + CRLF + '"' + Trim(string(digesthex)) + '"');
Assert(ret > 0);
Assert(AnsiCompareText(string(digesthex),OK_SHA512_ABC)=0);
// Compute message digest of 'abc' for other hash functions
s := AnsiString('abc');
digesthex := AnsiString(StringOfChar(#0, API_MAX_HASH_CHARS));
ret := HASH_HexFromBytes(Pointer(digesthex), Length(digesthex), Pointer(s), Length(s), API_HASH_MD5);
WriteLn('MD5('''+ string(s) + ''')="' + Trim(string(digesthex)) + '"');
Assert(ret > 0);
Assert(AnsiCompareText(string(digesthex),OK_MD5_ABC)=0);
ret := HASH_HexFromBytes(Pointer(digesthex), Length(digesthex), Pointer(s), Length(s), API_HASH_RMD160);
WriteLn('RMD160('''+ string(s) + ''')=' + CRLF +'"' + Trim(string(digesthex)) + '"');
Assert(ret > 0);
Assert(AnsiCompareText(string(digesthex),OK_RMD160_ABC)=0);
ret := HASH_HexFromBytes(Pointer(digesthex), Length(digesthex), Pointer(s), Length(s), API_HASH_SHA224);
WriteLn('SHA-224('''+ string(s) + ''')="' + Trim(string(digesthex)) + '"');
Assert(ret > 0);
Assert(AnsiCompareText(string(digesthex),OK_SHA224_ABC)=0);
ret := HASH_HexFromBytes(Pointer(digesthex), Length(digesthex), Pointer(s), Length(s), API_HASH_SHA256);
WriteLn('SHA-256('''+ string(s) + ''')=' + CRLF +'"' + Trim(string(digesthex)) + '"');
Assert(ret > 0);
Assert(AnsiCompareText(string(digesthex),OK_SHA256_ABC)=0);
ret := HASH_HexFromBytes(Pointer(digesthex), Length(digesthex), Pointer(s), Length(s), API_HASH_SHA384);
WriteLn('SHA-384('''+ string(s) + ''')=' + CRLF +'"' + Trim(string(digesthex)) + '"');
Assert(ret > 0);
Assert(AnsiCompareText(string(digesthex),OK_SHA384_ABC)=0);
WriteLn(CRLF + 'TEST REPEATED USE OF SHA-1 WITH CONTEXT:');
// Show we can add both a string and a byte array to the same context
hContext := SHA1_Init;
s := AnsiString('ab');
ret := SHA1_AddString(hContext, s);
Assert(0 = ret);
SetLength(arrbytes, 1);
arrbytes[0] := $63; // 'c'
ret := SHA1_AddBytes(hContext, Pointer(arrbytes), Length(arrbytes));
Assert(0 = ret);
// Finalize and form the digest
digesthex := AnsiString(StringOfChar(#0, API_MAX_HASH_CHARS));
ret := SHA1_HexDigest(Pointer(digesthex), hContext);
WriteLn('SHA-1(''ab''||' + '0x63)="' + Trim(string(digesthex)) + '"');
Assert(0 = ret);
// SHA-1 of one million x 'a' in blocks of 1000
correcthex := '34AA973CD4C4DAA4F61EEB2BDBAD27316534016F';
hContext := SHA1_Init;
s := AnsiString(StringOfChar('a', 1000));
for i := 1 to 1000 do
SHA1_AddString(hContext, s);
digesthex := AnsiString(StringOfChar(#0, API_MAX_HASH_CHARS));
ret := SHA1_HexDigest(Pointer(digesthex), hContext);
WriteLn('SHA-1(1 million x "a")="' + Trim(string(digesthex)) + '"');
WriteLn('Correct= "' + correcthex + '"');
Assert(0 = ret);
Assert(AnsiCompareText(string(digesthex), string(correcthex)) = 0);
WriteLn(CRLF + 'TEST GCM AUTHENTICATED ENCRYPTION:');
// This must be most complicated function in the CryptoSys API library.
// It is the only one to output to two variables.
// We add to the fun by using a static byte array for the output tag ,
// dynamic arrays for others,
// and constant byte arrays for some of the data.
WriteLn('Test case 6:');
pr_hexbytes('K =', gcm_key);
pr_hexbytes('PT=', gcm_pt);
pr_hexbytes('A =', gcm_adata);
pr_hexbytes('IV=', gcm_iv);
SetLength(abOut, Length(gcm_pt));
// CAUTION with difference between pointers to dynamic and static arrays!!
ret := GCM_Encrypt(Pointer(abOut), Length(abOut),
@tag[0], Length(tag),
@gcm_pt[0], Length(gcm_pt),
@gcm_key[0], Length(gcm_key),
@gcm_iv[0], Length(gcm_iv),
@gcm_adata[0], Length(gcm_adata),
0);
WriteLn('GCM_Encrypt returns ' + IntToStr(ret) + ' (expected 0)');
pr_hexbytes('CT=', abOut);
pr_hexbytes('T =', tag);
// Compare results with correct values
isok := CompareMem(abOut, @gcm_ok_ct, Length(gcm_ok_ct));
WriteLn('CompareMem(ciphertext, correct) returns ' + BoolToStr(isok, True));
isok := CompareMem(@tag, @gcm_ok_tag, Length(gcm_ok_tag));
WriteLn('CompareMem(tag, correct) returns ' + BoolToStr(isok, True));
// Now decrypt
abIn := abOut;
// COMMENT: for some reason we have to re-do SetLength here or it fails...
SetLength(abOut, Length(abIn));
ret := GCM_Decrypt(Pointer(abOut), Length(abOut),
Pointer(abIn), Length(abIn),
@gcm_key[0], Length(gcm_key),
@gcm_iv[0], Length(gcm_iv),
@gcm_adata[0], Length(gcm_adata),
@tag[0], Length(tag),
0);
WriteLn('GCM_Decrypt returns ' + IntToStr(ret) + ' (expected 0)');
pr_hexbytes('P''=', abOut);
isok := CompareMem(abOut, @gcm_pt, Length(gcm_pt));
WriteLn('CompareMem(plaintext, correct) returns ' + BoolToStr(isok, True));
WriteLn(CRLF + 'TEST GMAC AUTHENTICATION:');
// Ref: http://www.mail-archive.com/stds-p1619@listserv.ieee.org/msg00321.html
abKey := bytes_from_hex('feffe9928665731c6d6a8f9467308308');
abIV := bytes_from_hex('cafebabefacedbaddecaf888');
abAAD := bytes_from_hex('feedfacedeadbeeffeedfacedeadbeef');
abCorrect := bytes_from_hex('54df474f4e71a9ef8a09bf30da7b1a92');
pr_hexbytes('KEY=', abKey);
pr_hexbytes('IV =', abIV);
pr_hexbytes('AAD=', abAAD);
// GMAC := Encrypt with no CT output or PT input. GMAC value is in tag.
ret := GCM_Encrypt(NIL, 0,
@tag[0], Length(tag),
NIL, 0,
Pointer(abKey), Length(abKey),
Pointer(abIV), Length(abIV),
Pointer(abAAD), Length(abAAD),
0);
WriteLn('GCM_Encrypt(GMAC) returns ' + IntToStr(ret) + ' (expected 0)');
pr_hexbytes('TAG=', tag);
pr_hexbytes('OK =', abCorrect);
isok := CompareMem(@tag, abCorrect, Length(abCorrect));
WriteLn('CompareMem(gmac, correct) returns ' + BoolToStr(isok, True));
WriteLn(CRLF + 'TEST STREAM CIPHER:');
WriteLn('Salsa20:');
keyhex := '000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F';
ivhex := '0000000000000000';
// Ref: eSTREAM `verified.test-vectors.txt` Set 3, vector# 0:
inputhex := '0000000000000000000000000000000000000000000000000000000000000000';
correcthex := 'B580F7671C76E5F7441AF87C146D6B513910DC8B4146EF1B3211CF12AF4A4B49';
// Set sOutput to be same length as sInput--IMPORTANT
outputhex := AnsiString(StringOfChar(#0,Length(inputhex)));
WriteLn('KY=' + keyhex);
WriteLn('IV=' + ivhex);
WriteLn('PT=' + inputhex);
// Encipher using Salsa20 in hex mode
ret := CIPHER_StreamHex(Pointer(outputhex), Length(outputhex), inputhex, keyhex, ivhex, 0, API_SC_SALSA20);
WriteLn('CT=' + string(outputhex) + ' ret=' + IntToStr(ret));
WriteLn('OK=' + correcthex);
// Now decrypt back to plain text (just encrypt again)
inputhex := outputhex;
ret := CIPHER_StreamHex(Pointer(outputhex), Length(outputhex), inputhex, keyhex, ivhex, 0, API_SC_SALSA20);
WriteLn('P''=' + string(outputhex) + ' ret=' + IntToStr(ret));
Assert(AnsiCompareStr(string(outputhex), string(inputhex))= 0);
WriteLn('ChaCha20:');
// ChaCha20 test vector from "ChaCha20 and Poly1305 for IETF protocols
abKey := bytes_from_hex('000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F');
abIV := bytes_from_hex('000000000000004a00000000');
// ''
abIn := bytes_from_string('Ladies and Gentlemen of the class of ''99:');
abCorrect := bytes_from_hex('6E2E359A2568F98041BA0728DD0D6981E97E7AEC1D4360C20A27AFCCFD9FAE0BF91B65C5524733AB8F');
pr_hexbytes('KY=', abKey);
pr_hexbytes('IV=', abIV);
pr_hexbytes('PT=', abIn);
// Dimension the output buffer -- IMPORTANT
SetLength(abOut, Length(abIn));
ret := CIPHER_StreamBytes(Pointer(abOut), Pointer(abIn), Length(abIn), Pointer(abKey), Length(abKey), Pointer(abIV), Length(abIV), 1, API_SC_CHACHA20);
Assert(0 = ret);
pr_hexbytes('CT=', abOut);
pr_hexbytes('OK=', abCorrect);
isok := CompareMem(abOut, abCorrect, Length(abCorrect));
Assert(isok);
// Now decrypt back to plain text
ret := CIPHER_StreamBytes(Pointer(abOut), Pointer(abOut), Length(abOut), Pointer(abKey), Length(abKey), Pointer(abIV), Length(abIV), 1, API_SC_CHACHA20);
pr_hexbytes('P''=', abOut);
WriteLn('"' + bytes_to_string(abOut) + '"');
Assert(0 = ret);
isok := CompareMem(abOut, abIn, Length(abCorrect));
Assert(isok);
WriteLn(CRLF+'ALL DONE.');
end;
// MAIN PROCEDURE
begin
try
do_tests;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.