mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-07-09 13:10:52 +02:00
Merge pull request #10778 from SparkiDev/time_stamp_protocol
Time-Stamp Protocol (RFC 3161)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user