Time-Stamp Protocol (RFC 3161)

Implementation in wolfCrypt
OpenSSL compatibility layer in wolfSSL
Added tests, certificates, examples.
This commit is contained in:
Sean Parkinson
2026-06-25 21:59:57 +10:00
parent 0ecc0c5973
commit ae023a5643
73 changed files with 18311 additions and 27 deletions
+1
View File
@@ -11,6 +11,7 @@ include examples/sctp/include.am
include examples/configs/include.am
include examples/asn1/include.am
include examples/pem/include.am
include examples/tsp/include.am
include examples/ocsp_responder/include.am
include examples/tls13/include.am
EXTRA_DIST += examples/README.md
+21
View File
@@ -0,0 +1,21 @@
# vim:ft=automake
# included from Top Level Makefile.am
# All paths should be given relative to the root
if BUILD_EXAMPLE_TSP
noinst_PROGRAMS += examples/tsp/tsp_query examples/tsp/tsp_reply \
examples/tsp/tsp_verify
examples_tsp_tsp_query_SOURCES = examples/tsp/tsp_query.c
examples_tsp_tsp_query_LDADD = src/libwolfssl@LIBSUFFIX@.la $(LIB_STATIC_ADD)
examples_tsp_tsp_query_DEPENDENCIES = src/libwolfssl@LIBSUFFIX@.la
examples_tsp_tsp_reply_SOURCES = examples/tsp/tsp_reply.c
examples_tsp_tsp_reply_LDADD = src/libwolfssl@LIBSUFFIX@.la $(LIB_STATIC_ADD)
examples_tsp_tsp_reply_DEPENDENCIES = src/libwolfssl@LIBSUFFIX@.la
examples_tsp_tsp_verify_SOURCES = examples/tsp/tsp_verify.c
examples_tsp_tsp_verify_LDADD = src/libwolfssl@LIBSUFFIX@.la $(LIB_STATIC_ADD)
examples_tsp_tsp_verify_DEPENDENCIES = src/libwolfssl@LIBSUFFIX@.la
endif
+232
View File
@@ -0,0 +1,232 @@
/* tsp_query.c
*
* Copyright (C) 2006-2026 wolfSSL Inc.
*
* This file is part of wolfSSL.
*
* wolfSSL is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfSSL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
/* Time-Stamp Protocol (RFC 3161) example: create a request.
*
* tsp_query <file> <request.tsq>
* Hash the file with SHA-256 and write a time-stamp request.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifndef WOLFSSL_USER_SETTINGS
#include <wolfssl/options.h>
#endif
#include <wolfssl/wolfcrypt/settings.h>
#include <wolfssl/wolfcrypt/tsp.h>
#include <wolfssl/wolfcrypt/hash.h>
#include <wolfssl/wolfcrypt/sha256.h>
#include <wolfssl/wolfcrypt/random.h>
#include <wolfssl/wolfcrypt/error-crypt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && \
(!defined(NO_RSA) || defined(HAVE_ECC)) && \
!defined(NO_SHA256) && !defined(WC_NO_RNG) && !defined(NO_FILESYSTEM) && \
defined(WOLFSSL_TSP_REQUESTER)
/* Number of random bytes in a nonce. */
#define TSP_NUM_SZ 8
/* Maximum size of an encoded time-stamp request - a hash imprint, nonce and
* a few small fields. */
#ifndef WC_TSP_MAX_REQ_SZ
#define WC_TSP_MAX_REQ_SZ 512
#endif
/* Size of the buffer used to hash a file a chunk at a time. */
#ifndef WC_TSP_HASH_CHUNK_SZ
#define WC_TSP_HASH_CHUNK_SZ 256
#endif
/* Local variables larger than 63 bytes - big buffers and big structures - are
* kept off the stack. They are allocated from the heap, unless dynamic memory
* is not available (WOLFSSL_NO_MALLOC) in which case they go on the stack.
* WOLFSSL_SMALL_STACK uses the heap path; it never co-exists with
* WOLFSSL_NO_MALLOC. 'name' is always used as a pointer.
*
* TSP_DECL declares, TSP_ALLOC allocates (running 'fail' on failure) and
* TSP_FREE releases an array of 'cnt' items of 'type' (cnt is 1 for a single
* object). */
#ifdef WOLFSSL_NO_MALLOC
#define TSP_DECL(type, name, cnt) type name[cnt]
#define TSP_ALLOC(type, name, cnt, fail) WC_DO_NOTHING
#define TSP_FREE(name) WC_DO_NOTHING
#else
#define TSP_DECL(type, name, cnt) type* name = NULL
#define TSP_ALLOC(type, name, cnt, fail) \
do { \
(name) = (type*)XMALLOC(sizeof(type) * (cnt), NULL, \
DYNAMIC_TYPE_TMP_BUFFER); \
if ((name) == NULL) { \
fail; \
} \
} while (0)
#define TSP_FREE(name) \
XFREE((name), NULL, DYNAMIC_TYPE_TMP_BUFFER)
#endif
/* Hash a file with SHA-256 a chunk at a time so the whole file need not be
* held in memory. */
static int tsp_hash_file(const char* name, byte* hash)
{
int ret = -1;
FILE* f = NULL;
TSP_DECL(wc_Sha256, sha, 1);
TSP_DECL(byte, buf, WC_TSP_HASH_CHUNK_SZ);
size_t n;
TSP_ALLOC(wc_Sha256, sha, 1, goto done);
TSP_ALLOC(byte, buf, WC_TSP_HASH_CHUNK_SZ, goto done);
f = fopen(name, "rb");
if (f == NULL) {
fprintf(stderr, "failed to open %s\n", name);
goto done;
}
if (wc_InitSha256(sha) == 0) {
ret = 0;
while ((n = fread(buf, 1, WC_TSP_HASH_CHUNK_SZ, f)) > 0) {
if (wc_Sha256Update(sha, buf, (word32)n) != 0) {
ret = -1;
break;
}
}
if ((ret == 0) && (ferror(f) != 0))
ret = -1;
if (ret == 0)
ret = (wc_Sha256Final(sha, hash) == 0) ? 0 : -1;
wc_Sha256Free(sha);
}
done:
if (f != NULL)
fclose(f);
TSP_FREE(sha);
TSP_FREE(buf);
return ret;
}
/* Write a buffer to a file. */
static int tsp_write_file(const char* name, const byte* data, word32 sz)
{
int ret = -1;
FILE* f;
f = fopen(name, "wb");
if (f == NULL) {
fprintf(stderr, "failed to open %s\n", name);
return -1;
}
if (fwrite(data, 1, sz, f) == sz) {
ret = 0;
}
fclose(f);
return ret;
}
/* Create a time-stamp request for the file. */
static int tsp_query(const char* dataFile, const char* reqFile)
{
int ret = 1;
int r;
WC_RNG rng;
int rngInit = 0;
TSP_DECL(TspRequest, req, 1);
TSP_DECL(byte, enc, WC_TSP_MAX_REQ_SZ);
word32 encSz = WC_TSP_MAX_REQ_SZ;
byte hash[WC_SHA256_DIGEST_SIZE];
byte nonce[TSP_NUM_SZ];
TSP_ALLOC(TspRequest, req, 1, goto done);
TSP_ALLOC(byte, enc, WC_TSP_MAX_REQ_SZ, goto done);
/* Hash the data to be time-stamped - the TSA never sees the data. */
r = wc_TspRequest_Init(req);
if (r != 0)
goto done;
/* Set the message imprint hash algorithm, then its value. */
r = wc_TspRequest_SetHashType(req, WC_HASH_TYPE_SHA256);
if (r != 0)
goto done;
if (tsp_hash_file(dataFile, hash) != 0)
goto done;
r = wc_TspRequest_SetHash(req, hash, (word32)sizeof(hash));
if (r != 0)
goto done;
/* Random nonce to tie the response to this request - SetNonce strips
* any leading zero bytes so the nonce is encodable. */
if (wc_InitRng(&rng) != 0)
goto done;
rngInit = 1;
if (wc_RNG_GenerateBlock(&rng, nonce, (word32)sizeof(nonce)) != 0)
goto done;
r = wc_TspRequest_SetNonce(req, nonce, (word32)sizeof(nonce));
if (r != 0)
goto done;
/* Ask for the TSA's certificate to be included in the token. */
req->certReq = 1;
r = wc_TspRequest_Encode(req, enc, &encSz);
if (r != 0) {
fprintf(stderr, "encode request failed: %s\n", wc_GetErrorString(r));
goto done;
}
if (tsp_write_file(reqFile, enc, encSz) != 0)
goto done;
printf("Wrote %u byte time-stamp request to %s\n", encSz, reqFile);
ret = 0;
done:
if (rngInit)
wc_FreeRng(&rng);
TSP_FREE(req);
TSP_FREE(enc);
return ret;
}
int main(int argc, char* argv[])
{
if (argc != 3) {
fprintf(stderr, "usage: %s <file> <request.tsq>\n", argv[0]);
return 1;
}
return tsp_query(argv[1], argv[2]);
}
#else
int main(void)
{
#ifdef NO_FILESYSTEM
fprintf(stderr, "NO_FILESYSTEM is defined\n");
#else
fprintf(stderr, "Build wolfSSL with ./configure --enable-tsp\n");
#endif
return 1;
}
#endif
+346
View File
@@ -0,0 +1,346 @@
/* tsp_reply.c
*
* Copyright (C) 2006-2026 wolfSSL Inc.
*
* This file is part of wolfSSL.
*
* wolfSSL is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfSSL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
/* Time-Stamp Protocol (RFC 3161) example: act as a TSA and reply.
*
* tsp_reply <request.tsq> <tsa-cert.der> <tsa-key.der> <response.tsr>
* [rsa|ecc]
* Write a signed response for the request.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifndef WOLFSSL_USER_SETTINGS
#include <wolfssl/options.h>
#endif
#include <wolfssl/wolfcrypt/settings.h>
#include <wolfssl/wolfcrypt/tsp.h>
#include <wolfssl/wolfcrypt/hash.h>
#include <wolfssl/wolfcrypt/random.h>
#include <wolfssl/wolfcrypt/error-crypt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && \
(!defined(NO_RSA) || defined(HAVE_ECC)) && \
!defined(NO_SHA256) && !defined(WC_NO_RNG) && !defined(NO_FILESYSTEM) && \
defined(WOLFSSL_TSP_RESPONDER)
/* TSA policy of this example: 1.3.6.1.4.1.999.1. */
static const byte tsaPolicy[] = {
0x2b, 0x06, 0x01, 0x04, 0x01, 0x87, 0x67, 0x01
};
/* Number of random bytes in a serial number. */
#define TSP_NUM_SZ 8
/* Maximum size of a file read into memory: a DER request, certificate or key,
* and the time-stamp token written. Big enough for an RSA-2048 credential and
* a typical time-stamp token. */
#ifndef WC_TSP_MAX_FILE_SZ
#define WC_TSP_MAX_FILE_SZ 8192
#endif
/* Local variables larger than 63 bytes - big buffers and big structures - are
* kept off the stack. They are allocated from the heap, unless dynamic memory
* is not available (WOLFSSL_NO_MALLOC) in which case they go on the stack.
* WOLFSSL_SMALL_STACK uses the heap path; it never co-exists with
* WOLFSSL_NO_MALLOC. 'name' is always used as a pointer.
*
* TSP_DECL declares, TSP_ALLOC allocates (running 'fail' on failure) and
* TSP_FREE releases an array of 'cnt' items of 'type' (cnt is 1 for a single
* object). */
#ifdef WOLFSSL_NO_MALLOC
#define TSP_DECL(type, name, cnt) type name[cnt]
#define TSP_ALLOC(type, name, cnt, fail) WC_DO_NOTHING
#define TSP_FREE(name) WC_DO_NOTHING
#else
#define TSP_DECL(type, name, cnt) type* name = NULL
#define TSP_ALLOC(type, name, cnt, fail) \
do { \
(name) = (type*)XMALLOC(sizeof(type) * (cnt), NULL, \
DYNAMIC_TYPE_TMP_BUFFER); \
if ((name) == NULL) { \
fail; \
} \
} while (0)
#define TSP_FREE(name) \
XFREE((name), NULL, DYNAMIC_TYPE_TMP_BUFFER)
#endif
/* Read a whole file into the caller's buffer. Fails when the file is larger
* than maxSz so that no dynamic allocation of the file size is needed. */
static int tsp_read_file(const char* name, byte* data, word32 maxSz,
word32* sz)
{
int ret = -1;
FILE* f;
long len;
f = fopen(name, "rb");
if (f == NULL) {
fprintf(stderr, "failed to open %s\n", name);
return -1;
}
if ((fseek(f, 0, SEEK_END) == 0) && ((len = ftell(f)) >= 0) &&
(fseek(f, 0, SEEK_SET) == 0)) {
/* Compare as unsigned long so a length above 4GB is not truncated by
* a word32 cast to a small value that slips under maxSz. */
if ((unsigned long)len > maxSz) {
fprintf(stderr, "%s is too big (%ld > %u)\n", name, len, maxSz);
}
else if (fread(data, 1, (size_t)len, f) == (size_t)len) {
*sz = (word32)len;
ret = 0;
}
}
fclose(f);
return ret;
}
/* Write a buffer to a file. */
static int tsp_write_file(const char* name, const byte* data, word32 sz)
{
int ret = -1;
FILE* f;
f = fopen(name, "wb");
if (f == NULL) {
fprintf(stderr, "failed to open %s\n", name);
return -1;
}
if (fwrite(data, 1, sz, f) == sz) {
ret = 0;
}
fclose(f);
return ret;
}
/* Generate a random number without leading zero bytes. */
static int tsp_rand_num(WC_RNG* rng, byte* num, word32 sz, word32* outSz)
{
word32 i;
if (wc_RNG_GenerateBlock(rng, num, sz) != 0)
return -1;
/* Skip leading zero bytes - keep at least one byte. */
for (i = 0; (i < sz - 1) && (num[i] == 0x00); i++);
if (num[i] == 0x00)
num[i] = 0x01;
memmove(num, num + i, sz - i);
*outSz = sz - i;
return 0;
}
/* Write a response to the file. */
static int tsp_write_response(const char* name, const TspResponse* resp)
{
int ret = 1;
int r;
TSP_DECL(byte, enc, WC_TSP_MAX_FILE_SZ);
word32 encSz = WC_TSP_MAX_FILE_SZ;
TSP_ALLOC(byte, enc, WC_TSP_MAX_FILE_SZ, return 1);
r = wc_TspResponse_Encode(resp, enc, &encSz);
if (r != 0) {
fprintf(stderr, "encode response failed: %s\n", wc_GetErrorString(r));
goto done;
}
if (tsp_write_file(name, enc, encSz) != 0)
goto done;
printf("Wrote %u byte time-stamp response to %s\n", encSz, name);
ret = 0;
done:
TSP_FREE(enc);
return ret;
}
/* Write a rejection response with failure information. */
static int tsp_reject(const char* name, word32 failInfo, const char* text)
{
TspResponse resp;
(void)wc_TspResponse_Init(&resp);
(void)wc_TspResponse_SetStatus(&resp, WC_TSP_PKISTATUS_REJECTION,
(const byte*)text, (word32)strlen(text), failInfo);
printf("Rejecting request: %s\n", text);
return tsp_write_response(name, &resp);
}
/* Act as a TSA - create a response for the request. */
static int tsp_reply(const char* reqFile, const char* certFile,
const char* keyFile, const char* respFile, const char* keyType)
{
int ret = 1;
int r;
WC_RNG rng;
int rngInit = 0;
TSP_DECL(TspRequest, req, 1);
TSP_DECL(TspTstInfo, tst, 1);
TspResponse resp;
TSP_DECL(byte, reqDer, WC_TSP_MAX_FILE_SZ);
word32 reqDerSz = 0;
TSP_DECL(byte, cert, WC_TSP_MAX_FILE_SZ);
word32 certSz = 0;
TSP_DECL(byte, key, WC_TSP_MAX_FILE_SZ);
word32 keySz = 0;
byte serial[TSP_NUM_SZ];
word32 serialSz = 0;
TSP_DECL(byte, token, WC_TSP_MAX_FILE_SZ);
word32 tokenSz = WC_TSP_MAX_FILE_SZ;
TSP_ALLOC(TspRequest, req, 1, goto done);
TSP_ALLOC(TspTstInfo, tst, 1, goto done);
TSP_ALLOC(byte, reqDer, WC_TSP_MAX_FILE_SZ, goto done);
TSP_ALLOC(byte, cert, WC_TSP_MAX_FILE_SZ, goto done);
TSP_ALLOC(byte, key, WC_TSP_MAX_FILE_SZ, goto done);
TSP_ALLOC(byte, token, WC_TSP_MAX_FILE_SZ, goto done);
/* Load the request and the TSA's credentials. */
if ((tsp_read_file(reqFile, reqDer, WC_TSP_MAX_FILE_SZ, &reqDerSz) != 0) ||
(tsp_read_file(certFile, cert, WC_TSP_MAX_FILE_SZ, &certSz) != 0) ||
(tsp_read_file(keyFile, key, WC_TSP_MAX_FILE_SZ, &keySz) != 0)) {
goto done;
}
/* A request that does not parse is rejected. */
r = wc_TspRequest_Decode(req, reqDer, reqDerSz);
if (r != 0) {
ret = tsp_reject(respFile, WC_TSP_FAIL_BAD_DATA_FORMAT,
"request could not be parsed");
goto done;
}
/* The hash algorithm of the imprint must be one the TSA supports. */
if (wc_HashGetDigestSize(wc_OidGetHash((int)req->imprint.hashAlgOID)) !=
(int)req->imprint.hashSz) {
ret = tsp_reject(respFile, WC_TSP_FAIL_BAD_ALG,
"hash algorithm not supported");
goto done;
}
if (wc_InitRng(&rng) != 0)
goto done;
rngInit = 1;
/* Fill in the TSTInfo for the request. */
r = wc_TspTstInfo_Init(tst);
if (r != 0)
goto done;
tst->policy = tsaPolicy;
tst->policySz = (word32)sizeof(tsaPolicy);
/* Time-stamp the imprint as it was sent. */
tst->imprint = req->imprint;
/* Random serial number - a real TSA must ensure uniqueness across
* restarts. */
if (tsp_rand_num(&rng, serial, (word32)sizeof(serial), &serialSz) != 0)
goto done;
/* Leading zero bytes are stripped so the serial number encodes. */
if (wc_TspTstInfo_SetSerial(tst, serial, serialSz) != 0)
goto done;
/* genTime of NULL - the current time is used. */
tst->accuracy.seconds = 1;
/* The nonce must be returned when it was in the request. */
if (req->nonceSz != 0) {
tst->nonce = req->nonce;
tst->nonceSz = req->nonceSz;
}
/* Sign the TSTInfo to make a time-stamp token. This example always
* includes the TSA's certificate in the token. */
{
enum wc_PkType keyPkType;
#ifndef HAVE_ECC
(void)keyType;
#endif
#ifdef HAVE_ECC
if ((keyType != NULL) && (strcmp(keyType, "ecc") == 0)) {
keyPkType = WC_PK_TYPE_ECDSA_SIGN;
}
else
#endif
{
#ifndef NO_RSA
keyPkType = WC_PK_TYPE_RSA;
#else
/* RSA not available - fall back to ECC. */
keyPkType = WC_PK_TYPE_ECDSA_SIGN;
#endif
}
r = wc_TspTstInfo_Sign(tst, cert, certSz, key, keySz,
keyPkType, WC_HASH_TYPE_SHA256, &rng, token, &tokenSz);
}
if (r != 0) {
fprintf(stderr, "create token failed: %s\n", wc_GetErrorString(r));
goto done;
}
/* Put the token in a granted response. */
r = wc_TspResponse_Init(&resp);
if (r != 0)
goto done;
(void)wc_TspResponse_SetStatus(&resp, WC_TSP_PKISTATUS_GRANTED, NULL, 0, 0);
resp.token = token;
resp.tokenSz = tokenSz;
ret = tsp_write_response(respFile, &resp);
done:
if (rngInit)
wc_FreeRng(&rng);
TSP_FREE(req);
TSP_FREE(tst);
TSP_FREE(reqDer);
TSP_FREE(cert);
TSP_FREE(key);
TSP_FREE(token);
return ret;
}
int main(int argc, char* argv[])
{
if ((argc != 5) && (argc != 6)) {
fprintf(stderr, "usage: %s <request.tsq> <tsa-cert.der> "
"<tsa-key.der> <response.tsr> [rsa|ecc]\n", argv[0]);
return 1;
}
return tsp_reply(argv[1], argv[2], argv[3], argv[4],
(argc == 6) ? argv[5] : NULL);
}
#else
int main(void)
{
#ifdef NO_FILESYSTEM
fprintf(stderr, "NO_FILESYSTEM is defined\n");
#else
fprintf(stderr, "Build wolfSSL with ./configure --enable-tsp\n");
#endif
return 1;
}
#endif
+302
View File
@@ -0,0 +1,302 @@
/* tsp_verify.c
*
* Copyright (C) 2006-2026 wolfSSL Inc.
*
* This file is part of wolfSSL.
*
* wolfSSL is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfSSL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
/* Time-Stamp Protocol (RFC 3161) example: verify a response.
*
* tsp_verify <file> <request.tsq> <response.tsr> <tsa-cert.der>
* Verify a response against the data, the request sent and the
* trusted TSA certificate.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifndef WOLFSSL_USER_SETTINGS
#include <wolfssl/options.h>
#endif
#include <wolfssl/wolfcrypt/settings.h>
#include <wolfssl/wolfcrypt/tsp.h>
#include <wolfssl/wolfcrypt/hash.h>
#include <wolfssl/wolfcrypt/sha256.h>
#include <wolfssl/wolfcrypt/random.h>
#include <wolfssl/wolfcrypt/error-crypt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && \
(!defined(NO_RSA) || defined(HAVE_ECC)) && \
!defined(NO_SHA256) && !defined(WC_NO_RNG) && !defined(NO_FILESYSTEM) && \
defined(WOLFSSL_TSP_VERIFIER)
/* Maximum size of a file read into memory: a DER request, response or
* certificate. Big enough for an RSA-2048 credential and a typical
* time-stamp token. */
#ifndef WC_TSP_MAX_FILE_SZ
#define WC_TSP_MAX_FILE_SZ 8192
#endif
/* Size of the buffer used to hash a file a chunk at a time. */
#ifndef WC_TSP_HASH_CHUNK_SZ
#define WC_TSP_HASH_CHUNK_SZ 256
#endif
/* Local variables larger than 63 bytes - big buffers and big structures - are
* kept off the stack. They are allocated from the heap, unless dynamic memory
* is not available (WOLFSSL_NO_MALLOC) in which case they go on the stack.
* WOLFSSL_SMALL_STACK uses the heap path; it never co-exists with
* WOLFSSL_NO_MALLOC. 'name' is always used as a pointer.
*
* TSP_DECL declares, TSP_ALLOC allocates (running 'fail' on failure) and
* TSP_FREE releases an array of 'cnt' items of 'type' (cnt is 1 for a single
* object). */
#ifdef WOLFSSL_NO_MALLOC
#define TSP_DECL(type, name, cnt) type name[cnt]
#define TSP_ALLOC(type, name, cnt, fail) WC_DO_NOTHING
#define TSP_FREE(name) WC_DO_NOTHING
#else
#define TSP_DECL(type, name, cnt) type* name = NULL
#define TSP_ALLOC(type, name, cnt, fail) \
do { \
(name) = (type*)XMALLOC(sizeof(type) * (cnt), NULL, \
DYNAMIC_TYPE_TMP_BUFFER); \
if ((name) == NULL) { \
fail; \
} \
} while (0)
#define TSP_FREE(name) \
XFREE((name), NULL, DYNAMIC_TYPE_TMP_BUFFER)
#endif
/* Read a whole file into the caller's buffer. Fails when the file is larger
* than maxSz so that no dynamic allocation of the file size is needed. */
static int tsp_read_file(const char* name, byte* data, word32 maxSz,
word32* sz)
{
int ret = -1;
FILE* f;
long len;
f = fopen(name, "rb");
if (f == NULL) {
fprintf(stderr, "failed to open %s\n", name);
return -1;
}
if ((fseek(f, 0, SEEK_END) == 0) && ((len = ftell(f)) >= 0) &&
(fseek(f, 0, SEEK_SET) == 0)) {
/* Compare as unsigned long so a length above 4GB is not truncated by
* a word32 cast to a small value that slips under maxSz. */
if ((unsigned long)len > maxSz) {
fprintf(stderr, "%s is too big (%ld > %u)\n", name, len, maxSz);
}
else if (fread(data, 1, (size_t)len, f) == (size_t)len) {
*sz = (word32)len;
ret = 0;
}
}
fclose(f);
return ret;
}
/* Hash a file with SHA-256 a chunk at a time so the whole file need not be
* held in memory. */
static int tsp_hash_file(const char* name, byte* hash)
{
int ret = -1;
FILE* f = NULL;
TSP_DECL(wc_Sha256, sha, 1);
TSP_DECL(byte, buf, WC_TSP_HASH_CHUNK_SZ);
size_t n;
TSP_ALLOC(wc_Sha256, sha, 1, goto done);
TSP_ALLOC(byte, buf, WC_TSP_HASH_CHUNK_SZ, goto done);
f = fopen(name, "rb");
if (f == NULL) {
fprintf(stderr, "failed to open %s\n", name);
goto done;
}
if (wc_InitSha256(sha) == 0) {
ret = 0;
while ((n = fread(buf, 1, WC_TSP_HASH_CHUNK_SZ, f)) > 0) {
if (wc_Sha256Update(sha, buf, (word32)n) != 0) {
ret = -1;
break;
}
}
if ((ret == 0) && (ferror(f) != 0))
ret = -1;
if (ret == 0)
ret = (wc_Sha256Final(sha, hash) == 0) ? 0 : -1;
wc_Sha256Free(sha);
}
done:
if (f != NULL)
fclose(f);
TSP_FREE(sha);
TSP_FREE(buf);
return ret;
}
/* Check a response was granted - print the status when not. */
static int tsp_check_granted(const TspResponse* resp)
{
word32 status = 0;
const byte* statusText = NULL;
word32 statusTextSz = 0;
word32 failInfo = 0;
wc_TspResponse_GetStatus(resp, &status, &statusText, &statusTextSz,
&failInfo);
if ((status == WC_TSP_PKISTATUS_GRANTED) ||
(status == WC_TSP_PKISTATUS_GRANTED_WITH_MODS)) {
return 0;
}
fprintf(stderr, "time-stamp not granted: status %lu\n",
(unsigned long)status);
if (statusText != NULL) {
fprintf(stderr, " status text: %.*s\n", (int)statusTextSz,
statusText);
}
if (failInfo != 0) {
fprintf(stderr, " failure information: 0x%08lx\n",
(unsigned long)failInfo);
}
return 1;
}
/* Verify a response against the data and the request sent. */
static int tsp_verify(const char* dataFile, const char* reqFile,
const char* respFile, const char* certFile)
{
int ret = 1;
int r;
TSP_DECL(TspRequest, req, 1);
TspResponse resp;
TSP_DECL(TspTstInfo, tst, 1);
TSP_DECL(byte, reqDer, WC_TSP_MAX_FILE_SZ);
word32 reqDerSz = 0;
TSP_DECL(byte, respDer, WC_TSP_MAX_FILE_SZ);
word32 respDerSz = 0;
TSP_DECL(byte, cert, WC_TSP_MAX_FILE_SZ);
word32 certSz = 0;
byte hash[WC_SHA256_DIGEST_SIZE];
enum wc_HashType hashType = WC_HASH_TYPE_NONE;
TSP_ALLOC(TspRequest, req, 1, goto done);
TSP_ALLOC(TspTstInfo, tst, 1, goto done);
TSP_ALLOC(byte, reqDer, WC_TSP_MAX_FILE_SZ, goto done);
TSP_ALLOC(byte, respDer, WC_TSP_MAX_FILE_SZ, goto done);
TSP_ALLOC(byte, cert, WC_TSP_MAX_FILE_SZ, goto done);
/* Load the request, response and trusted TSA certificate. */
if ((tsp_read_file(reqFile, reqDer, WC_TSP_MAX_FILE_SZ, &reqDerSz) != 0) ||
(tsp_read_file(respFile, respDer, WC_TSP_MAX_FILE_SZ,
&respDerSz) != 0) ||
(tsp_read_file(certFile, cert, WC_TSP_MAX_FILE_SZ, &certSz) != 0)) {
goto done;
}
/* Check the request was for this data - hash the data file again. */
r = wc_TspRequest_Decode(req, reqDer, reqDerSz);
if (r != 0) {
fprintf(stderr, "decode request failed: %s\n", wc_GetErrorString(r));
goto done;
}
/* This example only hashes data with SHA-256 - the request must match. */
r = wc_TspRequest_GetHashType(req, &hashType);
if (r == 0)
r = tsp_hash_file(dataFile, hash);
if ((r != 0) || (hashType != WC_HASH_TYPE_SHA256) ||
(req->imprint.hashSz != (word32)sizeof(hash)) ||
(memcmp(req->imprint.hash, hash, sizeof(hash)) != 0)) {
fprintf(stderr, "request is not for this data\n");
goto done;
}
/* The time-stamp must have been granted. */
r = wc_TspResponse_Decode(&resp, respDer, respDerSz);
if (r != 0) {
fprintf(stderr, "decode response failed: %s\n", wc_GetErrorString(r));
goto done;
}
if (tsp_check_granted(&resp) != 0) {
goto done;
}
/* Verify the token against the trusted TSA certificate - the signer must
* be that certificate. The certificate is also used to verify tokens that
* do not include it. */
r = wc_TspResponse_Verify(&resp, cert, certSz, tst);
if (r != 0) {
fprintf(stderr, "token verification failed: %s\n",
wc_GetErrorString(r));
goto done;
}
/* Check the TSTInfo is for the request - imprint, nonce and policy. */
r = wc_TspTstInfo_CheckRequest(tst, req);
if (r != 0) {
fprintf(stderr, "token does not match request: %s\n",
wc_GetErrorString(r));
goto done;
}
/* tst references the response's token - use before respDer is freed. */
printf("Verification: OK\n");
printf(" Time: %.*s\n", (int)tst->genTimeSz, tst->genTime);
ret = 0;
done:
TSP_FREE(req);
TSP_FREE(tst);
TSP_FREE(reqDer);
TSP_FREE(respDer);
TSP_FREE(cert);
return ret;
}
int main(int argc, char* argv[])
{
if (argc != 5) {
fprintf(stderr, "usage: %s <file> <request.tsq> <response.tsr> "
"<tsa-cert.der>\n", argv[0]);
return 1;
}
return tsp_verify(argv[1], argv[2], argv[3], argv[4]);
}
#else
int main(void)
{
#ifdef NO_FILESYSTEM
fprintf(stderr, "NO_FILESYSTEM is defined\n");
#else
fprintf(stderr, "Build wolfSSL with ./configure --enable-tsp\n");
#endif
return 1;
}
#endif