397 lines
9.8 KiB
C
397 lines
9.8 KiB
C
/*
|
|
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* See the COPYRIGHT file distributed with this work for additional
|
|
* information regarding copyright ownership.
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#if HAVE_CMOCKA
|
|
|
|
#include <stdarg.h>
|
|
#include <stddef.h>
|
|
#include <setjmp.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#define UNIT_TESTING
|
|
#include <cmocka.h>
|
|
|
|
#include <isc/util.h>
|
|
#include <isc/print.h>
|
|
#include <isc/string.h>
|
|
|
|
#include "dnstest.h"
|
|
|
|
#ifdef HAVE_OPENSSL_GOST
|
|
#include "../dst_gost.h"
|
|
#include <openssl/err.h>
|
|
#include <openssl/objects.h>
|
|
#include <openssl/rsa.h>
|
|
#include <openssl/engine.h>
|
|
#include <openssl/bn.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_PKCS11_GOST
|
|
#include "../dst_gost.h"
|
|
#include <pk11/internal.h>
|
|
#define WANT_GOST_PARAMS
|
|
#include <pk11/constants.h>
|
|
#include <pkcs11/pkcs11.h>
|
|
#endif
|
|
|
|
#if defined(HAVE_OPENSSL_GOST) || defined(HAVE_PKCS11_GOST)
|
|
static int
|
|
_setup(void **state) {
|
|
isc_result_t result;
|
|
|
|
UNUSED(state);
|
|
|
|
result = dns_test_begin(NULL, false);
|
|
assert_int_equal(result, ISC_R_SUCCESS);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
_teardown(void **state) {
|
|
UNUSED(state);
|
|
|
|
dns_test_end();
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Test data from Wikipedia GOST (hash function)
|
|
*/
|
|
|
|
unsigned char digest[ISC_GOST_DIGESTLENGTH];
|
|
unsigned char buffer[1024];
|
|
const char *s;
|
|
char str[2 * ISC_GOST_DIGESTLENGTH + 3];
|
|
int i = 0;
|
|
|
|
/*
|
|
* Precondition: a hexadecimal number in *d, the length of that number in len,
|
|
* and a pointer to a character array to put the output (*out).
|
|
* Postcondition: A String representation of the given hexadecimal number is
|
|
* placed into the array *out
|
|
*
|
|
* 'out' MUST point to an array of at least len * 2 + 1
|
|
*
|
|
* Return values: ISC_R_SUCCESS if the operation is sucessful
|
|
*/
|
|
static isc_result_t
|
|
tohexstr(unsigned char *d, unsigned int len, char *out, size_t out_size) {
|
|
char c_ret[] = "AA";
|
|
unsigned int j;
|
|
|
|
out[0] = '\0';
|
|
strlcat(out, "0x", out_size);
|
|
for (j = 0; j < len; j++) {
|
|
snprintf(c_ret, sizeof(c_ret), "%02X", d[j]);
|
|
strlcat(out, c_ret, out_size);
|
|
}
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
|
|
#define TEST_INPUT(x) (x), sizeof(x)-1
|
|
|
|
typedef struct hash_testcase {
|
|
const char *input;
|
|
size_t input_len;
|
|
const char *result;
|
|
int repeats;
|
|
} hash_testcase_t;
|
|
|
|
/* GOST R 34.11-94 examples from Wikipedia */
|
|
static void
|
|
isc_gost_md(void **state) {
|
|
isc_gost_t gost;
|
|
isc_result_t result;
|
|
|
|
UNUSED(state);
|
|
|
|
/*
|
|
* These are the various test vectors. All of these are passed
|
|
* through the hash function and the results are compared to the
|
|
* result specified here.
|
|
*/
|
|
hash_testcase_t testcases[] = {
|
|
/* Test 1 */
|
|
{
|
|
TEST_INPUT(""),
|
|
"0x981E5F3CA30C841487830F84FB433E1"
|
|
"3AC1101569B9C13584AC483234CD656C0",
|
|
1
|
|
},
|
|
/* Test 2 */
|
|
{
|
|
TEST_INPUT("a"),
|
|
"0xE74C52DD282183BF37AF0079C9F7805"
|
|
"5715A103F17E3133CEFF1AACF2F403011",
|
|
1
|
|
},
|
|
/* Test 3 */
|
|
{
|
|
TEST_INPUT("abc"),
|
|
"0xB285056DBF18D7392D7677369524DD1"
|
|
"4747459ED8143997E163B2986F92FD42C",
|
|
1
|
|
},
|
|
/* Test 4 */
|
|
{
|
|
TEST_INPUT("message digest"),
|
|
"0xBC6041DD2AA401EBFA6E9886734174F"
|
|
"EBDB4729AA972D60F549AC39B29721BA0",
|
|
1
|
|
},
|
|
/* Test 5 */
|
|
{
|
|
TEST_INPUT("The quick brown fox jumps "
|
|
"over the lazy dog"),
|
|
"0x9004294A361A508C586FE53D1F1B027"
|
|
"46765E71B765472786E4770D565830A76",
|
|
1
|
|
},
|
|
|
|
/* Test 6 */
|
|
{
|
|
TEST_INPUT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcde"
|
|
"fghijklmnopqrstuvwxyz0123456789"),
|
|
"0x73B70A39497DE53A6E08C67B6D4DB85"
|
|
"3540F03E9389299D9B0156EF7E85D0F61",
|
|
1
|
|
},
|
|
/* Test 7 */
|
|
{
|
|
TEST_INPUT("1234567890123456789012345678901"
|
|
"2345678901234567890123456789012"
|
|
"345678901234567890"),
|
|
"0x6BC7B38989B28CF93AE8842BF9D7529"
|
|
"05910A7528A61E5BCE0782DE43E610C90",
|
|
1
|
|
},
|
|
/* Test 8 */
|
|
{
|
|
TEST_INPUT("This is message, length=32 bytes"),
|
|
"0x2CEFC2F7B7BDC514E18EA57FA74FF35"
|
|
"7E7FA17D652C75F69CB1BE7893EDE48EB",
|
|
1
|
|
},
|
|
/* Test 9 */
|
|
{
|
|
TEST_INPUT("Suppose the original message "
|
|
"has length = 50 bytes"),
|
|
"0xC3730C5CBCCACF915AC292676F21E8B"
|
|
"D4EF75331D9405E5F1A61DC3130A65011",
|
|
1
|
|
},
|
|
/* Test 10 */
|
|
{
|
|
TEST_INPUT("U") /* times 128 */,
|
|
"0x1C4AC7614691BBF427FA2316216BE8F"
|
|
"10D92EDFD37CD1027514C1008F649C4E8",
|
|
128
|
|
},
|
|
/* Test 11 */
|
|
{
|
|
TEST_INPUT("a") /* times 1000000 */,
|
|
"0x8693287AA62F9478F7CB312EC0866B6"
|
|
"C4E4A0F11160441E8F4FFCD2715DD554F",
|
|
1000000
|
|
},
|
|
{ NULL, 0, NULL, 1 }
|
|
};
|
|
|
|
hash_testcase_t *testcase = testcases;
|
|
|
|
while (testcase->input != NULL && testcase->result != NULL) {
|
|
result = isc_gost_init(&gost);
|
|
assert_int_equal(result, ISC_R_SUCCESS);
|
|
for(i = 0; i < testcase->repeats; i++) {
|
|
result = isc_gost_update(&gost,
|
|
(const uint8_t *) testcase->input,
|
|
testcase->input_len);
|
|
assert_int_equal(result, ISC_R_SUCCESS);
|
|
}
|
|
result = isc_gost_final(&gost, digest);
|
|
assert_int_equal(result, ISC_R_SUCCESS);
|
|
tohexstr(digest, ISC_GOST_DIGESTLENGTH, str, sizeof(str));
|
|
assert_string_equal(str, testcase->result);
|
|
|
|
testcase++;
|
|
}
|
|
}
|
|
|
|
/* GOST R 34.10-2001 private key */
|
|
static void
|
|
isc_gost_private(void **state) {
|
|
unsigned char privraw[31] = {
|
|
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
|
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
|
|
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
|
|
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e
|
|
};
|
|
#ifdef HAVE_OPENSSL_GOST
|
|
unsigned char rbuf[32];
|
|
unsigned char privasn1[70] = {
|
|
0x30, 0x44, 0x02, 0x01, 0x00, 0x30, 0x1c, 0x06,
|
|
0x06, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x13, 0x30,
|
|
0x12, 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02,
|
|
0x23, 0x01, 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02,
|
|
0x02, 0x1e, 0x01, 0x04, 0x21, 0x02, 0x1f, 0x01,
|
|
0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
|
|
0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
|
|
0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
|
|
0x1a, 0x1b, 0x1c, 0x1d, 0x1e
|
|
};
|
|
unsigned char abuf[71];
|
|
unsigned char gost_dummy_key[71] = {
|
|
0x30, 0x45, 0x02, 0x01, 0x00, 0x30, 0x1c, 0x06,
|
|
0x06, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x13, 0x30,
|
|
0x12, 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02,
|
|
0x23, 0x01, 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02,
|
|
0x02, 0x1e, 0x01, 0x04, 0x22, 0x02, 0x20, 0x1b,
|
|
0x3f, 0x94, 0xf7, 0x1a, 0x5f, 0x2f, 0xe7, 0xe5,
|
|
0x74, 0x0b, 0x8c, 0xd4, 0xb7, 0x18, 0xdd, 0x65,
|
|
0x68, 0x26, 0xd1, 0x54, 0xfb, 0x77, 0xba, 0x63,
|
|
0x72, 0xd9, 0xf0, 0x63, 0x87, 0xe0, 0xd6
|
|
};
|
|
EVP_PKEY *pkey;
|
|
EC_KEY *eckey;
|
|
BIGNUM *privkey;
|
|
const BIGNUM *privkey1;
|
|
const unsigned char *p;
|
|
int len;
|
|
unsigned char *q;
|
|
|
|
UNUSED(state);
|
|
|
|
/* raw parse */
|
|
privkey = BN_bin2bn(privraw, (int) sizeof(privraw), NULL);
|
|
assert_non_null(privkey);
|
|
p = gost_dummy_key;
|
|
pkey = NULL;
|
|
assert_non_null(d2i_PrivateKey(NID_id_GostR3410_2001, &pkey, &p,
|
|
(long) sizeof(gost_dummy_key)));
|
|
assert_non_null(pkey);
|
|
assert_int_equal(EVP_PKEY_bits(pkey), 256);
|
|
eckey = EVP_PKEY_get0(pkey);
|
|
assert_non_null(eckey);
|
|
assert_int_equal(EC_KEY_set_private_key(eckey, privkey), 1);
|
|
BN_clear_free(privkey);
|
|
|
|
/* asn1 tofile */
|
|
len = i2d_PrivateKey(pkey, NULL);
|
|
assert_int_equal(len, 70);
|
|
q = abuf;
|
|
assert_int_equal(i2d_PrivateKey(pkey, &q), len);
|
|
assert_int_equal(memcmp(abuf, privasn1, len), 0);
|
|
EVP_PKEY_free(pkey);
|
|
|
|
/* asn1 parse */
|
|
p = privasn1;
|
|
pkey = NULL;
|
|
assert_non_null(d2i_PrivateKey(NID_id_GostR3410_2001, &pkey, &p,
|
|
(long) len));
|
|
assert_non_null(pkey);
|
|
eckey = EVP_PKEY_get0(pkey);
|
|
assert_non_null(eckey);
|
|
privkey1 = EC_KEY_get0_private_key(eckey);
|
|
len = BN_num_bytes(privkey1);
|
|
assert_int_equal(len, 31);
|
|
assert_int_equal(BN_bn2bin(privkey1, rbuf), len);
|
|
assert_memory_equal(rbuf, privraw, len);
|
|
#else
|
|
CK_BBOOL truevalue = TRUE;
|
|
CK_BBOOL falsevalue = FALSE;
|
|
CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY;
|
|
CK_KEY_TYPE keyType = CKK_GOSTR3410;
|
|
CK_ATTRIBUTE keyTemplate[] =
|
|
{
|
|
{ CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
|
|
{ CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
|
|
{ CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
|
|
{ CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
|
|
{ CKA_SENSITIVE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
|
|
{ CKA_SIGN, &truevalue, (CK_ULONG) sizeof(truevalue) },
|
|
{ CKA_VALUE, privraw, sizeof(privraw) },
|
|
{ CKA_GOSTR3410_PARAMS, pk11_gost_a_paramset,
|
|
(CK_ULONG) sizeof(pk11_gost_a_paramset) },
|
|
{ CKA_GOSTR3411_PARAMS, pk11_gost_paramset,
|
|
(CK_ULONG) sizeof(pk11_gost_paramset) }
|
|
};
|
|
CK_MECHANISM mech = { CKM_GOSTR3410_WITH_GOSTR3411, NULL, 0 };
|
|
CK_BYTE sig[64];
|
|
CK_ULONG siglen;
|
|
pk11_context_t pk11_ctx;
|
|
|
|
/* create the private key */
|
|
memset(&pk11_ctx, 0, sizeof(pk11_ctx));
|
|
assert_int_equal(pk11_get_session(&pk11_ctx, OP_GOST, true,
|
|
false, false, NULL,
|
|
pk11_get_best_token(OP_GOST)),
|
|
ISC_R_SUCCESS);
|
|
pk11_ctx.object = CK_INVALID_HANDLE;
|
|
pk11_ctx.ontoken = false;
|
|
assert_int_equal(pkcs_C_CreateObject(pk11_ctx.session, keyTemplate,
|
|
(CK_ULONG) 9,
|
|
&pk11_ctx.object),
|
|
CKR_OK);
|
|
assert_int_not_equal(pk11_ctx.object, CK_INVALID_HANDLE);
|
|
|
|
/* sign something */
|
|
assert_int_equal(pkcs_C_SignInit(pk11_ctx.session, &mech,
|
|
pk11_ctx.object),
|
|
CKR_OK);
|
|
siglen = 0;
|
|
assert_int_equal(pkcs_C_Sign(pk11_ctx.session, sig, 64,
|
|
NULL, &siglen),
|
|
CKR_OK);
|
|
assert_int_equal(siglen, 64);
|
|
assert_int_equal(pkcs_C_Sign(pk11_ctx.session, sig, 64,
|
|
sig, &siglen),
|
|
CKR_OK);
|
|
assert_int_equal(siglen, 64);
|
|
#endif
|
|
};
|
|
#endif
|
|
|
|
int
|
|
main(void) {
|
|
#if defined(HAVE_OPENSSL_GOST) || defined(HAVE_PKCS11_GOST)
|
|
const struct CMUnitTest tests[] = {
|
|
cmocka_unit_test_setup_teardown(isc_gost_md,
|
|
_setup, _teardown),
|
|
cmocka_unit_test_setup_teardown(isc_gost_private,
|
|
_setup, _teardown),
|
|
};
|
|
|
|
return (cmocka_run_group_tests(tests, dns_test_init, dns_test_final));
|
|
#else
|
|
print_message("1..0 # Skip GOST is unavailable");
|
|
#endif
|
|
}
|
|
|
|
#else /* HAVE_CMOCKA */
|
|
|
|
#include <stdio.h>
|
|
|
|
int
|
|
main(void) {
|
|
printf("1..0 # Skipped: cmocka not available\n");
|
|
return (0);
|
|
}
|
|
|
|
#endif
|