VirtualBox

source: vbox/trunk/src/VBox/Main/testcase/tstVBoxCrypto.cpp

Last change on this file was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.1 KB
Line 
1/* $Id: tstVBoxCrypto.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * tstVBoxCrypto - Testcase for the cryptographic support module.
4 */
5
6/*
7 * Copyright (C) 2022-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#include <VBox/VBoxCryptoIf.h>
33#include <VBox/err.h>
34
35#include <iprt/file.h>
36#include <iprt/test.h>
37#include <iprt/ldr.h>
38#include <iprt/mem.h>
39#include <iprt/memsafer.h>
40#include <iprt/rand.h>
41#include <iprt/string.h>
42#include <iprt/vfs.h>
43
44
45/*********************************************************************************************************************************
46* Global Variables *
47*********************************************************************************************************************************/
48static RTTEST g_hTest;
49static const uint8_t g_abDek[64] = { 0x42 };
50static const char g_szPassword[] = "testtesttest";
51static const char g_szPasswordWrong[] = "testtest";
52
53static const char *g_aCiphers[] =
54{
55 "AES-XTS128-PLAIN64",
56 "AES-GCM128",
57 "AES-CTR128",
58
59 "AES-XTS256-PLAIN64",
60 "AES-GCM256",
61 "AES-CTR256"
62};
63
64#define CHECK_STR(str1, str2) do { if (strcmp(str1, str2)) { RTTestIFailed("line %u: '%s' != '%s' (*)", __LINE__, str1, str2); } } while (0)
65#define CHECK_BYTES(bytes1, bytes2, size) do { if (memcmp(bytes1, bytes2, size)) { RTTestIFailed("line %u: '%s' != '%s' (*)", __LINE__, #bytes1, bytes2); } } while (0)
66
67
68/**
69 * Creates a new cryptographic context and returns the encoded string version on success.
70 *
71 * @returns VBox status code.
72 * @param pCryptoIf Pointer to the cryptographic interface.
73 * @param pszCipher The cipher to use.
74 * @param pszPassword The password to use.
75 * @param ppszCtx Where to store the pointer to the context on success.
76 */
77static int tstCryptoCtxCreate(PCVBOXCRYPTOIF pCryptoIf, const char *pszCipher, const char *pszPassword, char **ppszCtx)
78{
79 VBOXCRYPTOCTX hCryptoCtx;
80
81 int rc = pCryptoIf->pfnCryptoCtxCreate(pszCipher, pszPassword, &hCryptoCtx);
82 if (RT_SUCCESS(rc))
83 {
84 rc = pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, ppszCtx);
85 int rc2 = pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
86 AssertReleaseRC(rc2);
87 }
88
89 return rc;
90}
91
92
93/**
94 * Writes data to the given file until the given size is reached.
95 *
96 * @returns VBox status code.
97 * @param hVfsFile The file handle to write to.
98 * @param cbWrite Number of bytes to write.
99 */
100static int tstCryptoVfsWrite(RTVFSFILE hVfsFile, size_t cbWrite)
101{
102 RTTestISub("Writing to encrypted file");
103
104 int rc = VINF_SUCCESS;
105 size_t cbBufLeft = _128K;
106 void *pv = RTMemTmpAllocZ(cbBufLeft);
107 if (pv)
108 {
109 size_t cbLeft = cbWrite;
110 uint32_t cCounter = 0;
111 uint8_t *pb = (uint8_t *)pv;
112
113 /* Fill the counter buffer. */
114 uint32_t *pu32 = (uint32_t *)pv;
115 for (uint32_t i = 0; i < cbBufLeft / sizeof(uint32_t); i++)
116 *pu32++ = cCounter++;
117
118
119 for (;;)
120 {
121 size_t cbThisWrite = RTRandU64Ex(1, RT_MIN(cbBufLeft, cbLeft));
122 rc = RTVfsFileWrite(hVfsFile, pb, cbThisWrite, NULL /*pcbWritten*/);
123 if (RT_FAILURE(rc))
124 {
125 RTTestIFailed("Writing to file failed with %Rrc (cbLeft=%zu, cbBufLeft=%zu, cbThisWrite=%zu)",
126 rc, cbLeft, cbBufLeft, cbThisWrite);
127 break;
128 }
129
130 cbLeft -= cbThisWrite;
131 cbBufLeft -= cbThisWrite;
132 pb += cbThisWrite;
133
134 if (!cbBufLeft)
135 {
136 /* Fill the counter buffer again. */
137 pu32 = (uint32_t *)pv;
138 pb = (uint8_t *)pv;
139 cbBufLeft = _128K;
140 for (uint32_t i = 0; i < cbBufLeft / sizeof(uint32_t); i++)
141 *pu32++ = cCounter++;
142 }
143
144 if (!cbLeft)
145 break;
146 }
147
148 RTMemTmpFree(pv);
149 }
150 else
151 {
152 RTTestIFailed("Allocating write buffer failed - out of memory");
153 rc = VERR_NO_MEMORY;
154 }
155
156 RTTestISubDone();
157 return rc;
158}
159
160
161/**
162 * Writes data to the given file until the given size is reached.
163 *
164 * @returns VBox status code.
165 * @param hVfsFile The file handle to write to.
166 * @param cbFile Size of the file payload in bytes.
167 */
168static int tstCryptoVfsReadAndVerify(RTVFSFILE hVfsFile, size_t cbFile)
169{
170 RTTestISub("Reading from encrypted file and verifying data");
171
172 int rc = VINF_SUCCESS;
173 void *pv = RTMemTmpAllocZ(_128K);
174 if (pv)
175 {
176 size_t cbLeft = cbFile;
177 uint32_t cCounter = 0;
178
179 for (;;)
180 {
181 /* Read the data in multiple calls. */
182 size_t cbBufLeft = RT_MIN(cbLeft, _128K);
183 uint8_t *pb = (uint8_t *)pv;
184
185 while (cbBufLeft)
186 {
187 size_t cbThisRead = RTRandU64Ex(1, RT_MIN(cbBufLeft, cbLeft));
188 rc = RTVfsFileRead(hVfsFile, pb, cbThisRead, NULL /*pcbWritten*/);
189 if (RT_FAILURE(rc))
190 {
191 RTTestIFailed("Reading from file failed with %Rrc (cbLeft=%zu, cbBufLeft=%zu, cbThisRead=%zu)",
192 rc, cbLeft, cbBufLeft, cbThisRead);
193 break;
194 }
195
196 cbBufLeft -= cbThisRead;
197 pb += cbThisRead;
198 }
199
200 if (RT_FAILURE(rc))
201 break;
202
203 /* Verify the read data. */
204 size_t cbInBuffer = RT_MIN(cbLeft, _128K);
205 Assert(!(cbInBuffer % sizeof(uint32_t)));
206 uint32_t *pu32 = (uint32_t *)pv;
207
208 for (uint32_t i = 0; i < cbInBuffer / sizeof(uint32_t); i++)
209 {
210 if (*pu32 != cCounter)
211 {
212 RTTestIFailed("Reading from file resulted in corrupted data (expected '%#x' got '%#x')",
213 cCounter, *pu32);
214 break;
215 }
216
217 pu32++;
218 cCounter++;
219 }
220
221 cbLeft -= RT_MIN(cbLeft, _128K);
222 if (!cbLeft)
223 break;
224 }
225
226 RTMemTmpFree(pv);
227 }
228 else
229 {
230 RTTestIFailed("Allocating read buffer failed - out of memory");
231 rc = VERR_NO_MEMORY;
232 }
233
234 RTTestISubDone();
235 return rc;
236}
237
238
239/**
240 * Testing some basics of the encrypted file VFS code.
241 *
242 * @param pCryptoIf Pointer to the callback table.
243 */
244static void tstCryptoVfsBasics(PCVBOXCRYPTOIF pCryptoIf)
245{
246 RTTestISub("Encrypted file - Basics");
247
248 RTTestDisableAssertions(g_hTest);
249
250 char *pszCtx = NULL;
251 int rc = tstCryptoCtxCreate(pCryptoIf, g_aCiphers[4], g_szPassword, &pszCtx);
252 if (RT_SUCCESS(rc))
253 {
254 /* Create the memory file to write to. */
255 RTVFSFILE hVfsFile;
256 rc = RTVfsMemFileCreate(NIL_RTVFSIOSTREAM, 0 /*cbEstimate*/, &hVfsFile);
257 if (RT_SUCCESS(rc))
258 {
259 RTVFSFILE hVfsFileEnc;
260
261 RTTestISub("Creating encrypted file");
262
263 rc = pCryptoIf->pfnCryptoFileFromVfsFile(hVfsFile, pszCtx, g_szPassword, &hVfsFileEnc);
264 if (RT_SUCCESS(rc))
265 {
266 RTTestISubDone();
267
268 size_t cbFile = RT_ALIGN_Z(RTRandU32Ex(_1K, 10 * _1M), sizeof(uint32_t)); /* Align to full counter field size. */
269 rc = tstCryptoVfsWrite(hVfsFileEnc, cbFile);
270 RTVfsFileRelease(hVfsFileEnc); /* Close file. */
271 if (RT_SUCCESS(rc))
272 {
273 /* Reopen for reading. */
274 RTTestISub("Open encrypted file");
275
276 /* Reset the memory file offset. */
277 RTVfsFileSeek(hVfsFile, 0, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
278
279 rc = pCryptoIf->pfnCryptoFileFromVfsFile(hVfsFile, pszCtx, g_szPassword, &hVfsFileEnc);
280 if (RT_SUCCESS(rc))
281 {
282 RTTestISubDone();
283
284 RTTestISub("Query encrypted file size");
285 uint64_t cbFileRd;
286 rc = RTVfsFileQuerySize(hVfsFileEnc, &cbFileRd);
287 if (RT_SUCCESS(rc))
288 {
289 if (cbFile != cbFileRd)
290 RTTestIFailed("Unexpected file size, got %#llx expected %#zx", cbFileRd, cbFile);
291
292 RTTestISubDone();
293 tstCryptoVfsReadAndVerify(hVfsFileEnc, cbFile);
294 }
295 else
296 RTTestIFailed("Querying encrypted file size failed %Rrc", rc);
297
298 RTVfsFileRelease(hVfsFileEnc); /* Close file. */
299 }
300 else
301 RTTestIFailed("Opening encrypted file for reading failed with %Rrc", rc);
302
303 }
304 /* Error set on failure. */
305 }
306 else
307 RTTestIFailed("Creating encrypted file handle failed with %Rrc", rc);
308
309 RTVfsFileRelease(hVfsFile);
310 }
311 else
312 RTTestIFailed("Creating a new encrypted file failed with %Rrc", rc);
313
314 RTMemFree(pszCtx);
315 }
316 else
317 RTTestIFailed("Creating a new encrypted context failed with %Rrc", rc);
318
319 RTTestRestoreAssertions(g_hTest);
320 RTTestISubDone();
321}
322
323
324/**
325 * Testing some basics of the crypto keystore code.
326 *
327 * @param pCryptoIf Pointer to the callback table.
328 */
329static void tstCryptoKeyStoreBasics(PCVBOXCRYPTOIF pCryptoIf)
330{
331 RTTestISub("Crypto Keystore - Basics");
332
333 RTTestDisableAssertions(g_hTest);
334
335 for (uint32_t i = 0; i < RT_ELEMENTS(g_aCiphers); i++)
336 {
337 RTTestISubF("Creating a new keystore for cipher '%s'", g_aCiphers[i]);
338
339 char *pszKeystoreEnc = NULL; /**< The encoded keystore. */
340 int rc = pCryptoIf->pfnCryptoKeyStoreCreate(g_szPassword, &g_abDek[0], sizeof(g_abDek),
341 g_aCiphers[i], &pszKeystoreEnc);
342 if (RT_SUCCESS(rc))
343 {
344 uint8_t *pbKey = NULL;
345 size_t cbKey = 0;
346 char *pszCipher = NULL;
347
348 RTTestSub(g_hTest, "Trying to unlock DEK with wrong password");
349 rc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(pszKeystoreEnc, g_szPasswordWrong,
350 &pbKey, &cbKey, &pszCipher);
351 RTTESTI_CHECK_RC(rc, VERR_VD_PASSWORD_INCORRECT);
352
353 RTTestSub(g_hTest, "Trying to unlock DEK with correct password");
354 rc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(pszKeystoreEnc, g_szPassword,
355 &pbKey, &cbKey, &pszCipher);
356 RTTESTI_CHECK_RC_OK(rc);
357 if (RT_SUCCESS(rc))
358 {
359 RTTESTI_CHECK(cbKey == sizeof(g_abDek));
360 CHECK_STR(pszCipher, g_aCiphers[i]);
361 CHECK_BYTES(pbKey, &g_abDek[0], sizeof(g_abDek));
362
363 RTMemSaferFree(pbKey, cbKey);
364 }
365
366 RTMemFree(pszKeystoreEnc);
367 }
368 else
369 RTTestIFailed("Creating a new keystore failed with %Rrc", rc);
370 }
371
372 RTTestRestoreAssertions(g_hTest);
373}
374
375
376int main(int argc, char *argv[])
377{
378 /*
379 * Initialization.
380 */
381 RTEXITCODE rcExit = RTTestInitAndCreate("tstVBoxCrypto", &g_hTest);
382 if (rcExit != RTEXITCODE_SUCCESS)
383 return rcExit;
384 RTTestBanner(g_hTest);
385
386 RTTestSub(g_hTest, "Loading the cryptographic support module");
387 const char *pszModCrypto = NULL;
388 if (argc == 2)
389 {
390 /* The module to load is given on the command line. */
391 pszModCrypto = argv[1];
392 }
393 else
394 {
395 /* Try find it in the extension pack. */
396 /** @todo */
397 RTTestSkipped(g_hTest, "Getting the module from the extension pack is not implemented yet, skipping testcase");
398 }
399
400 if (pszModCrypto)
401 {
402 RTLDRMOD hLdrModCrypto = NIL_RTLDRMOD;
403 int rc = RTLdrLoad(pszModCrypto, &hLdrModCrypto);
404 if (RT_SUCCESS(rc))
405 {
406 PFNVBOXCRYPTOENTRY pfnCryptoEntry = NULL;
407 rc = RTLdrGetSymbol(hLdrModCrypto, VBOX_CRYPTO_MOD_ENTRY_POINT, (void **)&pfnCryptoEntry);
408 if (RT_SUCCESS(rc))
409 {
410 PCVBOXCRYPTOIF pCryptoIf = NULL;
411 rc = pfnCryptoEntry(&pCryptoIf);
412 if (RT_SUCCESS(rc))
413 {
414 /* Loading succeeded, now we can start real testing. */
415 tstCryptoKeyStoreBasics(pCryptoIf);
416 tstCryptoVfsBasics(pCryptoIf);
417 }
418 else
419 RTTestIFailed("Calling '%s' failed with %Rrc", VBOX_CRYPTO_MOD_ENTRY_POINT, rc);
420 }
421 else
422 RTTestIFailed("Failed to resolve entry point '%s' with %Rrc", VBOX_CRYPTO_MOD_ENTRY_POINT, rc);
423 }
424 else
425 RTTestIFailed("Failed to load the crypto module '%s' with %Rrc", pszModCrypto, rc);
426 }
427
428 return RTTestSummaryAndDestroy(g_hTest);
429}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette