VirtualBox

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

Last change on this file since 95027 was 95027, checked in by vboxsync, 3 years ago

Main/testcases/tstVBoxCrypto.cpp: Some basic testcase for the VFS file part, bugref:9955

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