Skip to content

Commit 7a97319

Browse files
authored
Support digest state export for MD5, SHA1, and SHA2 digests (microsoft#117)
* Add exportable digests * Put common functions in macro expansion * Update SslPlay to test and validate exported hash states * Update version and fix typo in readme * Turn off optimization for debug builds * Cleanup and add SHA3 * Use fixed length array for state export * Fix pipeline errors * Revert "Turn off optimization for debug builds" This reverts commit 520bd0b. * Fix SslPlay argument parsing
1 parent 2988e46 commit 7a97319

File tree

3 files changed

+407
-39
lines changed

3 files changed

+407
-39
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
cmake_minimum_required(VERSION 3.13.0)
22

33
project(SymCrypt-OpenSSL
4-
VERSION 1.8.0
4+
VERSION 1.9.0
55
DESCRIPTION "The SymCrypt engine and provider for OpenSSL (SCOSSL)"
66
HOMEPAGE_URL "https://github.com/microsoft/SymCrypt-OpenSSL")
77

SslPlay/SslPlay.cpp

Lines changed: 278 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
#include <openssl/kdf.h>
1414
#include "e_scossl.h"
1515

16+
#if OPENSSL_VERSION_MAJOR == 3
17+
#include <openssl/provider.h>
18+
#endif
19+
1620
BIO *bio_err = NULL;
1721

1822
// By default exit Sslplay application if an error is encountered
@@ -1126,7 +1130,7 @@ void TestRsaEvpAll()
11261130

11271131
}
11281132

1129-
bool TestDigest(const char* digestname)
1133+
bool TestDigest(const char* digestname, const char *expected)
11301134
{
11311135
bool result = false;
11321136
EVP_MD_CTX *mdctx;
@@ -1161,24 +1165,208 @@ bool TestDigest(const char* digestname)
11611165
for (i = 0; i < md_len; i++)
11621166
printf("%02x", md_value[i]);
11631167
printf("\n");
1168+
1169+
if (memcmp(md_value, expected, md_len) != 0)
1170+
{
1171+
handleError("Result does not match expected value");
1172+
goto end;
1173+
}
1174+
11641175
result = true;
11651176
end:
11661177
printf("%s", SeparatorLine);
11671178
return result;
11681179
}
11691180

1170-
void TestDigests()
1181+
#if OPENSSL_VERSION_MAJOR == 3
1182+
bool TestDigestImportExport(const char *digestname, const char *expected, size_t export_state_size)
11711183
{
1184+
bool result = false;
1185+
EVP_MD *md = NULL;
1186+
EVP_MD_CTX *mdctx;
11721187
char mess1[] = "Test Message1234567";
11731188
char mess2[] = "Hello World";
1174-
unsigned int md_len=32, i;
1189+
unsigned char md_value[EVP_MAX_MD_SIZE];
1190+
unsigned int md_len, i;
1191+
1192+
const OSSL_PROVIDER *provider;
1193+
const char *providername;
1194+
const OSSL_PARAM *capableParams = NULL;
1195+
BYTE exported_state[export_state_size];
1196+
int recompute_checksum = 0;
1197+
OSSL_PARAM params[3] = {
1198+
OSSL_PARAM_construct_octet_string("state", exported_state, export_state_size),
1199+
OSSL_PARAM_construct_int("recompute_checksum", &recompute_checksum),
1200+
OSSL_PARAM_construct_end()
1201+
};
1202+
1203+
printf("\nTestDigestImportExport: %s\n\n", digestname);
1204+
1205+
md = EVP_MD_fetch(nullptr, digestname, "provider=symcryptprovider");
1206+
if (md == NULL)
1207+
{
1208+
printf("No Digest found for %s\n", digestname);
1209+
goto end;
1210+
}
1211+
1212+
printf("Command EVP_MD_CTX_new\n");
1213+
mdctx = EVP_MD_CTX_new();
1214+
printf("Command EVP_DigestInit_ex\n");
1215+
EVP_DigestInit_ex2(mdctx, md, NULL);
1216+
1217+
// Check capabilities
1218+
capableParams = EVP_MD_CTX_gettable_params(mdctx);
1219+
if (OSSL_PARAM_locate_const(capableParams, "state") == NULL)
1220+
{
1221+
handleError("state parameter not gettable");
1222+
goto end;
1223+
}
11751224

1176-
TestDigest("MD5");
1177-
TestDigest("SHA1");
1178-
TestDigest("SHA224");
1179-
TestDigest("SHA256");
1180-
TestDigest("SHA384");
1181-
TestDigest("SHA512");
1225+
capableParams = EVP_MD_CTX_settable_params(mdctx);
1226+
if (OSSL_PARAM_locate_const(capableParams, "state") == NULL ||
1227+
OSSL_PARAM_locate_const(capableParams, "recompute_checksum") == NULL)
1228+
{
1229+
handleError("state parameter not settable");
1230+
goto end;
1231+
}
1232+
1233+
printf("Command EVP_DigestUpdate\n");
1234+
EVP_DigestUpdate(mdctx, mess1, strlen(mess1));
1235+
1236+
// Export state
1237+
printf("Command EVP_MD_CTX_get_params\n");
1238+
EVP_MD_CTX_get_params(mdctx, params);
1239+
printf("Command EVP_MD_CTX_free\n");
1240+
EVP_MD_CTX_free(mdctx);
1241+
1242+
// New context with imported state supplied
1243+
printf("Command EVP_MD_CTX_new\n");
1244+
mdctx = EVP_MD_CTX_new();
1245+
printf("Command EVP_DigestInit_ex\n");
1246+
EVP_DigestInit_ex2(mdctx, md, params);
1247+
1248+
printf("Command EVP_DigestUpdate\n");
1249+
EVP_DigestUpdate(mdctx, mess2, strlen(mess2));
1250+
printf("Command EVP_DigestFinal_ex\n");
1251+
EVP_DigestFinal_ex(mdctx, md_value, &md_len);
1252+
printf("Command EVP_MD_CTX_free\n");
1253+
EVP_MD_CTX_free(mdctx);
1254+
1255+
printf("Digest (Original checksum) (%s) : \t", digestname);
1256+
for (i = 0; i < md_len; i++)
1257+
printf("%02x", md_value[i]);
1258+
printf("\n");
1259+
1260+
if (memcmp(md_value, expected, md_len) != 0)
1261+
{
1262+
handleError("Result does not match expected value");
1263+
goto end;
1264+
}
1265+
1266+
// Clear last 8 bytes of state (Marvin32 checksum of SymCrypt export)
1267+
OPENSSL_cleanse(exported_state + export_state_size - 8, 8);
1268+
recompute_checksum = 1;
1269+
1270+
printf("Command EVP_MD_CTX_new\n");
1271+
mdctx = EVP_MD_CTX_new();
1272+
printf("Command EVP_DigestInit_ex\n");
1273+
EVP_DigestInit_ex2(mdctx, md, params);
1274+
1275+
printf("Command EVP_DigestUpdate\n");
1276+
EVP_DigestUpdate(mdctx, mess2, strlen(mess2));
1277+
printf("Command EVP_DigestFinal_ex\n");
1278+
EVP_DigestFinal_ex(mdctx, md_value, &md_len);
1279+
printf("Command EVP_MD_CTX_free\n");
1280+
EVP_MD_CTX_free(mdctx);
1281+
1282+
printf("Digest (Recomputed checksum) (%s) : \t", digestname);
1283+
for (i = 0; i < md_len; i++)
1284+
printf("%02x", md_value[i]);
1285+
printf("\n");
1286+
1287+
if (memcmp(md_value, expected, md_len) != 0)
1288+
{
1289+
handleError("Result does not match expected value");
1290+
goto end;
1291+
}
1292+
1293+
result = true;
1294+
1295+
end:
1296+
EVP_MD_free(md);
1297+
1298+
printf("%s", SeparatorLine);
1299+
return result;
1300+
}
1301+
#endif
1302+
1303+
typedef struct {
1304+
const char *digestname;
1305+
const char *expected;
1306+
size_t export_state_size;
1307+
} SCOSSL_DIGEST_TEST_CASE;
1308+
1309+
// TestDigests tests hash state export and import if supported.
1310+
// This functionality is added by the SymCrypt provider and is
1311+
// only available when the engine is not loaded. If the engine
1312+
// is loaded, OpenSSL will use it for digests and the import/export
1313+
// functionality will not be available.
1314+
void TestDigests(bool useEngine)
1315+
{
1316+
char mess1[] = "Test Message1234567";
1317+
char mess2[] = "Hello World";
1318+
char expected_md_value[EVP_MAX_MD_SIZE];
1319+
unsigned int md_len=32, i, j;
1320+
const char *digestname, *expected;
1321+
1322+
const SCOSSL_DIGEST_TEST_CASE digest_test_cases[] =
1323+
{
1324+
{"MD5", "4d1074949b1b41311eff270f84804433",
1325+
SYMCRYPT_MD5_STATE_EXPORT_SIZE},
1326+
{"SHA1", "fc5f088a0395c8ad887ff531333b0405a7dbb098",
1327+
SYMCRYPT_SHA1_STATE_EXPORT_SIZE},
1328+
{"SHA224", "6fe30848035b96665e1f02eae7b2bcafa600e466d80bc1b93b6094cc",
1329+
SYMCRYPT_SHA224_STATE_EXPORT_SIZE},
1330+
{"SHA256", "6810f8a56670662c0e7a10ca415f270eae0aeb1f7b153d02668ae5e7143fcc40",
1331+
SYMCRYPT_SHA256_STATE_EXPORT_SIZE},
1332+
{"SHA384", "1f051f47c1b29006383bbc07f0f14b4cb81bac3c78604e2164e89acddf701264f9b5664686412b17624f67baf3af28a7",
1333+
SYMCRYPT_SHA384_STATE_EXPORT_SIZE},
1334+
{"SHA512", "070820b341fd2a45d28b390e67e3f3e4ce30c5b345540e1f587ccfbe94e22c3079f6c28e906b95871a21d1871a198c581ee9a71b7bcaec5f542d026f5dc87183",
1335+
SYMCRYPT_SHA512_STATE_EXPORT_SIZE},
1336+
#if OPENSSL_VERSION_MAJOR == 3
1337+
{"SHA512-224", "ff3da53a61923d460f85f98de06c6312092d0be3712ee611c4fe0a19",
1338+
SYMCRYPT_SHA512_224_STATE_EXPORT_SIZE},
1339+
{"SHA512-256", "036dbd97db1e37aabe6ded8ef9ead203e9adb02ad5596ac5af072dd7374993a0",
1340+
SYMCRYPT_SHA512_256_STATE_EXPORT_SIZE},
1341+
{"SHA3-224", "489a032b8923a05eca5b40f2ed9838f218c65bd082acc48fa2067213",
1342+
SYMCRYPT_SHA3_224_STATE_EXPORT_SIZE},
1343+
{"SHA3-256", "375e793a6d4e4947658e78cb697789434b8279feb2ec9595d03e44473ac478f6",
1344+
SYMCRYPT_SHA3_256_STATE_EXPORT_SIZE},
1345+
{"SHA3-384", "1d47002a9e96d5b6bdd70d476fd2038e50ac3eb0d4202b4eb988f02185fbb9c85cb7ed62804ddaff894e84d62e5832f2",
1346+
SYMCRYPT_SHA3_384_STATE_EXPORT_SIZE},
1347+
{"SHA3-512", "bf63544ae59243a5419a3ff5f598352eb1409d41dc746c9e9d5f258cddaff4e7f7b9d9ae13e90eb07f27e4e157b3fcf796f6554732a2e78a621f7313aba827f3",
1348+
SYMCRYPT_SHA3_512_STATE_EXPORT_SIZE},
1349+
#endif
1350+
};
1351+
1352+
for (i = 0; i < sizeof(digest_test_cases)/sizeof(digest_test_cases[0]); i++)
1353+
{
1354+
// Convert expected hex string to binary
1355+
for (int j = 0; digest_test_cases[i].expected[j] != '\0' && digest_test_cases[i].expected[j + 1] != '\0'; j += 2)
1356+
{
1357+
expected_md_value[j / 2] = OPENSSL_hexchar2int(digest_test_cases[i].expected[j]) << 4 |
1358+
OPENSSL_hexchar2int(digest_test_cases[i].expected[j + 1]);
1359+
}
1360+
1361+
TestDigest(digest_test_cases[i].digestname, expected_md_value);
1362+
1363+
#if OPENSSL_VERSION_MAJOR == 3
1364+
if (!useEngine && digest_test_cases[i].export_state_size > 0)
1365+
{
1366+
TestDigestImportExport(digest_test_cases[i].digestname, expected_md_value, digest_test_cases[i].export_state_size);
1367+
}
1368+
#endif
1369+
}
11821370

11831371
unsigned char md1[SHA256_DIGEST_LENGTH]; // 32 bytes
11841372
SHA256_CTX context;
@@ -1377,15 +1565,15 @@ void TestAesXts()
13771565
{
13781566
unsigned char plaintext[8192];
13791567
int plaintext_len = 64;
1380-
unsigned char iv[8];
1568+
unsigned char iv[16];
13811569
unsigned char key[64];
13821570

13831571
while(!RAND_bytes(key, 64));
1384-
while(!RAND_bytes(iv, 8));
1572+
while(!RAND_bytes(iv, 16));
13851573
while(!RAND_bytes(plaintext, plaintext_len));
13861574

1387-
TestAesCipher("EVP_aes_128_xts", EVP_aes_128_xts(), key, 32, iv, 8, plaintext, plaintext_len);
1388-
TestAesCipher("EVP_aes_256_xts", EVP_aes_256_xts(), key, 64, iv, 8, plaintext, plaintext_len);
1575+
TestAesCipher("EVP_aes_128_xts", EVP_aes_128_xts(), key, 32, iv, 16, plaintext, plaintext_len);
1576+
TestAesCipher("EVP_aes_256_xts", EVP_aes_256_xts(), key, 64, iv, 16, plaintext, plaintext_len);
13891577

13901578
printf("%s", SeparatorLine);
13911579
return;
@@ -2300,19 +2488,85 @@ void TestSshKdf(void)
23002488

23012489
int main(int argc, char** argv)
23022490
{
2491+
#if OPENSSL_VERSION_MAJOR == 3
2492+
OSSL_PROVIDER *symcrypt_provider = NULL;
2493+
#endif
23032494
int scossl_log_level = SCOSSL_LOG_LEVEL_NO_CHANGE;
23042495
int scossl_ossl_ERR_level = SCOSSL_LOG_LEVEL_NO_CHANGE;
2305-
if (argc >= 2) {
2306-
scossl_log_level = atoi(argv[1]);
2307-
}
2308-
if (argc >= 3) {
2309-
scossl_ossl_ERR_level = atoi(argv[2]);
2496+
bool useEngine = true;
2497+
2498+
for (int i = 1; i < argc; i++)
2499+
{
2500+
if (strcmp(argv[i], "--log-level") == 0)
2501+
{
2502+
if (argc < ++i)
2503+
{
2504+
printf("Missing log level");
2505+
return 0;
2506+
}
2507+
2508+
scossl_log_level = atoi(argv[i]);
2509+
}
2510+
else if (strcmp(argv[i], "--err-level") == 0)
2511+
{
2512+
if (argc < ++i)
2513+
{
2514+
printf("Missing log level");
2515+
return 0;
2516+
}
2517+
2518+
scossl_ossl_ERR_level = atoi(argv[i]);
2519+
}
2520+
#if OPENSSL_VERSION_MAJOR == 3
2521+
else if (strcmp(argv[i], "--provider-path") == 0)
2522+
{
2523+
if (argc < ++i)
2524+
{
2525+
printf("Missing provider path");
2526+
return 0;
2527+
}
2528+
2529+
if (!OSSL_PROVIDER_set_default_search_path(NULL, argv[i]))
2530+
{
2531+
{
2532+
handleOpenSSLError("Failed to set provider path");
2533+
return 0;
2534+
}
2535+
}
2536+
2537+
if ((symcrypt_provider = OSSL_PROVIDER_load(NULL, "symcryptprovider")) == NULL)
2538+
{
2539+
handleOpenSSLError("Failed to load symcrypt provider");
2540+
return 0;
2541+
}
2542+
}
2543+
else if (strcmp(argv[i], "--no-engine") == 0)
2544+
{
2545+
useEngine = false;
2546+
}
2547+
#endif
2548+
else if (strcmp(argv[i], "--help") == 0)
2549+
{
2550+
printf("Usage: SslPlay <options>\n");
2551+
printf("Options:\n");
2552+
printf(" --log-level <log level> Specify the SCOSSL logging level\n");
2553+
printf(" --err-level <err level> Specify the SCOSSL error logging level\n");
2554+
#if OPENSSL_VERSION_MAJOR == 3
2555+
printf(" --provider-path <provider path> Specify a directory to locate the symcrypt provider\n");
2556+
printf(" --no-engine Disable the SymCrypt engine for testing\n");
2557+
#endif
2558+
return 0;
2559+
}
23102560
}
2561+
23112562
SCOSSL_set_trace_level(scossl_log_level, scossl_ossl_ERR_level);
2312-
SCOSSL_ENGINE_Initialize();
2563+
if (useEngine)
2564+
{
2565+
SCOSSL_ENGINE_Initialize();
2566+
}
23132567
bio_err = BIO_new_fp(stdout, BIO_NOCLOSE);
23142568

2315-
TestDigests();
2569+
TestDigests(useEngine);
23162570
TestCiphers();
23172571
TestHKDF();
23182572
TestTls1Prf();
@@ -2325,6 +2579,10 @@ int main(int argc, char** argv)
23252579
TestSshKdf();
23262580
#endif
23272581

2582+
#if OPENSSL_VERSION_MAJOR == 3
2583+
OSSL_PROVIDER_unload(symcrypt_provider);
2584+
#endif
2585+
23282586
BIO_free(bio_err);
23292587
return 0;
23302588
}

0 commit comments

Comments
 (0)