VirtualBox

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

Last change on this file since 98526 was 98103, checked in by vboxsync, 23 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 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * tstVBoxCrypto - Testcase for the cryptographic support module.
4 */
5
6/*
7 * Copyright (C) 2022-2023 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 * @returns nothing.
243 * @param pCryptoIf Pointer to the callback table.
244 */
245static void tstCryptoVfsBasics(PCVBOXCRYPTOIF pCryptoIf)
246{
247 RTTestISub("Encrypted file - Basics");
248
249 RTTestDisableAssertions(g_hTest);
250
251 char *pszCtx = NULL;
252 int rc = tstCryptoCtxCreate(pCryptoIf, g_aCiphers[4], g_szPassword, &pszCtx);
253 if (RT_SUCCESS(rc))
254 {
255 /* Create the memory file to write to. */
256 RTVFSFILE hVfsFile;
257 rc = RTVfsMemFileCreate(NIL_RTVFSIOSTREAM, 0 /*cbEstimate*/, &hVfsFile);
258 if (RT_SUCCESS(rc))
259 {
260 RTVFSFILE hVfsFileEnc;
261
262 RTTestISub("Creating encrypted file");
263
264 rc = pCryptoIf->pfnCryptoFileFromVfsFile(hVfsFile, pszCtx, g_szPassword, &hVfsFileEnc);
265 if (RT_SUCCESS(rc))
266 {
267 RTTestISubDone();
268
269 size_t cbFile = RT_ALIGN_Z(RTRandU32Ex(_1K, 10 * _1M), sizeof(uint32_t)); /* Align to full counter field size. */
270 rc = tstCryptoVfsWrite(hVfsFileEnc, cbFile);
271 RTVfsFileRelease(hVfsFileEnc); /* Close file. */
272 if (RT_SUCCESS(rc))
273 {
274 /* Reopen for reading. */
275 RTTestISub("Open encrypted file");
276
277 /* Reset the memory file offset. */
278 RTVfsFileSeek(hVfsFile, 0, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
279
280 rc = pCryptoIf->pfnCryptoFileFromVfsFile(hVfsFile, pszCtx, g_szPassword, &hVfsFileEnc);
281 if (RT_SUCCESS(rc))
282 {
283 RTTestISubDone();
284
285 RTTestISub("Query encrypted file size");
286 uint64_t cbFileRd;
287 rc = RTVfsFileQuerySize(hVfsFileEnc, &cbFileRd);
288 if (RT_SUCCESS(rc))
289 {
290 if (cbFile != cbFileRd)
291 RTTestIFailed("Unexpected file size, got %#llx expected %#zx", cbFileRd, cbFile);
292
293 RTTestISubDone();
294 tstCryptoVfsReadAndVerify(hVfsFileEnc, cbFile);
295 }
296 else
297 RTTestIFailed("Querying encrypted file size failed %Rrc", rc);
298
299 RTVfsFileRelease(hVfsFileEnc); /* Close file. */
300 }
301 else
302 RTTestIFailed("Opening encrypted file for reading failed with %Rrc", rc);
303
304 }
305 /* Error set on failure. */
306 }
307 else
308 RTTestIFailed("Creating encrypted file handle failed with %Rrc", rc);
309
310 RTVfsFileRelease(hVfsFile);
311 }
312 else
313 RTTestIFailed("Creating a new encrypted file failed with %Rrc", rc);
314
315 RTMemFree(pszCtx);
316 }
317 else
318 RTTestIFailed("Creating a new encrypted context failed with %Rrc", rc);
319
320 RTTestRestoreAssertions(g_hTest);
321 RTTestISubDone();
322}
323
324
325/**
326 * Testing some basics of the crypto keystore code.
327 *
328 * @returns nothing.
329 * @param pCryptoIf Pointer to the callback table.
330 */
331static void tstCryptoKeyStoreBasics(PCVBOXCRYPTOIF pCryptoIf)
332{
333 RTTestISub("Crypto Keystore - Basics");
334
335 RTTestDisableAssertions(g_hTest);
336
337 for (uint32_t i = 0; i < RT_ELEMENTS(g_aCiphers); i++)
338 {
339 RTTestISubF("Creating a new keystore for cipher '%s'", g_aCiphers[i]);
340
341 char *pszKeystoreEnc = NULL; /**< The encoded keystore. */
342 int rc = pCryptoIf->pfnCryptoKeyStoreCreate(g_szPassword, &g_abDek[0], sizeof(g_abDek),
343 g_aCiphers[i], &pszKeystoreEnc);
344 if (RT_SUCCESS(rc))
345 {
346 uint8_t *pbKey = NULL;
347 size_t cbKey = 0;
348 char *pszCipher = NULL;
349
350 RTTestSub(g_hTest, "Trying to unlock DEK with wrong password");
351 rc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(pszKeystoreEnc, g_szPasswordWrong,
352 &pbKey, &cbKey, &pszCipher);
353 RTTESTI_CHECK_RC(rc, VERR_VD_PASSWORD_INCORRECT);
354
355 RTTestSub(g_hTest, "Trying to unlock DEK with correct password");
356 rc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(pszKeystoreEnc, g_szPassword,
357 &pbKey, &cbKey, &pszCipher);
358 RTTESTI_CHECK_RC_OK(rc);
359 if (RT_SUCCESS(rc))
360 {
361 RTTESTI_CHECK(cbKey == sizeof(g_abDek));
362 CHECK_STR(pszCipher, g_aCiphers[i]);
363 CHECK_BYTES(pbKey, &g_abDek[0], sizeof(g_abDek));
364
365 RTMemSaferFree(pbKey, cbKey);
366 }
367
368 RTMemFree(pszKeystoreEnc);
369 }
370 else
371 RTTestIFailed("Creating a new keystore failed with %Rrc", rc);
372 }
373
374 RTTestRestoreAssertions(g_hTest);
375}
376
377
378int main(int argc, char *argv[])
379{
380 /*
381 * Initialization.
382 */
383 RTEXITCODE rcExit = RTTestInitAndCreate("tstVBoxCrypto", &g_hTest);
384 if (rcExit != RTEXITCODE_SUCCESS)
385 return rcExit;
386 RTTestBanner(g_hTest);
387
388 RTTestSub(g_hTest, "Loading the cryptographic support module");
389 const char *pszModCrypto = NULL;
390 if (argc == 2)
391 {
392 /* The module to load is given on the command line. */
393 pszModCrypto = argv[1];
394 }
395 else
396 {
397 /* Try find it in the extension pack. */
398 /** @todo */
399 RTTestSkipped(g_hTest, "Getting the module from the extension pack is not implemented yet, skipping testcase");
400 }
401
402 if (pszModCrypto)
403 {
404 RTLDRMOD hLdrModCrypto = NIL_RTLDRMOD;
405 int rc = RTLdrLoad(pszModCrypto, &hLdrModCrypto);
406 if (RT_SUCCESS(rc))
407 {
408 PFNVBOXCRYPTOENTRY pfnCryptoEntry = NULL;
409 rc = RTLdrGetSymbol(hLdrModCrypto, VBOX_CRYPTO_MOD_ENTRY_POINT, (void **)&pfnCryptoEntry);
410 if (RT_SUCCESS(rc))
411 {
412 PCVBOXCRYPTOIF pCryptoIf = NULL;
413 rc = pfnCryptoEntry(&pCryptoIf);
414 if (RT_SUCCESS(rc))
415 {
416 /* Loading succeeded, now we can start real testing. */
417 tstCryptoKeyStoreBasics(pCryptoIf);
418 tstCryptoVfsBasics(pCryptoIf);
419 }
420 else
421 RTTestIFailed("Calling '%s' failed with %Rrc", VBOX_CRYPTO_MOD_ENTRY_POINT, rc);
422 }
423 else
424 RTTestIFailed("Failed to resolve entry point '%s' with %Rrc", VBOX_CRYPTO_MOD_ENTRY_POINT, rc);
425 }
426 else
427 RTTestIFailed("Failed to load the crypto module '%s' with %Rrc", pszModCrypto, rc);
428 }
429
430 return RTTestSummaryAndDestroy(g_hTest);
431}
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