mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-07-09 12:20:51 +02:00
ae023a5643
Implementation in wolfCrypt OpenSSL compatibility layer in wolfSSL Added tests, certificates, examples.
2388 lines
83 KiB
C
2388 lines
83 KiB
C
/* tsp.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
|
|
*/
|
|
|
|
/*
|
|
* DESCRIPTION
|
|
* This library provides the application interface to the Time-Stamp Protocol
|
|
* (TSP): TimeStampReq, TSTInfo and TimeStampResp setup and accessors, and
|
|
* verification helpers for a response. RFC 3161.
|
|
*
|
|
* The encoding and decoding of TSP messages, and creation and verification of
|
|
* time-stamp tokens (which use ASN.1 template machinery private to asn.c), are
|
|
* in asn_tsp.c.
|
|
*/
|
|
|
|
#include <wolfssl/wolfcrypt/libwolfssl_sources.h>
|
|
|
|
#if defined(WOLFSSL_TSP) && defined(WOLFSSL_ASN_TEMPLATE) && !defined(NO_ASN)
|
|
|
|
#include <wolfssl/wolfcrypt/asn.h>
|
|
#include <wolfssl/wolfcrypt/tsp.h>
|
|
#include <wolfssl/wolfcrypt/error-crypt.h>
|
|
#include <wolfssl/wolfcrypt/hash.h>
|
|
#include <wolfssl/wolfcrypt/random.h>
|
|
#ifdef HAVE_PKCS7
|
|
#include <wolfssl/wolfcrypt/pkcs7.h>
|
|
#endif
|
|
|
|
#ifdef WOLFSSL_TSP_REQUESTER
|
|
/* Initialize a TimeStampReq.
|
|
*
|
|
* @param [out] req TimeStampReq object.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when req is NULL.
|
|
*/
|
|
int wc_TspRequest_Init(TspRequest* req)
|
|
{
|
|
/* Validate parameter. */
|
|
if (req == NULL) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
/* All fields empty - optional fields not encoded. */
|
|
XMEMSET(req, 0, sizeof(TspRequest));
|
|
/* Only version 1 defined. */
|
|
req->version = WC_TSP_VERSION;
|
|
|
|
return 0;
|
|
}
|
|
#endif /* WOLFSSL_TSP_REQUESTER */
|
|
|
|
#if defined(WOLFSSL_TSP_REQUESTER) || defined(WOLFSSL_TSP_VERIFIER)
|
|
/* Get the message imprint hash type of a TimeStampReq.
|
|
*
|
|
* Maps the message imprint hash algorithm OID to a hash type. The OID may be
|
|
* one not recognized as a hash algorithm - e.g. after decoding a request from
|
|
* an unknown source.
|
|
*
|
|
* @param [in] req TimeStampReq object.
|
|
* @param [out] hashType Hash algorithm of the message imprint.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when req or hashType is NULL.
|
|
* @return HASH_TYPE_E when the hash algorithm is not a recognized hash.
|
|
*/
|
|
int wc_TspRequest_GetHashType(const TspRequest* req, enum wc_HashType* hashType)
|
|
{
|
|
int ret = 0;
|
|
|
|
/* Validate parameters. */
|
|
if ((req == NULL) || (hashType == NULL)) {
|
|
ret = BAD_FUNC_ARG;
|
|
}
|
|
|
|
if (ret == 0) {
|
|
/* Map the OID sum to a hash type - NONE when not a known hash. */
|
|
*hashType = wc_OidGetHash((int)req->imprint.hashAlgOID);
|
|
if (*hashType == WC_HASH_TYPE_NONE) {
|
|
ret = HASH_TYPE_E;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif /* WOLFSSL_TSP_REQUESTER || WOLFSSL_TSP_VERIFIER */
|
|
|
|
#ifdef WOLFSSL_TSP_REQUESTER
|
|
/* Set the message imprint hash algorithm of a TimeStampReq.
|
|
*
|
|
* Sets the hash algorithm OID and hash size from the hash type. The caller
|
|
* fills req->imprint.hash with the digest of the data to be time-stamped.
|
|
*
|
|
* @param [in, out] req TimeStampReq object.
|
|
* @param [in] hashType Hash algorithm to use - e.g. WC_HASH_TYPE_SHA256.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when req is NULL.
|
|
* @return HASH_TYPE_E when the hash algorithm is not available.
|
|
* @return BUFFER_E when the digest is too big for the message imprint.
|
|
*/
|
|
int wc_TspRequest_SetHashType(TspRequest* req, enum wc_HashType hashType)
|
|
{
|
|
int ret = 0;
|
|
int oid = 0;
|
|
int digestSz = 0;
|
|
|
|
/* Validate parameter. */
|
|
if (req == NULL) {
|
|
ret = BAD_FUNC_ARG;
|
|
}
|
|
|
|
if (ret == 0) {
|
|
/* Map the hash type to its OID sum - negative when not available. */
|
|
oid = wc_HashGetOID(hashType);
|
|
if (oid <= 0) {
|
|
ret = HASH_TYPE_E;
|
|
}
|
|
}
|
|
if (ret == 0) {
|
|
/* The digest size is the length of the message imprint hash. */
|
|
digestSz = wc_HashGetDigestSize(hashType);
|
|
if (digestSz <= 0) {
|
|
ret = HASH_TYPE_E;
|
|
}
|
|
else if (digestSz > (int)sizeof(req->imprint.hash)) {
|
|
ret = BUFFER_E;
|
|
}
|
|
}
|
|
if (ret == 0) {
|
|
req->imprint.hashAlgOID = (word32)oid;
|
|
req->imprint.hashSz = (word32)digestSz;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif /* WOLFSSL_TSP_REQUESTER */
|
|
|
|
#if defined(WOLFSSL_TSP_REQUESTER) || defined(WOLFSSL_TSP_VERIFIER)
|
|
/* Get the message imprint hash of a TimeStampReq.
|
|
*
|
|
* Copies the hash into the caller's buffer.
|
|
*
|
|
* @param [in] req TimeStampReq object.
|
|
* @param [out] hash Buffer to hold the hash.
|
|
* @param [in, out] hashSz On in, length of buffer in bytes.
|
|
* On out, length of the hash in bytes.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when req, hash or hashSz is NULL.
|
|
* @return BUFFER_E when the buffer is too small for the hash.
|
|
*/
|
|
int wc_TspRequest_GetHash(const TspRequest* req, byte* hash, word32* hashSz)
|
|
{
|
|
int ret = 0;
|
|
|
|
/* Validate parameters. */
|
|
if ((req == NULL) || (hash == NULL) || (hashSz == NULL)) {
|
|
ret = BAD_FUNC_ARG;
|
|
}
|
|
else if (*hashSz < req->imprint.hashSz) {
|
|
ret = BUFFER_E;
|
|
}
|
|
|
|
if (ret == 0) {
|
|
XMEMCPY(hash, req->imprint.hash, req->imprint.hashSz);
|
|
*hashSz = req->imprint.hashSz;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif /* WOLFSSL_TSP_REQUESTER || WOLFSSL_TSP_VERIFIER */
|
|
|
|
#ifdef WOLFSSL_TSP_REQUESTER
|
|
/* Set the message imprint hash of a TimeStampReq.
|
|
*
|
|
* Copies the hash and its length into the message imprint. The hash algorithm
|
|
* is set separately - see wc_TspRequest_SetHashType().
|
|
*
|
|
* @param [in, out] req TimeStampReq object.
|
|
* @param [in] hash Hash of the data to be time-stamped.
|
|
* @param [in] hashSz Length of hash in bytes.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when req or hash is NULL or hashSz is 0.
|
|
* @return BUFFER_E when hashSz is too big for the message imprint.
|
|
*/
|
|
int wc_TspRequest_SetHash(TspRequest* req, const byte* hash, word32 hashSz)
|
|
{
|
|
int ret = 0;
|
|
|
|
/* Validate parameters. */
|
|
if ((req == NULL) || (hash == NULL) || (hashSz == 0)) {
|
|
ret = BAD_FUNC_ARG;
|
|
}
|
|
else if (hashSz > sizeof(req->imprint.hash)) {
|
|
ret = BUFFER_E;
|
|
}
|
|
|
|
if (ret == 0) {
|
|
XMEMCPY(req->imprint.hash, hash, hashSz);
|
|
req->imprint.hashSz = hashSz;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif /* WOLFSSL_TSP_REQUESTER */
|
|
|
|
#if defined(WOLFSSL_TSP_REQUESTER) || defined(WOLFSSL_TSP_VERIFIER)
|
|
/* Get the nonce of a TimeStampReq.
|
|
*
|
|
* Copies the nonce into the caller's buffer. A length of 0 means no nonce is
|
|
* set.
|
|
*
|
|
* @param [in] req TimeStampReq object.
|
|
* @param [out] nonce Buffer to hold the nonce.
|
|
* @param [in, out] nonceSz On in, length of buffer in bytes.
|
|
* On out, length of the nonce in bytes.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when req, nonce or nonceSz is NULL.
|
|
* @return BUFFER_E when the buffer is too small for the nonce.
|
|
*/
|
|
int wc_TspRequest_GetNonce(const TspRequest* req, byte* nonce, word32* nonceSz)
|
|
{
|
|
int ret = 0;
|
|
|
|
/* Validate parameters. */
|
|
if ((req == NULL) || (nonce == NULL) || (nonceSz == NULL)) {
|
|
ret = BAD_FUNC_ARG;
|
|
}
|
|
else if (*nonceSz < req->nonceSz) {
|
|
ret = BUFFER_E;
|
|
}
|
|
|
|
if (ret == 0) {
|
|
XMEMCPY(nonce, req->nonce, req->nonceSz);
|
|
*nonceSz = req->nonceSz;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif /* WOLFSSL_TSP_REQUESTER || WOLFSSL_TSP_VERIFIER */
|
|
|
|
#ifdef WOLFSSL_TSP_REQUESTER
|
|
/* Set the nonce of a TimeStampReq.
|
|
*
|
|
* The nonce is a big-endian number that must not have a leading zero byte to
|
|
* encode. Leading zero bytes are stripped, keeping at least one byte so an
|
|
* all-zero nonce becomes the number zero.
|
|
*
|
|
* @param [in, out] req TimeStampReq object.
|
|
* @param [in] nonce Nonce as a big-endian number.
|
|
* @param [in] nonceSz Length of nonce in bytes.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when req or nonce is NULL or nonceSz is 0.
|
|
* @return BUFFER_E when nonceSz is too big for the nonce field.
|
|
*/
|
|
int wc_TspRequest_SetNonce(TspRequest* req, const byte* nonce, word32 nonceSz)
|
|
{
|
|
int ret = 0;
|
|
|
|
/* Validate parameters. */
|
|
if ((req == NULL) || (nonce == NULL) || (nonceSz == 0)) {
|
|
ret = BAD_FUNC_ARG;
|
|
}
|
|
else if (nonceSz > sizeof(req->nonce)) {
|
|
ret = BUFFER_E;
|
|
}
|
|
|
|
if (ret == 0) {
|
|
/* Strip leading zeros. */
|
|
while ((nonceSz > 1) && (nonce[0] == 0x00)) {
|
|
nonce++;
|
|
nonceSz--;
|
|
}
|
|
XMEMCPY(req->nonce, nonce, nonceSz);
|
|
req->nonceSz = nonceSz;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#ifndef WC_NO_RNG
|
|
/* Generate a random nonce for a TimeStampReq.
|
|
*
|
|
* A convenience over generating random bytes and calling
|
|
* wc_TspRequest_SetNonce(). The nonce is a minimal positive INTEGER: the top
|
|
* bit of the first byte is cleared so it is positive and the first byte is
|
|
* made non-zero so there is no leading zero byte to strip.
|
|
*
|
|
* @param [in, out] req TimeStampReq object.
|
|
* @param [in] rng Random number generator.
|
|
* @param [in] sz Length of nonce to generate in bytes.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when req or rng is NULL or sz is 0.
|
|
* @return BUFFER_E when sz is too big for the nonce field.
|
|
* @return Other negative value on random number generation failure.
|
|
*/
|
|
int wc_TspRequest_GenerateNonce(TspRequest* req, WC_RNG* rng, word32 sz)
|
|
{
|
|
int ret = 0;
|
|
|
|
/* Validate parameters. */
|
|
if ((req == NULL) || (rng == NULL) || (sz == 0)) {
|
|
ret = BAD_FUNC_ARG;
|
|
}
|
|
else if (sz > sizeof(req->nonce)) {
|
|
ret = BUFFER_E;
|
|
}
|
|
|
|
if (ret == 0) {
|
|
ret = wc_RNG_GenerateBlock(rng, req->nonce, sz);
|
|
}
|
|
if (ret == 0) {
|
|
/* Make a minimal positive INTEGER: clear the sign bit and ensure a
|
|
* non-zero leading byte so there is no leading zero to strip. */
|
|
req->nonce[0] &= 0x7F;
|
|
if (req->nonce[0] == 0x00) {
|
|
req->nonce[0] = 0x01;
|
|
}
|
|
req->nonceSz = sz;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif /* WC_NO_RNG */
|
|
#endif /* WOLFSSL_TSP_REQUESTER */
|
|
|
|
#if defined(WOLFSSL_TSP_REQUESTER) || defined(WOLFSSL_TSP_VERIFIER)
|
|
/* Get the TSA policy of a TimeStampReq.
|
|
*
|
|
* Copies the policy into the caller's buffer. A length of 0 means no policy is
|
|
* set.
|
|
*
|
|
* @param [in] req TimeStampReq object.
|
|
* @param [out] policy Buffer to hold the policy.
|
|
* @param [in, out] policySz On in, length of buffer in bytes.
|
|
* On out, length of the policy in bytes.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when req, policy or policySz is NULL.
|
|
* @return BUFFER_E when the buffer is too small for the policy.
|
|
*/
|
|
int wc_TspRequest_GetPolicy(const TspRequest* req, byte* policy,
|
|
word32* policySz)
|
|
{
|
|
int ret = 0;
|
|
|
|
/* Validate parameters. */
|
|
if ((req == NULL) || (policy == NULL) || (policySz == NULL)) {
|
|
ret = BAD_FUNC_ARG;
|
|
}
|
|
else if (*policySz < req->policySz) {
|
|
ret = BUFFER_E;
|
|
}
|
|
|
|
if (ret == 0) {
|
|
XMEMCPY(policy, req->policy, req->policySz);
|
|
*policySz = req->policySz;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif /* WOLFSSL_TSP_REQUESTER || WOLFSSL_TSP_VERIFIER */
|
|
|
|
#ifdef WOLFSSL_TSP_REQUESTER
|
|
/* Set the TSA policy of a TimeStampReq.
|
|
*
|
|
* The policy is the content of an OBJECT IDENTIFIER - the bytes after the type
|
|
* and length. It is copied into the request.
|
|
*
|
|
* @param [in, out] req TimeStampReq object.
|
|
* @param [in] policy Policy as OBJECT IDENTIFIER content.
|
|
* @param [in] policySz Length of policy in bytes.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when req or policy is NULL or policySz is 0.
|
|
* @return BUFFER_E when policySz is too big for the policy field.
|
|
*/
|
|
int wc_TspRequest_SetPolicy(TspRequest* req, const byte* policy,
|
|
word32 policySz)
|
|
{
|
|
int ret = 0;
|
|
|
|
/* Validate parameters. */
|
|
if ((req == NULL) || (policy == NULL) || (policySz == 0)) {
|
|
ret = BAD_FUNC_ARG;
|
|
}
|
|
else if (policySz > sizeof(req->policy)) {
|
|
ret = BUFFER_E;
|
|
}
|
|
|
|
if (ret == 0) {
|
|
XMEMCPY(req->policy, policy, policySz);
|
|
req->policySz = policySz;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif /* WOLFSSL_TSP_REQUESTER */
|
|
|
|
#ifdef WOLFSSL_TSP_RESPONDER
|
|
/* Initialize a TSTInfo.
|
|
*
|
|
* @param [out] tstInfo TSTInfo object.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when tstInfo is NULL.
|
|
*/
|
|
int wc_TspTstInfo_Init(TspTstInfo* tstInfo)
|
|
{
|
|
/* Validate parameter. */
|
|
if (tstInfo == NULL) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
/* All fields empty - optional fields not encoded. */
|
|
XMEMSET(tstInfo, 0, sizeof(TspTstInfo));
|
|
/* Only version 1 defined. */
|
|
tstInfo->version = WC_TSP_VERSION;
|
|
|
|
return 0;
|
|
}
|
|
#endif /* WOLFSSL_TSP_RESPONDER */
|
|
|
|
#if defined(WOLFSSL_TSP_RESPONDER) || defined(WOLFSSL_TSP_VERIFIER)
|
|
/* Get the serial number of a TSTInfo.
|
|
*
|
|
* Returns a reference to the serial number - it is not copied and is valid
|
|
* while the TSTInfo references it.
|
|
*
|
|
* @param [in] tstInfo TSTInfo object.
|
|
* @param [out] serial Serial number as a big-endian number.
|
|
* @param [out] serialSz Length of serial number in bytes.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when tstInfo, serial or serialSz is NULL.
|
|
*/
|
|
int wc_TspTstInfo_GetSerial(const TspTstInfo* tstInfo, const byte** serial,
|
|
word32* serialSz)
|
|
{
|
|
/* Validate parameters. */
|
|
if ((tstInfo == NULL) || (serial == NULL) || (serialSz == NULL)) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
*serial = tstInfo->serial;
|
|
*serialSz = tstInfo->serialSz;
|
|
|
|
return 0;
|
|
}
|
|
#endif /* WOLFSSL_TSP_RESPONDER || WOLFSSL_TSP_VERIFIER */
|
|
|
|
#ifdef WOLFSSL_TSP_RESPONDER
|
|
/* Set the serial number of a TSTInfo.
|
|
*
|
|
* The serial number is a big-endian number that is referenced - it is not
|
|
* copied and must remain available while the TSTInfo is used. Leading zero
|
|
* bytes are stripped, keeping at least one byte, so the serial number has no
|
|
* leading zero byte and encodes - an all-zero serial number becomes zero.
|
|
*
|
|
* @param [in, out] tstInfo TSTInfo object.
|
|
* @param [in] serial Serial number as a big-endian number.
|
|
* @param [in] serialSz Length of serial number in bytes.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when tstInfo or serial is NULL or serialSz is 0.
|
|
*/
|
|
int wc_TspTstInfo_SetSerial(TspTstInfo* tstInfo, const byte* serial,
|
|
word32 serialSz)
|
|
{
|
|
/* Validate parameters. */
|
|
if ((tstInfo == NULL) || (serial == NULL) || (serialSz == 0)) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
/* Strip leading zero bytes - keep at least one byte. */
|
|
while ((serialSz > 1) && (serial[0] == 0x00)) {
|
|
serial++;
|
|
serialSz--;
|
|
}
|
|
tstInfo->serial = serial;
|
|
tstInfo->serialSz = serialSz;
|
|
|
|
return 0;
|
|
}
|
|
#endif /* WOLFSSL_TSP_RESPONDER */
|
|
|
|
#if defined(WOLFSSL_TSP_RESPONDER) || defined(WOLFSSL_TSP_VERIFIER)
|
|
/* Get the TSA policy of a TSTInfo.
|
|
*
|
|
* Returns a reference to the policy - it is not copied and is valid while the
|
|
* TSTInfo references it. A length of 0 means no policy is present.
|
|
*
|
|
* @param [in] tstInfo TSTInfo object.
|
|
* @param [out] policy Policy as OBJECT IDENTIFIER content.
|
|
* @param [out] policySz Length of policy in bytes.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when tstInfo, policy or policySz is NULL.
|
|
*/
|
|
int wc_TspTstInfo_GetPolicy(const TspTstInfo* tstInfo, const byte** policy,
|
|
word32* policySz)
|
|
{
|
|
/* Validate parameters. */
|
|
if ((tstInfo == NULL) || (policy == NULL) || (policySz == NULL)) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
*policy = tstInfo->policy;
|
|
*policySz = tstInfo->policySz;
|
|
|
|
return 0;
|
|
}
|
|
#endif /* WOLFSSL_TSP_RESPONDER || WOLFSSL_TSP_VERIFIER */
|
|
|
|
#ifdef WOLFSSL_TSP_RESPONDER
|
|
/* Set the TSA policy of a TSTInfo.
|
|
*
|
|
* The policy is the content of an OBJECT IDENTIFIER - it is referenced, not
|
|
* copied, and must remain available while the TSTInfo is used.
|
|
*
|
|
* @param [in, out] tstInfo TSTInfo object.
|
|
* @param [in] policy Policy as OBJECT IDENTIFIER content.
|
|
* @param [in] policySz Length of policy in bytes.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when tstInfo or policy is NULL or policySz is 0.
|
|
*/
|
|
int wc_TspTstInfo_SetPolicy(TspTstInfo* tstInfo, const byte* policy,
|
|
word32 policySz)
|
|
{
|
|
/* Validate parameters. */
|
|
if ((tstInfo == NULL) || (policy == NULL) || (policySz == 0)) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
tstInfo->policy = policy;
|
|
tstInfo->policySz = policySz;
|
|
|
|
return 0;
|
|
}
|
|
#endif /* WOLFSSL_TSP_RESPONDER */
|
|
|
|
#if defined(WOLFSSL_TSP_RESPONDER) || defined(WOLFSSL_TSP_VERIFIER)
|
|
/* Get the message imprint of a TSTInfo.
|
|
*
|
|
* The hash is the digest of the time-stamped data. Each output is optional -
|
|
* pass NULL to not retrieve it. The hash references the TSTInfo.
|
|
*
|
|
* @param [in] tstInfo TSTInfo object.
|
|
* @param [out] hashOID Hash algorithm OID sum. May be NULL.
|
|
* @param [out] hash Hash of the time-stamped data. May be NULL.
|
|
* @param [out] hashSz Length of hash in bytes. May be NULL.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when tstInfo is NULL.
|
|
*/
|
|
int wc_TspTstInfo_GetMsgImprint(const TspTstInfo* tstInfo, word32* hashOID,
|
|
const byte** hash, word32* hashSz)
|
|
{
|
|
/* Validate parameter. */
|
|
if (tstInfo == NULL) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
if (hashOID != NULL) {
|
|
*hashOID = tstInfo->imprint.hashAlgOID;
|
|
}
|
|
if (hash != NULL) {
|
|
*hash = tstInfo->imprint.hash;
|
|
}
|
|
if (hashSz != NULL) {
|
|
*hashSz = tstInfo->imprint.hashSz;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif /* WOLFSSL_TSP_RESPONDER || WOLFSSL_TSP_VERIFIER */
|
|
|
|
#ifdef WOLFSSL_TSP_RESPONDER
|
|
/* Set the message imprint of a TSTInfo.
|
|
*
|
|
* The hash is the digest of the data being time-stamped - it is copied into
|
|
* the TSTInfo. The hash and algorithm are typically those of the request.
|
|
*
|
|
* @param [in, out] tstInfo TSTInfo object.
|
|
* @param [in] hashOID Hash algorithm OID sum: SHA256h, etc.
|
|
* @param [in] hash Hash of the data to time-stamp.
|
|
* @param [in] hashSz Length of hash in bytes.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when tstInfo or hash is NULL or hashSz is 0.
|
|
* @return BUFFER_E when hashSz is too big for the message imprint.
|
|
*/
|
|
int wc_TspTstInfo_SetMsgImprint(TspTstInfo* tstInfo, word32 hashOID,
|
|
const byte* hash, word32 hashSz)
|
|
{
|
|
/* Validate parameters. */
|
|
if ((tstInfo == NULL) || (hash == NULL) || (hashSz == 0)) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
if (hashSz > sizeof(tstInfo->imprint.hash)) {
|
|
return BUFFER_E;
|
|
}
|
|
|
|
tstInfo->imprint.hashAlgOID = hashOID;
|
|
XMEMCPY(tstInfo->imprint.hash, hash, hashSz);
|
|
tstInfo->imprint.hashSz = hashSz;
|
|
|
|
return 0;
|
|
}
|
|
#endif /* WOLFSSL_TSP_RESPONDER */
|
|
|
|
#if defined(WOLFSSL_TSP_RESPONDER) || defined(WOLFSSL_TSP_VERIFIER)
|
|
/* Get the time of the time-stamp of a TSTInfo.
|
|
*
|
|
* Returns a reference to the genTime as a GeneralizedTime string of RFC 3161:
|
|
* "YYYYMMDDhhmmss[.s...]Z" - not copied and valid while the TSTInfo references
|
|
* it.
|
|
*
|
|
* @param [in] tstInfo TSTInfo object.
|
|
* @param [out] genTime Time as a GeneralizedTime string.
|
|
* @param [out] genTimeSz Length of string in bytes.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when tstInfo, genTime or genTimeSz is NULL.
|
|
*/
|
|
int wc_TspTstInfo_GetGenTime(const TspTstInfo* tstInfo, const byte** genTime,
|
|
word32* genTimeSz)
|
|
{
|
|
/* Validate parameters. */
|
|
if ((tstInfo == NULL) || (genTime == NULL) || (genTimeSz == NULL)) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
*genTime = tstInfo->genTime;
|
|
*genTimeSz = tstInfo->genTimeSz;
|
|
|
|
return 0;
|
|
}
|
|
#endif /* WOLFSSL_TSP_RESPONDER || WOLFSSL_TSP_VERIFIER */
|
|
|
|
#ifdef WOLFSSL_TSP_RESPONDER
|
|
/* Set the time of the time-stamp of a TSTInfo.
|
|
*
|
|
* The genTime is a GeneralizedTime string of RFC 3161 - it is referenced, not
|
|
* copied, and must remain available while the TSTInfo is used. The syntax is
|
|
* checked on encode. Leave unset to use the current time on encode.
|
|
*
|
|
* @param [in, out] tstInfo TSTInfo object.
|
|
* @param [in] genTime Time as a GeneralizedTime string.
|
|
* @param [in] genTimeSz Length of string in bytes.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when tstInfo or genTime is NULL or genTimeSz is 0.
|
|
*/
|
|
int wc_TspTstInfo_SetGenTime(TspTstInfo* tstInfo, const byte* genTime,
|
|
word32 genTimeSz)
|
|
{
|
|
/* Validate parameters. */
|
|
if ((tstInfo == NULL) || (genTime == NULL) || (genTimeSz == 0)) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
tstInfo->genTime = genTime;
|
|
tstInfo->genTimeSz = genTimeSz;
|
|
|
|
return 0;
|
|
}
|
|
#endif /* WOLFSSL_TSP_RESPONDER */
|
|
|
|
#ifndef NO_ASN_TIME
|
|
#if defined(WOLFSSL_TSP_RESPONDER) || defined(WOLFSSL_TSP_VERIFIER)
|
|
/* Convert a broken-down UTC time to seconds since the Unix epoch.
|
|
*
|
|
* Computed directly rather than with mktime() - mktime() interprets the
|
|
* fields as local time and may overflow a 32-bit time_t in 2038.
|
|
*
|
|
* @param [in] year Year including century. e.g. 2026.
|
|
* @param [in] mon Month of year. 1-12.
|
|
* @param [in] day Day of month. 1-31.
|
|
* @param [in] hour Hour of day. 0-23.
|
|
* @param [in] min Minute of hour. 0-59.
|
|
* @param [in] sec Second of minute. 0-60.
|
|
* @return Seconds since 00:00:00 UTC, 1 January 1970.
|
|
*/
|
|
static time_t TspGenTimeToUnix(int year, int mon, int day, int hour, int min,
|
|
int sec)
|
|
{
|
|
/* Cumulative days before each month in a non-leap year. */
|
|
static const int monthDays[12] = {
|
|
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
|
|
};
|
|
/* Years contributing a leap day - exclude this year before March. */
|
|
int y = year - ((mon <= 2) ? 1 : 0);
|
|
int leapDays = y / 4 - y / 100 + y / 400 -
|
|
(1969 / 4 - 1969 / 100 + 1969 / 400);
|
|
|
|
return (time_t)(((((time_t)(year - 1970) * 365 + leapDays +
|
|
monthDays[mon - 1] + day - 1) * 24 + hour) * 60 + min) * 60 + sec);
|
|
}
|
|
|
|
/* Get the time of the time-stamp of a TSTInfo as a time_t.
|
|
*
|
|
* Parses the genTime GeneralizedTime string of RFC 3161 - any fraction of a
|
|
* second is ignored. The time is UTC.
|
|
*
|
|
* Not available when there is no time support - NO_ASN_TIME.
|
|
*
|
|
* @param [in] tstInfo TSTInfo object.
|
|
* @param [out] t Time of the time-stamp as seconds since the Unix
|
|
* epoch.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when tstInfo, its genTime or t is NULL.
|
|
* @return ASN_PARSE_E when the genTime string is not valid.
|
|
*/
|
|
int wc_TspTstInfo_GetGenTimeAsTime(const TspTstInfo* tstInfo, time_t* t)
|
|
{
|
|
int ret = 0;
|
|
const byte* g;
|
|
|
|
/* Validate parameters. */
|
|
if ((tstInfo == NULL) || (tstInfo->genTime == NULL) || (t == NULL)) {
|
|
ret = BAD_FUNC_ARG;
|
|
}
|
|
|
|
/* The genTime must be a valid GeneralizedTime to convert. */
|
|
if (ret == 0) {
|
|
ret = TspCheckGenTimeSyntax(tstInfo->genTime, tstInfo->genTimeSz);
|
|
}
|
|
if (ret == 0) {
|
|
/* Date and time digits checked by TspCheckGenTimeSyntax. */
|
|
g = tstInfo->genTime;
|
|
*t = TspGenTimeToUnix(
|
|
(g[0] - '0') * 1000 + (g[1] - '0') * 100 + (g[2] - '0') * 10 +
|
|
(g[3] - '0'),
|
|
(g[4] - '0') * 10 + (g[5] - '0'),
|
|
(g[6] - '0') * 10 + (g[7] - '0'),
|
|
(g[8] - '0') * 10 + (g[9] - '0'),
|
|
(g[10] - '0') * 10 + (g[11] - '0'),
|
|
(g[12] - '0') * 10 + (g[13] - '0'));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif /* WOLFSSL_TSP_RESPONDER || WOLFSSL_TSP_VERIFIER */
|
|
|
|
#ifdef WOLFSSL_TSP_RESPONDER
|
|
/* Set the time of the time-stamp of a TSTInfo from a time_t.
|
|
*
|
|
* Formats the time as a GeneralizedTime string of RFC 3161 into the caller's
|
|
* buffer and references it - the buffer must remain available while the
|
|
* TSTInfo is used and be at least ASN_GENERALIZED_TIME_SIZE bytes. The time
|
|
* is treated as UTC.
|
|
*
|
|
* Not available when there is no time support - NO_ASN_TIME.
|
|
*
|
|
* @param [in, out] tstInfo TSTInfo object.
|
|
* @param [in] t Time of the time-stamp as seconds since the Unix
|
|
* epoch.
|
|
* @param [out] buf Buffer to hold the formatted GeneralizedTime.
|
|
* @param [in] bufSz Length of buffer in bytes.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when tstInfo or buf is NULL.
|
|
* @return BUFFER_E when bufSz is too small for the GeneralizedTime string.
|
|
* @return ASN_TIME_E when the time could not be converted.
|
|
*/
|
|
int wc_TspTstInfo_SetGenTimeAsTime(TspTstInfo* tstInfo, time_t t, byte* buf,
|
|
word32 bufSz)
|
|
{
|
|
int ret = 0;
|
|
int n = 0;
|
|
struct tm* ts = NULL;
|
|
#ifdef NEED_TMP_TIME
|
|
struct tm tmpTimeStorage;
|
|
struct tm* tmpTime = &tmpTimeStorage;
|
|
#else
|
|
struct tm* tmpTime = NULL;
|
|
#endif
|
|
/* Needed in case XGMTIME does not use the tmpTime argument. */
|
|
(void)tmpTime;
|
|
|
|
/* Validate parameters. */
|
|
if ((tstInfo == NULL) || (buf == NULL)) {
|
|
ret = BAD_FUNC_ARG;
|
|
}
|
|
/* Buffer must hold "YYYYMMDDhhmmssZ" and a NUL from formatting. */
|
|
else if (bufSz < ASN_GENERALIZED_TIME_SIZE) {
|
|
ret = BUFFER_E;
|
|
}
|
|
|
|
if (ret == 0) {
|
|
/* Break the time down as UTC. */
|
|
ts = (struct tm*)XGMTIME(&t, tmpTime);
|
|
if ((ts == NULL) || ValidateGmtime(ts)) {
|
|
ret = ASN_TIME_E;
|
|
}
|
|
}
|
|
if (ret == 0) {
|
|
/* Format as a GeneralizedTime string of RFC 3161. */
|
|
n = XSNPRINTF((char*)buf, bufSz, "%04d%02d%02d%02d%02d%02dZ",
|
|
ts->tm_year + 1900, ts->tm_mon + 1, ts->tm_mday, ts->tm_hour,
|
|
ts->tm_min, ts->tm_sec);
|
|
/* Negative on error; >= bufSz when the time was truncated (e.g. a
|
|
* year beyond 9999 needs more than the 15 expected characters). */
|
|
if ((n < 0) || (n >= (int)bufSz)) {
|
|
ret = ASN_TIME_E;
|
|
}
|
|
}
|
|
if (ret == 0) {
|
|
tstInfo->genTime = buf;
|
|
/* Content length excludes the NUL terminator. */
|
|
tstInfo->genTimeSz = ASN_GENERALIZED_TIME_SIZE - 1;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif /* WOLFSSL_TSP_RESPONDER */
|
|
#endif /* !NO_ASN_TIME */
|
|
|
|
#if defined(WOLFSSL_TSP_RESPONDER) || defined(WOLFSSL_TSP_VERIFIER)
|
|
/* Get the accuracy of the time of a TSTInfo.
|
|
*
|
|
* The accuracy is the seconds, milliseconds and microseconds the genTime may
|
|
* be off by. Each output is optional - pass NULL to not retrieve it. A value
|
|
* of 0 means that part of the accuracy is not present.
|
|
*
|
|
* @param [in] tstInfo TSTInfo object.
|
|
* @param [out] seconds Accuracy in seconds. May be NULL.
|
|
* @param [out] millis Accuracy in milliseconds. May be NULL.
|
|
* @param [out] micros Accuracy in microseconds. May be NULL.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when tstInfo is NULL.
|
|
*/
|
|
int wc_TspTstInfo_GetAccuracy(const TspTstInfo* tstInfo, word32* seconds,
|
|
word16* millis, word16* micros)
|
|
{
|
|
/* Validate parameter. */
|
|
if (tstInfo == NULL) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
if (seconds != NULL) {
|
|
*seconds = tstInfo->accuracy.seconds;
|
|
}
|
|
if (millis != NULL) {
|
|
*millis = tstInfo->accuracy.millis;
|
|
}
|
|
if (micros != NULL) {
|
|
*micros = tstInfo->accuracy.micros;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif /* WOLFSSL_TSP_RESPONDER || WOLFSSL_TSP_VERIFIER */
|
|
|
|
#ifdef WOLFSSL_TSP_RESPONDER
|
|
/* Set the accuracy of the time of a TSTInfo.
|
|
*
|
|
* The accuracy is how far the genTime may be off. A value of 0 for a part
|
|
* means it is not present. Milliseconds and microseconds must be 1..999 -
|
|
* checked on encode.
|
|
*
|
|
* @param [in, out] tstInfo TSTInfo object.
|
|
* @param [in] seconds Accuracy in seconds.
|
|
* @param [in] millis Accuracy in milliseconds.
|
|
* @param [in] micros Accuracy in microseconds.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when tstInfo is NULL.
|
|
*/
|
|
int wc_TspTstInfo_SetAccuracy(TspTstInfo* tstInfo, word32 seconds,
|
|
word16 millis, word16 micros)
|
|
{
|
|
/* Validate parameter. */
|
|
if (tstInfo == NULL) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
tstInfo->accuracy.seconds = seconds;
|
|
tstInfo->accuracy.millis = millis;
|
|
tstInfo->accuracy.micros = micros;
|
|
|
|
return 0;
|
|
}
|
|
#endif /* WOLFSSL_TSP_RESPONDER */
|
|
|
|
#if defined(WOLFSSL_TSP_RESPONDER) || defined(WOLFSSL_TSP_VERIFIER)
|
|
/* Get the nonce of a TSTInfo.
|
|
*
|
|
* Returns a reference to the nonce - it is not copied and is valid while the
|
|
* TSTInfo references it. A length of 0 means no nonce is present.
|
|
*
|
|
* @param [in] tstInfo TSTInfo object.
|
|
* @param [out] nonce Nonce as a big-endian number.
|
|
* @param [out] nonceSz Length of nonce in bytes.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when tstInfo, nonce or nonceSz is NULL.
|
|
*/
|
|
int wc_TspTstInfo_GetNonce(const TspTstInfo* tstInfo, const byte** nonce,
|
|
word32* nonceSz)
|
|
{
|
|
/* Validate parameters. */
|
|
if ((tstInfo == NULL) || (nonce == NULL) || (nonceSz == NULL)) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
*nonce = tstInfo->nonce;
|
|
*nonceSz = tstInfo->nonceSz;
|
|
|
|
return 0;
|
|
}
|
|
#endif /* WOLFSSL_TSP_RESPONDER || WOLFSSL_TSP_VERIFIER */
|
|
|
|
#ifdef WOLFSSL_TSP_RESPONDER
|
|
/* Set the nonce of a TSTInfo.
|
|
*
|
|
* The nonce is referenced, not copied, and must remain available while the
|
|
* TSTInfo is used. It must match the request's nonce. Leading zero bytes are
|
|
* stripped, keeping at least one byte, so it has no leading zero byte and
|
|
* encodes - the request's decoded nonce is already in this form.
|
|
*
|
|
* @param [in, out] tstInfo TSTInfo object.
|
|
* @param [in] nonce Nonce as a big-endian number.
|
|
* @param [in] nonceSz Length of nonce in bytes.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when tstInfo or nonce is NULL or nonceSz is 0.
|
|
*/
|
|
int wc_TspTstInfo_SetNonce(TspTstInfo* tstInfo, const byte* nonce,
|
|
word32 nonceSz)
|
|
{
|
|
/* Validate parameters. */
|
|
if ((tstInfo == NULL) || (nonce == NULL) || (nonceSz == 0)) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
/* Strip leading zero bytes - keep at least one byte. */
|
|
while ((nonceSz > 1) && (nonce[0] == 0x00)) {
|
|
nonce++;
|
|
nonceSz--;
|
|
}
|
|
tstInfo->nonce = nonce;
|
|
tstInfo->nonceSz = nonceSz;
|
|
|
|
return 0;
|
|
}
|
|
#endif /* WOLFSSL_TSP_RESPONDER */
|
|
|
|
#if defined(WOLFSSL_TSP_RESPONDER) || defined(WOLFSSL_TSP_VERIFIER)
|
|
/* Get the TSA name of a TSTInfo.
|
|
*
|
|
* Returns a reference to the tsa - the DER encoding of a GeneralName - it is
|
|
* not copied and is valid while the TSTInfo references it. A length of 0
|
|
* means no TSA name is present.
|
|
*
|
|
* @param [in] tstInfo TSTInfo object.
|
|
* @param [out] tsa TSA name as the DER encoding of a GeneralName.
|
|
* @param [out] tsaSz Length of TSA name in bytes.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when tstInfo, tsa or tsaSz is NULL.
|
|
*/
|
|
int wc_TspTstInfo_GetTsa(const TspTstInfo* tstInfo, const byte** tsa,
|
|
word32* tsaSz)
|
|
{
|
|
/* Validate parameters. */
|
|
if ((tstInfo == NULL) || (tsa == NULL) || (tsaSz == NULL)) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
*tsa = tstInfo->tsa;
|
|
*tsaSz = tstInfo->tsaSz;
|
|
|
|
return 0;
|
|
}
|
|
#endif /* WOLFSSL_TSP_RESPONDER || WOLFSSL_TSP_VERIFIER */
|
|
|
|
#ifdef WOLFSSL_TSP_RESPONDER
|
|
/* Set the TSA name of a TSTInfo.
|
|
*
|
|
* The tsa is the DER encoding of a GeneralName - it is referenced, not
|
|
* copied, and must remain available while the TSTInfo is used.
|
|
*
|
|
* @param [in, out] tstInfo TSTInfo object.
|
|
* @param [in] tsa TSA name as the DER encoding of a GeneralName.
|
|
* @param [in] tsaSz Length of TSA name in bytes.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when tstInfo or tsa is NULL or tsaSz is 0.
|
|
*/
|
|
int wc_TspTstInfo_SetTsa(TspTstInfo* tstInfo, const byte* tsa, word32 tsaSz)
|
|
{
|
|
/* Validate parameters. */
|
|
if ((tstInfo == NULL) || (tsa == NULL) || (tsaSz == 0)) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
tstInfo->tsa = tsa;
|
|
tstInfo->tsaSz = tsaSz;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Set the values of a TSTInfo to respond to a request.
|
|
*
|
|
* A convenience for a TSA building a response: echoes the request's message
|
|
* imprint (copied) and nonce (referenced), and sets the TSA's policy, serial
|
|
* number and time. The TSTInfo should be initialized with
|
|
* wc_TspTstInfo_Init() first. The request, policy, serial and genTime buffers
|
|
* are referenced - not copied (except the imprint) - and must remain available
|
|
* while the TSTInfo is used.
|
|
*
|
|
* @param [in, out] tstInfo TSTInfo object to set.
|
|
* @param [in] req Decoded request being time-stamped.
|
|
* @param [in] policy TSA policy as OBJECT IDENTIFIER content.
|
|
* @param [in] policySz Length of policy in bytes.
|
|
* @param [in] serial Serial number of the time-stamp - big-endian.
|
|
* Leading zero bytes are stripped.
|
|
* @param [in] serialSz Length of serial in bytes.
|
|
* @param [in] genTime Time of the time-stamp as a GeneralizedTime
|
|
* string. NULL to use the current time on encode.
|
|
* @param [in] genTimeSz Length of genTime in bytes - 0 when NULL.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when tstInfo, req, policy or serial is NULL or
|
|
* policySz or serialSz is 0.
|
|
*/
|
|
int wc_TspTstInfo_SetFromRequest(TspTstInfo* tstInfo, const TspRequest* req,
|
|
const byte* policy, word32 policySz, const byte* serial, word32 serialSz,
|
|
const byte* genTime, word32 genTimeSz)
|
|
{
|
|
int ret = 0;
|
|
|
|
/* Validate parameters - genTime is optional. */
|
|
if ((tstInfo == NULL) || (req == NULL) || (policy == NULL) ||
|
|
(policySz == 0) || (serial == NULL) || (serialSz == 0)) {
|
|
ret = BAD_FUNC_ARG;
|
|
}
|
|
|
|
if (ret == 0) {
|
|
/* Set the serial through the setter so a leading zero pad byte of a
|
|
* positive value with the high bit set is stripped. */
|
|
ret = wc_TspTstInfo_SetSerial(tstInfo, serial, serialSz);
|
|
}
|
|
if (ret == 0) {
|
|
tstInfo->policy = policy;
|
|
tstInfo->policySz = policySz;
|
|
/* Echo the requester's message imprint - copies the embedded hash. */
|
|
tstInfo->imprint = req->imprint;
|
|
/* NULL genTime uses the current time when encoding. */
|
|
tstInfo->genTime = genTime;
|
|
tstInfo->genTimeSz = genTimeSz;
|
|
/* Echo the nonce when the request has one. */
|
|
if (req->nonceSz != 0) {
|
|
tstInfo->nonce = req->nonce;
|
|
tstInfo->nonceSz = req->nonceSz;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif /* WOLFSSL_TSP_RESPONDER */
|
|
|
|
#if !defined(NO_ASN_TIME) && !defined(USER_TIME) && !defined(TIME_OVERRIDES)
|
|
#ifdef WOLFSSL_TSP_VERIFIER
|
|
/* Check the genTime of a TSTInfo is close enough to the current time.
|
|
*
|
|
* RFC 3161, 2.4.2: the requester verifies the genTime is within an
|
|
* acceptable period of the local trusted time. GeneralizedTime strings of
|
|
* the same form compare as times - the bounds of the acceptable period are
|
|
* formatted and compared as strings. Any fraction of a second in the
|
|
* genTime is ignored.
|
|
*
|
|
* Not available when there is no real time clock - NO_ASN_TIME, USER_TIME
|
|
* or TIME_OVERRIDES.
|
|
*
|
|
* @param [in] tstInfo Decoded TSTInfo object from response.
|
|
* @param [in] tolerance Acceptable time around the current time in
|
|
* seconds.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when tstInfo or its genTime is NULL.
|
|
* @return ASN_PARSE_E when the genTime string is not valid.
|
|
* @return ASN_TIME_E when getting the current time failed.
|
|
* @return TSP_VERIFY_E when the genTime is outside the acceptable period.
|
|
*/
|
|
int wc_TspTstInfo_CheckGenTime(const TspTstInfo* tstInfo, word32 tolerance)
|
|
{
|
|
int ret = 0;
|
|
time_t now;
|
|
time_t bound;
|
|
byte lo[ASN_GENERALIZED_TIME_SIZE];
|
|
byte hi[ASN_GENERALIZED_TIME_SIZE];
|
|
|
|
WOLFSSL_ENTER("wc_TspTstInfo_CheckGenTime");
|
|
|
|
/* Validate parameters. */
|
|
if ((tstInfo == NULL) || (tstInfo->genTime == NULL)) {
|
|
ret = BAD_FUNC_ARG;
|
|
}
|
|
|
|
/* The genTime must be a valid time to compare. */
|
|
if (ret == 0) {
|
|
ret = TspCheckGenTimeSyntax(tstInfo->genTime, tstInfo->genTimeSz);
|
|
}
|
|
if (ret == 0) {
|
|
/* Format the bounds of the acceptable period. */
|
|
now = wc_Time(0);
|
|
bound = now - (time_t)tolerance;
|
|
if (GetFormattedTime_ex(&bound, lo, sizeof(lo),
|
|
ASN_GENERALIZED_TIME) <= 0) {
|
|
ret = ASN_TIME_E;
|
|
}
|
|
bound = now + (time_t)tolerance;
|
|
if ((ret == 0) && (GetFormattedTime_ex(&bound, hi, sizeof(hi),
|
|
ASN_GENERALIZED_TIME) <= 0)) {
|
|
ret = ASN_TIME_E;
|
|
}
|
|
}
|
|
/* Compare the date and time digits - fraction of a second ignored. */
|
|
if ((ret == 0) && ((XMEMCMP(tstInfo->genTime, lo, 14) < 0) ||
|
|
(XMEMCMP(tstInfo->genTime, hi, 14) > 0))) {
|
|
WOLFSSL_MSG("TSP genTime is outside the acceptable period");
|
|
ret = TSP_VERIFY_E;
|
|
}
|
|
|
|
WOLFSSL_LEAVE("wc_TspTstInfo_CheckGenTime", ret);
|
|
return ret;
|
|
}
|
|
#endif /* WOLFSSL_TSP_VERIFIER */
|
|
#endif /* !NO_ASN_TIME && !USER_TIME && !TIME_OVERRIDES */
|
|
|
|
#ifdef WOLFSSL_TSP_VERIFIER
|
|
/* Check the TSTInfo of a response against the request sent.
|
|
*
|
|
* Checks the version, that the message imprint is the same and, when in the
|
|
* request, that the nonce and policy are matched. RFC 3161, 2.4.2.
|
|
*
|
|
* The genTime and the token's signature are not validated here.
|
|
*
|
|
* @param [in] tstInfo Decoded TSTInfo object from response.
|
|
* @param [in] req TimeStampReq object sent.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when tstInfo or req is NULL.
|
|
* @return ASN_VERSION_E when the version is not supported.
|
|
* @return TSP_VERIFY_E when a field of the TSTInfo does not match the
|
|
* request.
|
|
*/
|
|
int wc_TspTstInfo_CheckRequest(const TspTstInfo* tstInfo, const TspRequest* req)
|
|
{
|
|
int ret = 0;
|
|
|
|
WOLFSSL_ENTER("wc_TspTstInfo_CheckRequest");
|
|
|
|
/* Validate parameters. */
|
|
if ((tstInfo == NULL) || (req == NULL)) {
|
|
ret = BAD_FUNC_ARG;
|
|
}
|
|
|
|
/* Only version 1 defined. */
|
|
if ((ret == 0) && (tstInfo->version != WC_TSP_VERSION)) {
|
|
ret = ASN_VERSION_E;
|
|
}
|
|
/* Message imprint must be the same as the request - same hash
|
|
* algorithm ... */
|
|
if ((ret == 0) &&
|
|
(tstInfo->imprint.hashAlgOID != req->imprint.hashAlgOID)) {
|
|
ret = TSP_VERIFY_E;
|
|
}
|
|
/* ... and same hash of data. */
|
|
if ((ret == 0) &&
|
|
((tstInfo->imprint.hashSz != req->imprint.hashSz) ||
|
|
(XMEMCMP(tstInfo->imprint.hash, req->imprint.hash,
|
|
req->imprint.hashSz) != 0))) {
|
|
ret = TSP_VERIFY_E;
|
|
}
|
|
/* Nonce must be returned when in request - compared exactly. */
|
|
if ((ret == 0) && (req->nonceSz != 0) &&
|
|
((tstInfo->nonce == NULL) ||
|
|
(tstInfo->nonceSz != req->nonceSz) ||
|
|
(XMEMCMP(tstInfo->nonce, req->nonce, req->nonceSz) != 0))) {
|
|
ret = TSP_VERIFY_E;
|
|
}
|
|
/* Policy must match when requested. */
|
|
if ((ret == 0) && (req->policySz != 0) &&
|
|
((tstInfo->policy == NULL) ||
|
|
(tstInfo->policySz != req->policySz) ||
|
|
(XMEMCMP(tstInfo->policy, req->policy, req->policySz) != 0))) {
|
|
ret = TSP_VERIFY_E;
|
|
}
|
|
|
|
WOLFSSL_LEAVE("wc_TspTstInfo_CheckRequest", ret);
|
|
return ret;
|
|
}
|
|
#endif /* WOLFSSL_TSP_VERIFIER */
|
|
|
|
#ifdef WOLFSSL_TSP_VERIFIER
|
|
/* Check the TSA name of a TSTInfo is the expected name.
|
|
*
|
|
* The TSA name must be present and be the same encoding as the expected
|
|
* name - the DER encodings of the GeneralNames are compared exactly.
|
|
*
|
|
* @param [in] tstInfo Decoded TSTInfo object from response.
|
|
* @param [in] tsa Expected name: DER encoding of GeneralName.
|
|
* @param [in] tsaSz Length of expected name in bytes.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when tstInfo or tsa is NULL or tsaSz is 0.
|
|
* @return TSP_VERIFY_E when the TSA name is not present or does not match
|
|
* the expected name.
|
|
*/
|
|
int wc_TspTstInfo_CheckTsaName(const TspTstInfo* tstInfo, const byte* tsa,
|
|
word32 tsaSz)
|
|
{
|
|
int ret = 0;
|
|
|
|
WOLFSSL_ENTER("wc_TspTstInfo_CheckTsaName");
|
|
|
|
/* Validate parameters. */
|
|
if ((tstInfo == NULL) || (tsa == NULL) || (tsaSz == 0)) {
|
|
ret = BAD_FUNC_ARG;
|
|
}
|
|
|
|
/* TSA name must be present and exactly the expected encoding. */
|
|
if ((ret == 0) && ((tstInfo->tsa == NULL) || (tstInfo->tsaSz != tsaSz) ||
|
|
(XMEMCMP(tstInfo->tsa, tsa, tsaSz) != 0))) {
|
|
WOLFSSL_MSG("TSP TSA name is not the expected name");
|
|
ret = TSP_VERIFY_E;
|
|
}
|
|
|
|
WOLFSSL_LEAVE("wc_TspTstInfo_CheckTsaName", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* Verify the message imprint of a TSTInfo against the original data.
|
|
*
|
|
* Hashes the data with the TSTInfo's message imprint hash algorithm and
|
|
* compares the result to the imprint hash - confirming the time-stamp is over
|
|
* the given data. The caller does not need to hash the data first.
|
|
*
|
|
* @param [in] tstInfo TSTInfo object.
|
|
* @param [in] data Data that was time-stamped.
|
|
* @param [in] dataSz Length of data in bytes.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when tstInfo or data is NULL.
|
|
* @return HASH_TYPE_E when the imprint's hash algorithm is not supported.
|
|
* @return TSP_VERIFY_E when the hash of the data does not match the imprint.
|
|
* @return Other negative value on hashing failure.
|
|
*/
|
|
int wc_TspTstInfo_VerifyData(const TspTstInfo* tstInfo, const byte* data,
|
|
word32 dataSz)
|
|
{
|
|
int ret = 0;
|
|
enum wc_HashType hashType = WC_HASH_TYPE_NONE;
|
|
int digestSz = 0;
|
|
byte digest[WC_MAX_DIGEST_SIZE];
|
|
|
|
WOLFSSL_ENTER("wc_TspTstInfo_VerifyData");
|
|
|
|
/* Validate parameters. */
|
|
if ((tstInfo == NULL) || (data == NULL)) {
|
|
ret = BAD_FUNC_ARG;
|
|
}
|
|
|
|
if (ret == 0) {
|
|
/* Determine the hash algorithm of the message imprint. */
|
|
hashType = wc_OidGetHash((int)tstInfo->imprint.hashAlgOID);
|
|
digestSz = wc_HashGetDigestSize(hashType);
|
|
if (digestSz <= 0) {
|
|
ret = HASH_TYPE_E;
|
|
}
|
|
}
|
|
/* The imprint length must match the algorithm's digest size. */
|
|
if ((ret == 0) && (tstInfo->imprint.hashSz != (word32)digestSz)) {
|
|
ret = TSP_VERIFY_E;
|
|
}
|
|
if (ret == 0) {
|
|
/* Hash the data and compare to the message imprint. */
|
|
ret = wc_Hash(hashType, data, dataSz, digest, (word32)digestSz);
|
|
}
|
|
if ((ret == 0) && (XMEMCMP(digest, tstInfo->imprint.hash,
|
|
(word32)digestSz) != 0)) {
|
|
WOLFSSL_MSG("TSP data does not match the message imprint");
|
|
ret = TSP_VERIFY_E;
|
|
}
|
|
|
|
WOLFSSL_LEAVE("wc_TspTstInfo_VerifyData", ret);
|
|
return ret;
|
|
}
|
|
#endif /* WOLFSSL_TSP_VERIFIER */
|
|
|
|
#ifdef HAVE_PKCS7
|
|
|
|
/* id-ct-TSTInfo: 1.2.840.113549.1.9.16.1.4. RFC 3161 - the content type of
|
|
* a time-stamp token. Used by wc_TspTstInfo_SignWithPkcs7() and
|
|
* wc_TspTstInfo_VerifyWithPKCS7(). */
|
|
static const byte tspTstInfoOid[] = {
|
|
ASN_OBJECT_ID, 0x0b,
|
|
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x10, 0x01, 0x04
|
|
};
|
|
|
|
#ifdef WOLFSSL_TSP_RESPONDER
|
|
/* Create a TimeStampToken signed with the TSA's certificate and private key.
|
|
*
|
|
* Convenience wrapper around wc_TspTstInfo_SignWithPkcs7() that creates and
|
|
* disposes of the PKCS7 object. The TSA's certificate is included in the
|
|
* token.
|
|
*
|
|
* @param [in] tstInfo TSTInfo object to encode and sign.
|
|
* @param [in] cert DER encoded certificate of the TSA.
|
|
* @param [in] certSz Length of certificate in bytes.
|
|
* @param [in] key DER encoded private key of the TSA.
|
|
* @param [in] keySz Length of private key in bytes.
|
|
* @param [in] keyType Type of the private key - WC_PK_TYPE_RSA or
|
|
* WC_PK_TYPE_ECDSA_SIGN.
|
|
* @param [in] hashType Hash algorithm for the signature - e.g.
|
|
* WC_HASH_TYPE_SHA256.
|
|
* @param [in] rng Random number generator.
|
|
* @param [out] out Buffer to hold encoding.
|
|
* @param [in, out] outSz On in, length of buffer in bytes.
|
|
* On out, length of encoding in bytes.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when a pointer argument is NULL, a length is 0 or the
|
|
* key type is not supported.
|
|
* @return HASH_TYPE_E when the hash algorithm is not available.
|
|
* @return BUFFER_E when the encoding is longer than outSz.
|
|
* @return MEMORY_E on dynamic memory allocation failure.
|
|
*/
|
|
int wc_TspTstInfo_Sign(const TspTstInfo* tstInfo,
|
|
const byte* cert, word32 certSz, const byte* key, word32 keySz,
|
|
enum wc_PkType keyType, enum wc_HashType hashType, WC_RNG* rng,
|
|
byte* out, word32* outSz)
|
|
{
|
|
int ret = 0;
|
|
#ifdef WOLFSSL_NO_MALLOC
|
|
/* No dynamic memory - the PKCS7 object is on the stack. */
|
|
wc_PKCS7 pkcs7Obj;
|
|
wc_PKCS7* pkcs7 = &pkcs7Obj;
|
|
#else
|
|
wc_PKCS7* pkcs7 = NULL;
|
|
#endif
|
|
int hashOID = 0;
|
|
int encryptOID = 0;
|
|
|
|
WOLFSSL_ENTER("wc_TspTstInfo_Sign");
|
|
|
|
#ifdef WOLFSSL_NO_MALLOC
|
|
/* Zero the stack object up front - an early error returns through the
|
|
* unconditional wc_PKCS7_Free below, which must see isDynamic 0 and all
|
|
* pointers NULL so it does not free the stack object or wild pointers. */
|
|
XMEMSET(pkcs7, 0, sizeof(pkcs7Obj));
|
|
#endif
|
|
|
|
/* Validate parameters. */
|
|
if ((tstInfo == NULL) || (cert == NULL) || (certSz == 0) ||
|
|
(key == NULL) || (keySz == 0) || (rng == NULL) ||
|
|
(out == NULL) || (outSz == NULL)) {
|
|
ret = BAD_FUNC_ARG;
|
|
}
|
|
|
|
/* Map the key type to the signature algorithm OID. */
|
|
if (ret == 0) {
|
|
if (keyType == WC_PK_TYPE_RSA) {
|
|
encryptOID = RSAk;
|
|
}
|
|
#ifdef HAVE_ECC
|
|
else if (keyType == WC_PK_TYPE_ECDSA_SIGN) {
|
|
encryptOID = ECDSAk;
|
|
}
|
|
#endif
|
|
else {
|
|
WOLFSSL_MSG("TSP key type not supported");
|
|
ret = BAD_FUNC_ARG;
|
|
}
|
|
}
|
|
/* Map the hash type to its OID sum. */
|
|
if (ret == 0) {
|
|
hashOID = wc_HashGetOID(hashType);
|
|
if (hashOID <= 0) {
|
|
ret = HASH_TYPE_E;
|
|
}
|
|
}
|
|
|
|
if (ret == 0) {
|
|
#ifdef WOLFSSL_NO_MALLOC
|
|
ret = wc_PKCS7_Init(pkcs7, NULL, INVALID_DEVID);
|
|
#else
|
|
pkcs7 = wc_PKCS7_New(NULL, INVALID_DEVID);
|
|
if (pkcs7 == NULL) {
|
|
ret = MEMORY_E;
|
|
}
|
|
#endif
|
|
}
|
|
if (ret == 0) {
|
|
ret = wc_PKCS7_InitWithCert(pkcs7, (byte*)cert, certSz);
|
|
}
|
|
if (ret == 0) {
|
|
/* Configure the signer and sign the TSTInfo. */
|
|
pkcs7->rng = rng;
|
|
pkcs7->hashOID = hashOID;
|
|
pkcs7->encryptOID = encryptOID;
|
|
pkcs7->privateKey = (byte*)key;
|
|
pkcs7->privateKeySz = keySz;
|
|
|
|
ret = wc_TspTstInfo_SignWithPkcs7(tstInfo, pkcs7, out, outSz);
|
|
}
|
|
|
|
wc_PKCS7_Free(pkcs7);
|
|
WOLFSSL_LEAVE("wc_TspTstInfo_Sign", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* Maximum size of an encoded SigningCertificateV2: 4 headers of 4 bytes and
|
|
* a hash algorithm of 15 bytes. */
|
|
#define TSP_MAX_SIGN_CERT_V2_SZ (4 * 4 + 15 + WC_MAX_DIGEST_SIZE)
|
|
|
|
#ifdef WOLFSSL_NO_MALLOC
|
|
/* Maximum size of an encoded TSTInfo when no dynamic memory. */
|
|
#ifndef WC_TSP_MAX_TSTINFO_SZ
|
|
#define WC_TSP_MAX_TSTINFO_SZ 512
|
|
#endif
|
|
/* Maximum number of signed attributes, including SigningCertificateV2, when
|
|
* no dynamic memory. */
|
|
#ifndef WC_TSP_MAX_SIGNED_ATTRIBS
|
|
#define WC_TSP_MAX_SIGNED_ATTRIBS 4
|
|
#endif
|
|
#endif
|
|
|
|
/* Create a TimeStampToken - CMS SignedData with TSTInfo content.
|
|
*
|
|
* The PKCS7 object must be initialized with the certificate and private key
|
|
* of the TSA, and the hash algorithm and RNG set. A SigningCertificateV2
|
|
* signed attribute is added as required by RFC 3161, 2.4.2.
|
|
*
|
|
* The TSA's certificate is included in the token. When the request did not
|
|
* set certReq the certificates must not be included - RFC 3161, 2.4.1 -
|
|
* set the PKCS7 object's noCerts field.
|
|
*
|
|
* @param [in] tstInfo TSTInfo object to encode and sign.
|
|
* @param [in] pkcs7 PKCS7 object with signer configured.
|
|
* @param [out] out Buffer to hold encoding.
|
|
* @param [in, out] outSz On in, length of buffer in bytes.
|
|
* On out, length of encoding in bytes.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when pkcs7, tstInfo, out or outSz is NULL or the
|
|
* signer's certificate is not set or, when no dynamic memory, there
|
|
* are more signed attributes than WC_TSP_MAX_SIGNED_ATTRIBS.
|
|
* @return BUFFER_E when no dynamic memory and the encoded TSTInfo is longer
|
|
* than WC_TSP_MAX_TSTINFO_SZ.
|
|
* @return MEMORY_E on dynamic memory allocation failure.
|
|
*/
|
|
int wc_TspTstInfo_SignWithPkcs7(const TspTstInfo* tstInfo, wc_PKCS7* pkcs7,
|
|
byte* out, word32* outSz)
|
|
{
|
|
int ret = 0;
|
|
#ifdef WOLFSSL_NO_MALLOC
|
|
byte tstDer[WC_TSP_MAX_TSTINFO_SZ];
|
|
PKCS7Attrib attribs[WC_TSP_MAX_SIGNED_ATTRIBS];
|
|
#else
|
|
byte* tstDer = NULL;
|
|
PKCS7Attrib* attribs = NULL;
|
|
#endif
|
|
word32 tstDerSz = 0;
|
|
WC_DECLARE_VAR(signCert, byte, TSP_MAX_SIGN_CERT_V2_SZ, pkcs7->heap);
|
|
word32 signCertSz = TSP_MAX_SIGN_CERT_V2_SZ;
|
|
word32 cnt = 0;
|
|
|
|
WOLFSSL_ENTER("wc_TspTstInfo_SignWithPkcs7");
|
|
|
|
/* Validate parameters. */
|
|
if ((pkcs7 == NULL) || (tstInfo == NULL) || (out == NULL) ||
|
|
(outSz == NULL)) {
|
|
ret = BAD_FUNC_ARG;
|
|
}
|
|
/* The signer's certificate is hashed into a signed attribute. */
|
|
if ((ret == 0) && ((pkcs7->singleCert == NULL) ||
|
|
(pkcs7->singleCertSz == 0))) {
|
|
ret = BAD_FUNC_ARG;
|
|
}
|
|
|
|
if (ret == 0) {
|
|
WC_ALLOC_VAR_EX(signCert, byte, TSP_MAX_SIGN_CERT_V2_SZ, pkcs7->heap,
|
|
DYNAMIC_TYPE_TMP_BUFFER, ret = MEMORY_E);
|
|
}
|
|
|
|
/* Encode TSTInfo as the content. */
|
|
#ifdef WOLFSSL_NO_MALLOC
|
|
if (ret == 0) {
|
|
/* Fixed size buffer on the stack. */
|
|
tstDerSz = (word32)sizeof(tstDer);
|
|
}
|
|
#else
|
|
if (ret == 0) {
|
|
/* Get the length of the encoding to allocate. */
|
|
ret = wc_TspTstInfo_Encode(tstInfo, NULL, &tstDerSz);
|
|
}
|
|
if (ret == 0) {
|
|
tstDer = (byte*)XMALLOC(tstDerSz, pkcs7->heap,
|
|
DYNAMIC_TYPE_TMP_BUFFER);
|
|
if (tstDer == NULL) {
|
|
ret = MEMORY_E;
|
|
}
|
|
}
|
|
#endif
|
|
if (ret == 0) {
|
|
ret = wc_TspTstInfo_Encode(tstInfo, tstDer, &tstDerSz);
|
|
}
|
|
/* Hash of signer's certificate in signed attribute. */
|
|
if (ret == 0) {
|
|
ret = TspEncodeSigningCertV2(pkcs7->hashOID, pkcs7->singleCert,
|
|
pkcs7->singleCertSz, signCert, &signCertSz, pkcs7->heap);
|
|
}
|
|
/* Add SigningCertificateV2 to user's signed attributes. */
|
|
if (ret == 0) {
|
|
cnt = pkcs7->signedAttribsSz;
|
|
#ifdef WOLFSSL_NO_MALLOC
|
|
/* Check fixed size array is big enough for one more. */
|
|
if (cnt + 1 > WC_TSP_MAX_SIGNED_ATTRIBS) {
|
|
ret = BAD_FUNC_ARG;
|
|
}
|
|
#else
|
|
/* Allocate array for user's attributes and one more. */
|
|
attribs = (PKCS7Attrib*)XMALLOC((cnt + 1) * sizeof(PKCS7Attrib),
|
|
pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER);
|
|
if (attribs == NULL) {
|
|
ret = MEMORY_E;
|
|
}
|
|
#endif
|
|
}
|
|
if (ret == 0) {
|
|
/* Copy in user's attributes and append SigningCertificateV2. */
|
|
if (cnt > 0) {
|
|
XMEMCPY(attribs, pkcs7->signedAttribs,
|
|
cnt * sizeof(PKCS7Attrib));
|
|
}
|
|
attribs[cnt].oid = tspSigningCertV2Oid;
|
|
attribs[cnt].oidSz = (word32)sizeof(tspSigningCertV2Oid);
|
|
attribs[cnt].value = signCert;
|
|
attribs[cnt].valueSz = signCertSz;
|
|
}
|
|
if (ret == 0) {
|
|
/* Sign TSTInfo keeping original PKCS7 object fields. */
|
|
byte* content = pkcs7->content;
|
|
word32 contentSz = pkcs7->contentSz;
|
|
int contentOID = pkcs7->contentOID;
|
|
byte contentType[MAX_OID_SZ];
|
|
word32 contentTypeSz = pkcs7->contentTypeSz;
|
|
PKCS7Attrib* signedAttribs = pkcs7->signedAttribs;
|
|
word32 signedAttribsSz = pkcs7->signedAttribsSz;
|
|
|
|
XMEMCPY(contentType, pkcs7->contentType, MAX_OID_SZ);
|
|
|
|
/* TSTInfo encoding is the content to be signed. */
|
|
pkcs7->content = tstDer;
|
|
pkcs7->contentSz = tstDerSz;
|
|
pkcs7->contentOID = TSTINFO_DATA;
|
|
pkcs7->signedAttribs = attribs;
|
|
pkcs7->signedAttribsSz = cnt + 1;
|
|
|
|
/* Content type written and put in signed attributes. */
|
|
ret = wc_PKCS7_SetContentType(pkcs7, (byte*)tspTstInfoOid,
|
|
(word32)sizeof(tspTstInfoOid));
|
|
if (ret == 0) {
|
|
/* Encode CMS SignedData - returns length of encoding. */
|
|
ret = wc_PKCS7_EncodeSignedData(pkcs7, out, *outSz);
|
|
}
|
|
|
|
/* Restore caller's PKCS7 object fields. */
|
|
pkcs7->content = content;
|
|
pkcs7->contentSz = contentSz;
|
|
pkcs7->contentOID = contentOID;
|
|
pkcs7->signedAttribs = signedAttribs;
|
|
pkcs7->signedAttribsSz = signedAttribsSz;
|
|
XMEMCPY(pkcs7->contentType, contentType, MAX_OID_SZ);
|
|
pkcs7->contentTypeSz = contentTypeSz;
|
|
|
|
if (ret > 0) {
|
|
/* Return the length of the encoding. */
|
|
*outSz = (word32)ret;
|
|
ret = 0;
|
|
}
|
|
else if (ret == 0) {
|
|
/* Zero length encoding is not valid. */
|
|
ret = BAD_STATE_E;
|
|
}
|
|
}
|
|
|
|
#ifndef WOLFSSL_NO_MALLOC
|
|
XFREE(attribs, (pkcs7 != NULL) ? pkcs7->heap : NULL,
|
|
DYNAMIC_TYPE_TMP_BUFFER);
|
|
XFREE(tstDer, (pkcs7 != NULL) ? pkcs7->heap : NULL,
|
|
DYNAMIC_TYPE_TMP_BUFFER);
|
|
#endif
|
|
WC_FREE_VAR_EX(signCert, (pkcs7 != NULL) ? pkcs7->heap : NULL,
|
|
DYNAMIC_TYPE_TMP_BUFFER);
|
|
WOLFSSL_LEAVE("wc_TspTstInfo_SignWithPkcs7", ret);
|
|
return ret;
|
|
}
|
|
|
|
#endif /* WOLFSSL_TSP_RESPONDER */
|
|
|
|
#ifdef WOLFSSL_TSP_VERIFIER
|
|
|
|
/* Check a hash algorithm meets the minimum security strength.
|
|
*
|
|
* The collision resistance of a hash is half the digest length in bits -
|
|
* digest size in bytes * 4. With WC_TSP_MIN_HASH_STRENGTH_BITS of 0 any
|
|
* available hash algorithm is acceptable.
|
|
*
|
|
* This is a strength check, not an availability check: with the default
|
|
* WC_TSP_MIN_HASH_STRENGTH_BITS of 0 it returns 0 for an unavailable OID.
|
|
* Callers that go on to use the algorithm independently reject an unavailable
|
|
* hash via wc_HashGetDigestSize() - see TspCheckSigningCertAttr() and
|
|
* wc_TspTstInfo_VerifyData().
|
|
*
|
|
* Not static - also used by TspCheckSigningCertAttr() in asn_tsp.c (declared
|
|
* in tsp.h).
|
|
*
|
|
* @param [in] hashOID Hash algorithm OID sum.
|
|
* @return 0 when strong enough.
|
|
* @return HASH_TYPE_E when not available or below
|
|
* WC_TSP_MIN_HASH_STRENGTH_BITS.
|
|
*/
|
|
int Tsp_CheckHashStrength(word32 hashOID)
|
|
{
|
|
int ret = 0;
|
|
#if WC_TSP_MIN_HASH_STRENGTH_BITS > 0
|
|
int digestSz = wc_HashGetDigestSize(wc_OidGetHash((int)hashOID));
|
|
|
|
if ((digestSz <= 0) ||
|
|
((digestSz * 4) < WC_TSP_MIN_HASH_STRENGTH_BITS)) {
|
|
WOLFSSL_MSG("TSP hash algorithm below minimum security strength");
|
|
ret = HASH_TYPE_E;
|
|
}
|
|
#else
|
|
(void)hashOID;
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
/* Check the TSA name corresponds to a subject name of the signer's
|
|
* certificate.
|
|
*
|
|
* RFC 3161, 2.4.2: the tsa field, when present, must correspond to one of
|
|
* the subject names included in the certificate that is to be used to
|
|
* verify the token.
|
|
*
|
|
* A directoryName is checked against the subject name and other supported
|
|
* GeneralName forms against the subject alternative names.
|
|
*
|
|
* @param [in] dCert Decoded certificate of signer.
|
|
* @param [in] tsa DER encoding of GeneralName.
|
|
* @param [in] tsaSz Length of GeneralName in bytes.
|
|
* @return 0 on success.
|
|
* @return TSP_VERIFY_E when the name does not match the certificate or the
|
|
* form of name is not supported.
|
|
* @return ASN_PARSE_E when the encoding is invalid.
|
|
*/
|
|
static int Tsp_CheckTsaName(DecodedCert* dCert, const byte* tsa, word32 tsaSz)
|
|
{
|
|
int ret = 0;
|
|
word32 idx = 0;
|
|
byte tag = 0;
|
|
int len = 0;
|
|
|
|
/* Get header of the one GeneralName. */
|
|
if ((GetASNTag(tsa, &idx, &tag, tsaSz) < 0) ||
|
|
(GetLength(tsa, &idx, &len, tsaSz) < 0) ||
|
|
(idx + (word32)len != tsaSz)) {
|
|
ret = ASN_PARSE_E;
|
|
}
|
|
/* directoryName [4] - explicitly tagged Name. */
|
|
else if (tag == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED |
|
|
ASN_DIR_TYPE)) {
|
|
#if !defined(IGNORE_NAME_CONSTRAINTS) || defined(WOLFSSL_CERT_EXT)
|
|
byte nameTag = 0;
|
|
int nameLen = 0;
|
|
|
|
/* Step into the Name to compare contents of SEQUENCE. */
|
|
if ((GetASNTag(tsa, &idx, &nameTag, tsaSz) < 0) ||
|
|
(nameTag != (ASN_SEQUENCE | ASN_CONSTRUCTED)) ||
|
|
(GetLength(tsa, &idx, &nameLen, tsaSz) < 0) ||
|
|
(idx + (word32)nameLen != tsaSz)) {
|
|
ret = ASN_PARSE_E;
|
|
}
|
|
/* Compare with the subject name of the signer's certificate. */
|
|
else if ((dCert->subjectRaw == NULL) ||
|
|
(nameLen != dCert->subjectRawLen) ||
|
|
(XMEMCMP(tsa + idx, dCert->subjectRaw,
|
|
(size_t)nameLen) != 0)) {
|
|
WOLFSSL_MSG("TSP TSA name doesn't match signer's subject");
|
|
ret = TSP_VERIFY_E;
|
|
}
|
|
#else
|
|
/* No raw subject name to compare against. */
|
|
WOLFSSL_MSG("TSP TSA name check requires raw subject name");
|
|
ret = TSP_VERIFY_E;
|
|
#endif
|
|
}
|
|
/* Name forms of the subject alternative names extension. */
|
|
else if ((tag == (ASN_CONTEXT_SPECIFIC | ASN_RFC822_TYPE)) ||
|
|
(tag == (ASN_CONTEXT_SPECIFIC | ASN_DNS_TYPE)) ||
|
|
(tag == (ASN_CONTEXT_SPECIFIC | ASN_URI_TYPE))) {
|
|
const DNS_entry* entry;
|
|
int type = (int)(tag & ~ASN_CONTEXT_SPECIFIC);
|
|
|
|
/* Compare against each subject alternative name of the form. */
|
|
ret = TSP_VERIFY_E;
|
|
for (entry = dCert->altNames; entry != NULL; entry = entry->next) {
|
|
if ((entry->type == type) && (entry->len == len) &&
|
|
(XMEMCMP(entry->name, tsa + idx, (size_t)len) == 0)) {
|
|
ret = 0;
|
|
break;
|
|
}
|
|
}
|
|
if (ret != 0) {
|
|
WOLFSSL_MSG("TSP TSA name not in signer's alternative names");
|
|
}
|
|
}
|
|
else {
|
|
/* Other forms of GeneralName are not supported. */
|
|
WOLFSSL_MSG("TSP TSA name form not supported");
|
|
ret = TSP_VERIFY_E;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Check the signer's certificate is valid for time-stamping.
|
|
*
|
|
* RFC 3161, 2.3: the TSA's certificate must have an extended key usage of
|
|
* id-kp-timeStamping only and the extension must be critical. The key
|
|
* usage, when present, must only be for signing.
|
|
*
|
|
* The TSA name of the TSTInfo, when present, must correspond to a subject
|
|
* name of the certificate. RFC 3161, 2.4.2.
|
|
*
|
|
* @param [in] cert DER encoded certificate of signer.
|
|
* @param [in] certSz Length of certificate in bytes.
|
|
* @param [in] tsa DER encoding of GeneralName from TSTInfo. May be NULL.
|
|
* @param [in] tsaSz Length of GeneralName in bytes.
|
|
* @param [in] heap Dynamic memory allocation hint.
|
|
* @return 0 on success.
|
|
* @return EXTKEYUSAGE_E when the extended key usage is not critical or not
|
|
* time-stamping only.
|
|
* @return KEYUSAGE_E when the key usage is not for signing only.
|
|
* @return TSP_VERIFY_E when the TSA name does not match the certificate.
|
|
* @return ASN_PARSE_E when an encoding is invalid.
|
|
* @return MEMORY_E on dynamic memory allocation failure.
|
|
*/
|
|
static int Tsp_CheckSignerCert(const byte* cert, word32 certSz,
|
|
const byte* tsa, word32 tsaSz, void* heap)
|
|
{
|
|
int ret = 0;
|
|
WC_DECLARE_VAR(dCert, DecodedCert, 1, heap);
|
|
|
|
WC_ALLOC_VAR_EX(dCert, DecodedCert, 1, heap, DYNAMIC_TYPE_DCERT,
|
|
return MEMORY_E);
|
|
|
|
/* Parse certificate for extensions and names - no chain verify. */
|
|
InitDecodedCert(dCert, cert, certSz, heap);
|
|
ret = ParseCertRelative(dCert, CERT_TYPE, NO_VERIFY, NULL, NULL);
|
|
if (ret == 0) {
|
|
/* Extended key usage must be critical and time-stamping only - the
|
|
* OID count catches an extra purpose whose OID is not recognized and
|
|
* so leaves no extra bit set in extExtKeyUsage. */
|
|
if ((!dCert->extExtKeyUsageSet) ||
|
|
(dCert->extExtKeyUsage != EXTKEYUSE_TIMESTAMP) ||
|
|
(dCert->extExtKeyUsageOidCnt != 1) ||
|
|
(!dCert->extExtKeyUsageCrit)) {
|
|
WOLFSSL_MSG("TSP signer's cert not for time-stamping only");
|
|
ret = EXTKEYUSAGE_E;
|
|
}
|
|
}
|
|
if (ret == 0) {
|
|
/* Key usage, when present, must be for signing only. */
|
|
if (dCert->extKeyUsageSet &&
|
|
(((dCert->extKeyUsage & (word16)~(KEYUSE_DIGITAL_SIG |
|
|
KEYUSE_CONTENT_COMMIT)) != 0) ||
|
|
((dCert->extKeyUsage & (KEYUSE_DIGITAL_SIG |
|
|
KEYUSE_CONTENT_COMMIT)) == 0))) {
|
|
WOLFSSL_MSG("TSP signer's cert key usage not signing only");
|
|
ret = KEYUSAGE_E;
|
|
}
|
|
}
|
|
/* TSA name, when present, must correspond to the certificate. */
|
|
if ((ret == 0) && (tsa != NULL)) {
|
|
ret = Tsp_CheckTsaName(dCert, tsa, tsaSz);
|
|
}
|
|
FreeDecodedCert(dCert);
|
|
|
|
WC_FREE_VAR_EX(dCert, heap, DYNAMIC_TYPE_DCERT);
|
|
return ret;
|
|
}
|
|
|
|
/* Verify a TimeStampToken and decode the TSTInfo content.
|
|
*
|
|
* The PKCS7 object must be initialized. The signature of the CMS SignedData
|
|
* is verified with the certificates in the token. When the token does not
|
|
* include certificates - certReq was not set in the request - initialize
|
|
* the PKCS7 object with the TSA's certificate. Trust in the TSA's
|
|
* certificate must be established by the caller.
|
|
*
|
|
* The signer's certificate must be valid for time-stamping only -
|
|
* RFC 3161, 2.3 - and be the certificate identified by the signing
|
|
* certificate attribute - RFC 3161, 2.4.2. Only the certHash of the first
|
|
* ESSCertID(v2) of the attribute is checked - any issuerSerial and further
|
|
* certificate identifiers are not used. The TSA name of the TSTInfo, when
|
|
* present, must correspond to a subject name of the signer's certificate -
|
|
* RFC 3161, 2.4.2.
|
|
*
|
|
* Pointers in tstInfo reference the content of the PKCS7 object - the
|
|
* PKCS7 object and the token buffer must remain available while tstInfo is
|
|
* in use.
|
|
*
|
|
* @param [in] pkcs7 Initialized PKCS7 object.
|
|
* @param [in, out] token Buffer holding DER encoding of token.
|
|
* @param [in] tokenSz Length of data in buffer in bytes.
|
|
* @param [out] tstInfo TSTInfo object to fill. May be NULL.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when pkcs7 or token is NULL or tokenSz is 0.
|
|
* @return PKCS7_OID_E when the content is not a TSTInfo.
|
|
* @return EXTKEYUSAGE_E when the signer's extended key usage is not
|
|
* critical or not time-stamping only.
|
|
* @return KEYUSAGE_E when the signer's key usage is not for signing only.
|
|
* @return TSP_VERIFY_E when the token does not have exactly one
|
|
* SignerInfo, no signing certificate attribute is found or it does
|
|
* not match the signer's certificate or the TSA name does not
|
|
* match the signer's certificate.
|
|
* @return HASH_TYPE_E when the signing certificate attribute's hash
|
|
* algorithm is not available or a hash algorithm is below
|
|
* WC_TSP_MIN_HASH_STRENGTH_BITS.
|
|
* @return ASN_PARSE_E when an encoding is invalid.
|
|
* @return MEMORY_E on dynamic memory allocation failure.
|
|
*/
|
|
int wc_TspTstInfo_VerifyWithPKCS7(wc_PKCS7* pkcs7, byte* token, word32 tokenSz,
|
|
TspTstInfo* tstInfo)
|
|
{
|
|
int ret = 0;
|
|
TspTstInfo tstDec;
|
|
|
|
WOLFSSL_ENTER("wc_TspTstInfo_VerifyWithPKCS7");
|
|
|
|
/* Validate parameters. */
|
|
if ((pkcs7 == NULL) || (token == NULL) || (tokenSz == 0)) {
|
|
ret = BAD_FUNC_ARG;
|
|
}
|
|
|
|
if (ret == 0) {
|
|
/* Token must have a single SignerInfo. */
|
|
ret = TspCheckOneSignerInfo(token, tokenSz, pkcs7->heap);
|
|
}
|
|
if (ret == 0) {
|
|
/* Verify signature of CMS SignedData. */
|
|
ret = wc_PKCS7_VerifySignedData(pkcs7, token, tokenSz);
|
|
}
|
|
/* Content type must be id-ct-TSTInfo. */
|
|
if ((ret == 0) && ((pkcs7->contentTypeSz != sizeof(tspTstInfoOid)) ||
|
|
(XMEMCMP(pkcs7->contentType, tspTstInfoOid,
|
|
sizeof(tspTstInfoOid)) != 0))) {
|
|
ret = PKCS7_OID_E;
|
|
}
|
|
/* The digest algorithm of the signature must meet the minimum
|
|
* strength. */
|
|
if (ret == 0) {
|
|
ret = Tsp_CheckHashStrength((word32)pkcs7->hashOID);
|
|
}
|
|
if (ret == 0) {
|
|
/* Decode the content as a TSTInfo - TSA name needed for checks. */
|
|
ret = wc_TspTstInfo_Decode(&tstDec, pkcs7->content, pkcs7->contentSz);
|
|
}
|
|
/* The hash algorithm of the imprint must meet the minimum strength. */
|
|
if (ret == 0) {
|
|
ret = Tsp_CheckHashStrength(tstDec.imprint.hashAlgOID);
|
|
}
|
|
/* Check the signer's certificate is valid for time-stamping. */
|
|
if (ret == 0) {
|
|
if (pkcs7->verifyCert == NULL) {
|
|
/* No certificate to check - must be in token. */
|
|
ret = TSP_VERIFY_E;
|
|
}
|
|
else {
|
|
ret = Tsp_CheckSignerCert(pkcs7->verifyCert, pkcs7->verifyCertSz,
|
|
tstDec.tsa, tstDec.tsaSz, pkcs7->heap);
|
|
}
|
|
}
|
|
/* Check the signing certificate attribute matches the signer. */
|
|
if (ret == 0) {
|
|
ret = TspCheckSigningCertAttr(pkcs7);
|
|
}
|
|
/* Return the decoded TSTInfo when requested. */
|
|
if ((ret == 0) && (tstInfo != NULL)) {
|
|
*tstInfo = tstDec;
|
|
}
|
|
|
|
WOLFSSL_LEAVE("wc_TspTstInfo_VerifyWithPKCS7", ret);
|
|
return ret;
|
|
}
|
|
|
|
#endif /* WOLFSSL_TSP_VERIFIER */
|
|
#endif /* HAVE_PKCS7 */
|
|
|
|
#ifdef WOLFSSL_TSP_RESPONDER
|
|
/* Initialize a TimeStampResp.
|
|
*
|
|
* @param [out] resp TimeStampResp object.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when resp is NULL.
|
|
*/
|
|
int wc_TspResponse_Init(TspResponse* resp)
|
|
{
|
|
/* Validate parameter. */
|
|
if (resp == NULL) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
/* All fields empty - status of 0 is granted. */
|
|
XMEMSET(resp, 0, sizeof(TspResponse));
|
|
|
|
return 0;
|
|
}
|
|
#endif /* WOLFSSL_TSP_RESPONDER */
|
|
|
|
#if defined(WOLFSSL_TSP_RESPONDER) || defined(WOLFSSL_TSP_VERIFIER)
|
|
/* Get the status information of a TimeStampResp.
|
|
*
|
|
* Each output is optional - pass NULL to not retrieve a value. The status
|
|
* string references into the response and is valid while the response is.
|
|
*
|
|
* @param [in] resp TimeStampResp object.
|
|
* @param [out] status PKIStatus value. See TspPkiStatus. May be NULL.
|
|
* @param [out] str Status string - UTF-8, no NUL terminator. NULL when
|
|
* no status string present. May be NULL.
|
|
* @param [out] strSz Length of status string in bytes. May be NULL.
|
|
* @param [out] failInfo Failure information: WC_TSP_FAIL_* flags, 0 when not
|
|
* present. May be NULL.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when resp is NULL.
|
|
*/
|
|
int wc_TspResponse_GetStatus(const TspResponse* resp, word32* status,
|
|
const byte** str, word32* strSz, word32* failInfo)
|
|
{
|
|
/* Validate parameter. */
|
|
if (resp == NULL) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
/* Return each value the caller asked for. */
|
|
if (status != NULL) {
|
|
*status = resp->status;
|
|
}
|
|
if (str != NULL) {
|
|
*str = resp->statusString;
|
|
}
|
|
if (strSz != NULL) {
|
|
*strSz = resp->statusStringSz;
|
|
}
|
|
if (failInfo != NULL) {
|
|
*failInfo = resp->failInfo;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif /* WOLFSSL_TSP_RESPONDER || WOLFSSL_TSP_VERIFIER */
|
|
|
|
#ifdef WOLFSSL_TSP_RESPONDER
|
|
/* Set the status information of a TimeStampResp.
|
|
*
|
|
* The status string is assigned - it is not copied and must remain available
|
|
* while the response is used. Pass NULL to have no status string.
|
|
*
|
|
* @param [in, out] resp TimeStampResp object.
|
|
* @param [in] status PKIStatus value. See TspPkiStatus.
|
|
* @param [in] str Status string - UTF-8, no NUL terminator. May be
|
|
* NULL.
|
|
* @param [in] strSz Length of status string in bytes.
|
|
* @param [in] failInfo Failure information: WC_TSP_FAIL_* flags, 0 when
|
|
* none.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when resp is NULL.
|
|
*/
|
|
int wc_TspResponse_SetStatus(TspResponse* resp, word32 status, const byte* str,
|
|
word32 strSz, word32 failInfo)
|
|
{
|
|
/* Validate parameter. */
|
|
if (resp == NULL) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
resp->status = (byte)status;
|
|
/* The status string is assigned, not copied. */
|
|
resp->statusString = str;
|
|
resp->statusStringSz = (str != NULL) ? strSz : 0;
|
|
resp->failInfo = failInfo;
|
|
|
|
return 0;
|
|
}
|
|
#endif /* WOLFSSL_TSP_RESPONDER */
|
|
|
|
/* Get a human-readable string for a PKIStatus value.
|
|
*
|
|
* @param [in] status PKIStatus value. See TspPkiStatus.
|
|
* @return Description of the status - a constant string, not to be freed.
|
|
*/
|
|
const char* wc_TspStatus_ToString(word32 status)
|
|
{
|
|
switch (status) {
|
|
case WC_TSP_PKISTATUS_GRANTED:
|
|
return "granted";
|
|
case WC_TSP_PKISTATUS_GRANTED_WITH_MODS:
|
|
return "granted with modifications";
|
|
case WC_TSP_PKISTATUS_REJECTION:
|
|
return "rejection";
|
|
case WC_TSP_PKISTATUS_WAITING:
|
|
return "waiting";
|
|
case WC_TSP_PKISTATUS_REVOCATION_WARNING:
|
|
return "revocation warning";
|
|
case WC_TSP_PKISTATUS_REVOCATION_NOTIFICATION:
|
|
return "revocation notification";
|
|
default:
|
|
return "unknown status";
|
|
}
|
|
}
|
|
|
|
/* Get a human-readable string for a PKIFailureInfo flag.
|
|
*
|
|
* Expects a single WC_TSP_FAIL_* flag - the failure information of a response
|
|
* has at most one.
|
|
*
|
|
* @param [in] failInfo Failure information: a WC_TSP_FAIL_* flag.
|
|
* @return Description of the failure - a constant string, not to be freed.
|
|
*/
|
|
const char* wc_TspFailInfo_ToString(word32 failInfo)
|
|
{
|
|
switch (failInfo) {
|
|
case WC_TSP_FAIL_BAD_ALG:
|
|
return "unrecognized or unsupported algorithm";
|
|
case WC_TSP_FAIL_BAD_REQUEST:
|
|
return "transaction not permitted or supported";
|
|
case WC_TSP_FAIL_BAD_DATA_FORMAT:
|
|
return "data submitted has the wrong format";
|
|
case WC_TSP_FAIL_TIME_NOT_AVAILABLE:
|
|
return "the TSA's time source is not available";
|
|
case WC_TSP_FAIL_UNACCEPTED_POLICY:
|
|
return "the requested TSA policy is not supported";
|
|
case WC_TSP_FAIL_UNACCEPTED_EXTENSION:
|
|
return "the requested extension is not supported";
|
|
case WC_TSP_FAIL_ADD_INFO_NOT_AVAILABLE:
|
|
return "the additional information is not available";
|
|
case WC_TSP_FAIL_SYSTEM_FAILURE:
|
|
return "the request cannot be handled due to system failure";
|
|
default:
|
|
return "unknown failure information";
|
|
}
|
|
}
|
|
|
|
#ifdef HAVE_PKCS7
|
|
#ifdef WOLFSSL_TSP_VERIFIER
|
|
/* Verify a signer's certificate chains to a trusted CA in a manager.
|
|
*
|
|
* @param [in] cert DER encoded signer certificate.
|
|
* @param [in] certSz Length of certificate in bytes.
|
|
* @param [in] cm WOLFSSL_CERT_MANAGER with the trusted CAs - a void
|
|
* pointer to avoid an SSL layer dependency in wolfCrypt.
|
|
* @param [in] heap Dynamic memory allocation hint.
|
|
* @return 0 when the certificate chains to a trusted CA.
|
|
* @return TSP_VERIFY_E when it does not.
|
|
* @return MEMORY_E on dynamic memory allocation failure.
|
|
*/
|
|
static int Tsp_VerifyCertChain(const byte* cert, word32 certSz, void* cm,
|
|
void* heap)
|
|
{
|
|
int ret;
|
|
WC_DECLARE_VAR(dCert, DecodedCert, 1, heap);
|
|
|
|
WC_ALLOC_VAR_EX(dCert, DecodedCert, 1, heap, DYNAMIC_TYPE_DCERT,
|
|
return MEMORY_E);
|
|
|
|
/* Parse and verify the certificate chains to a trusted CA in the
|
|
* manager. The manager must hold the trust anchor and any intermediate
|
|
* CAs needed - certificates carried in the token are not trust anchors. */
|
|
InitDecodedCert(dCert, cert, certSz, heap);
|
|
ret = ParseCertRelative(dCert, CERT_TYPE, VERIFY, cm, NULL);
|
|
FreeDecodedCert(dCert);
|
|
|
|
WC_FREE_VAR_EX(dCert, heap, DYNAMIC_TYPE_DCERT);
|
|
|
|
if (ret != 0) {
|
|
WOLFSSL_MSG("TSP signer's certificate is not trusted by the manager");
|
|
ret = TSP_VERIFY_E;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/* Verify the time-stamp token of a TimeStampResp, establishing trust in the
|
|
* signer by either pinning a certificate or chaining to a certificate
|
|
* manager. Used by wc_TspResponse_Verify() and wc_TspResponse_VerifyWithCm().
|
|
*
|
|
* @param [in] resp TimeStampResp object with a token to verify.
|
|
* @param [in] cert DER encoded trusted TSA certificate to pin, or NULL.
|
|
* @param [in] certSz Length of certificate in bytes.
|
|
* @param [in] cm Certificate manager to chain against, or NULL.
|
|
* @param [out] tstInfo TSTInfo object to fill. May be NULL.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when resp is NULL.
|
|
* @return TSP_VERIFY_E on a verification failure.
|
|
* @return MEMORY_E on dynamic memory allocation failure.
|
|
*/
|
|
static int TspResponse_Verify(TspResponse* resp, const byte* cert,
|
|
word32 certSz, void* cm, TspTstInfo* tstInfo)
|
|
{
|
|
int ret = 0;
|
|
#ifdef WOLFSSL_NO_MALLOC
|
|
/* No dynamic memory - the PKCS7 object is on the stack. */
|
|
wc_PKCS7 pkcs7Obj;
|
|
wc_PKCS7* pkcs7 = &pkcs7Obj;
|
|
#else
|
|
wc_PKCS7* pkcs7 = NULL;
|
|
#endif
|
|
|
|
#ifdef WOLFSSL_NO_MALLOC
|
|
/* Zero the stack object up front - an early error returns through the
|
|
* unconditional wc_PKCS7_Free below, which must see isDynamic 0 and all
|
|
* pointers NULL so it does not free the stack object or wild pointers. */
|
|
XMEMSET(pkcs7, 0, sizeof(pkcs7Obj));
|
|
#endif
|
|
|
|
/* Validate parameter. */
|
|
if (resp == NULL) {
|
|
ret = BAD_FUNC_ARG;
|
|
}
|
|
/* The time-stamp must have been granted. */
|
|
if ((ret == 0) && (resp->status != WC_TSP_PKISTATUS_GRANTED) &&
|
|
(resp->status != WC_TSP_PKISTATUS_GRANTED_WITH_MODS)) {
|
|
WOLFSSL_MSG("TSP response status is not granted");
|
|
ret = TSP_VERIFY_E;
|
|
}
|
|
/* A granted response has a token to verify. */
|
|
if ((ret == 0) && ((resp->token == NULL) || (resp->tokenSz == 0))) {
|
|
WOLFSSL_MSG("TSP response has no time-stamp token");
|
|
ret = TSP_VERIFY_E;
|
|
}
|
|
|
|
if (ret == 0) {
|
|
#ifdef WOLFSSL_NO_MALLOC
|
|
ret = wc_PKCS7_Init(pkcs7, NULL, INVALID_DEVID);
|
|
#else
|
|
pkcs7 = wc_PKCS7_New(NULL, INVALID_DEVID);
|
|
if (pkcs7 == NULL) {
|
|
ret = MEMORY_E;
|
|
}
|
|
#endif
|
|
}
|
|
if (ret == 0) {
|
|
/* Initialize with the TSA's certificate - NULL when in the token. */
|
|
ret = wc_PKCS7_InitWithCert(pkcs7, (byte*)cert, certSz);
|
|
}
|
|
if (ret == 0) {
|
|
/* The token references the response - not modified by verify. */
|
|
ret = wc_TspTstInfo_VerifyWithPKCS7(pkcs7, (byte*)resp->token,
|
|
resp->tokenSz, tstInfo);
|
|
}
|
|
/* Establish trust in the signer. The token's signature was verified
|
|
* against the certificate in the token; trust pins that certificate to a
|
|
* known one or chains it to a trusted CA in the manager. */
|
|
if ((ret == 0) && (cm != NULL)) {
|
|
/* Chain the signer's certificate to a trusted CA. */
|
|
ret = Tsp_VerifyCertChain(pkcs7->verifyCert, pkcs7->verifyCertSz,
|
|
cm, pkcs7->heap);
|
|
}
|
|
else if ((ret == 0) && (cert != NULL) &&
|
|
((pkcs7->verifyCertSz != certSz) ||
|
|
(pkcs7->verifyCert == NULL) ||
|
|
(XMEMCMP(pkcs7->verifyCert, cert, certSz) != 0))) {
|
|
/* Pin: the signer must be the given trusted certificate. */
|
|
WOLFSSL_MSG("TSP signer is not the trusted TSA");
|
|
ret = TSP_VERIFY_E;
|
|
}
|
|
/* A verified TSTInfo must have carried content for tstInfo to reference.
|
|
* Enforce the invariant explicitly: success with zero content would skip
|
|
* both the rebasing below and the error clear at the end, handing the
|
|
* caller references into the pkcs7->content that wc_PKCS7_Free releases. */
|
|
if ((ret == 0) && (tstInfo != NULL) && (pkcs7->contentSz == 0)) {
|
|
WOLFSSL_MSG("TSP token verified with no content");
|
|
ret = TSP_VERIFY_E;
|
|
}
|
|
|
|
/* tstInfo references pkcs7->content, which wc_PKCS7_Free releases - the
|
|
* verify may copy the eContent into PKCS7-owned memory. Re-point the
|
|
* references into the caller's token, which holds the same TSTInfo DER,
|
|
* so tstInfo stays valid after this function returns. */
|
|
if ((ret == 0) && (tstInfo != NULL) && (pkcs7->contentSz > 0)) {
|
|
const byte* c = pkcs7->content;
|
|
word32 off;
|
|
word32 matchOff = 0;
|
|
int matches = 0;
|
|
|
|
/* The content must appear exactly once in the response token so the
|
|
* references can be rebased unambiguously. Zero matches means the
|
|
* content is not contiguous in the token (e.g. a constructed OCTET
|
|
* STRING); more than one means a duplicated byte sequence that could
|
|
* rebase the references to the wrong - though in-bounds - location.
|
|
* Both are rejected rather than hand back possibly-wrong references. */
|
|
for (off = 0; off + pkcs7->contentSz <= resp->tokenSz; off++) {
|
|
if (XMEMCMP(resp->token + off, c, pkcs7->contentSz) == 0) {
|
|
matchOff = off;
|
|
if (++matches > 1) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (matches != 1) {
|
|
WOLFSSL_MSG("TSP token content not found uniquely in response");
|
|
ret = TSP_VERIFY_E;
|
|
}
|
|
else {
|
|
const byte* tok = resp->token + matchOff;
|
|
if (tstInfo->policy != NULL)
|
|
tstInfo->policy = tok + (tstInfo->policy - c);
|
|
if (tstInfo->serial != NULL)
|
|
tstInfo->serial = tok + (tstInfo->serial - c);
|
|
if (tstInfo->genTime != NULL)
|
|
tstInfo->genTime = tok + (tstInfo->genTime - c);
|
|
if (tstInfo->nonce != NULL)
|
|
tstInfo->nonce = tok + (tstInfo->nonce - c);
|
|
if (tstInfo->tsa != NULL)
|
|
tstInfo->tsa = tok + (tstInfo->tsa - c);
|
|
}
|
|
}
|
|
|
|
/* On any error tstInfo may hold references into pkcs7->content, which
|
|
* wc_PKCS7_Free is about to release - clear them so a caller that ignores
|
|
* the return value is not handed dangling pointers. */
|
|
if ((ret != 0) && (tstInfo != NULL)) {
|
|
XMEMSET(tstInfo, 0, sizeof(*tstInfo));
|
|
}
|
|
|
|
wc_PKCS7_Free(pkcs7);
|
|
return ret;
|
|
}
|
|
|
|
/* Verify the time-stamp token of a TimeStampResp with the TSA's certificate.
|
|
*
|
|
* Convenience wrapper around wc_TspTstInfo_VerifyWithPKCS7() that creates and
|
|
* disposes of the PKCS7 object. The time-stamp must have been granted and the
|
|
* response must have a token. The token's signature is verified and the
|
|
* signer's certificate checked - see wc_TspTstInfo_VerifyWithPKCS7().
|
|
*
|
|
* When a certificate is given it is the trusted TSA - the signer of the token
|
|
* must be that certificate. This establishes trust by pinning the TSA's
|
|
* certificate. The certificate is also used to verify the signature when the
|
|
* token does not include the signer's certificate - certReq was not set in
|
|
* the request. When the certificate is NULL the token must include the
|
|
* signer's certificate: the token's signature is verified against that
|
|
* embedded certificate but no trust anchoring is performed - any self-signed
|
|
* certificate carrying the time-stamping EKU is accepted. The NULL-cert form
|
|
* verifies the signature only; the caller must trust the signer by other
|
|
* means. To anchor the signer to a trusted CA, use
|
|
* wc_TspResponse_VerifyWithCm().
|
|
*
|
|
* Pointers in tstInfo reference the token of the response - the response and
|
|
* its token buffer must remain available while tstInfo is in use.
|
|
*
|
|
* @param [in] resp TimeStampResp object with a token to verify.
|
|
* @param [in] cert DER encoded certificate of the trusted TSA. May be
|
|
* NULL when the token includes the signer's certificate
|
|
* - the NULL-cert form verifies the signature only and
|
|
* establishes no trust.
|
|
* @param [in] certSz Length of certificate in bytes.
|
|
* @param [out] tstInfo TSTInfo object to fill. May be NULL.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when resp is NULL.
|
|
* @return TSP_VERIFY_E when the response was not granted, has no token, the
|
|
* token does not verify - see wc_TspTstInfo_VerifyWithPKCS7() - or the
|
|
* signer is not the trusted TSA certificate.
|
|
* @return MEMORY_E on dynamic memory allocation failure.
|
|
*/
|
|
int wc_TspResponse_Verify(TspResponse* resp, const byte* cert, word32 certSz,
|
|
TspTstInfo* tstInfo)
|
|
{
|
|
int ret;
|
|
|
|
WOLFSSL_ENTER("wc_TspResponse_Verify");
|
|
|
|
/* Pin the signer to the given certificate - no certificate manager. */
|
|
ret = TspResponse_Verify(resp, cert, certSz, NULL, tstInfo);
|
|
|
|
WOLFSSL_LEAVE("wc_TspResponse_Verify", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* Verify the time-stamp token of a TimeStampResp, trusting the signer via a
|
|
* certificate manager.
|
|
*
|
|
* Convenience wrapper around wc_TspTstInfo_VerifyWithPKCS7() that creates and
|
|
* disposes of the PKCS7 object. The time-stamp must have been granted and the
|
|
* response must have a token. The token's signature is verified and the
|
|
* signer's certificate checked - see wc_TspTstInfo_VerifyWithPKCS7() - then
|
|
* the signer's certificate is verified to chain to a trusted CA in the
|
|
* manager.
|
|
*
|
|
* The token must include the signer's certificate - the certificate manager
|
|
* must hold the trust anchor and any intermediate CAs needed to build the
|
|
* chain. Certificates carried in the token are used to verify the token's
|
|
* signature but are not trusted as CAs - load intermediate CAs into the
|
|
* manager to support a signer issued by an intermediate.
|
|
*
|
|
* Pointers in tstInfo reference the token of the response - the response and
|
|
* its token buffer must remain available while tstInfo is in use.
|
|
*
|
|
* @param [in] resp TimeStampResp object with a token to verify.
|
|
* @param [in] cm WOLFSSL_CERT_MANAGER with the trusted CAs - passed as
|
|
* a void pointer to avoid an SSL layer dependency.
|
|
* @param [out] tstInfo TSTInfo object to fill. May be NULL.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when resp or cm is NULL.
|
|
* @return TSP_VERIFY_E when the response was not granted, has no token, the
|
|
* token does not verify or the signer does not chain to a trusted CA.
|
|
* @return MEMORY_E on dynamic memory allocation failure.
|
|
*/
|
|
int wc_TspResponse_VerifyWithCm(TspResponse* resp, void* cm,
|
|
TspTstInfo* tstInfo)
|
|
{
|
|
int ret;
|
|
|
|
WOLFSSL_ENTER("wc_TspResponse_VerifyWithCm");
|
|
|
|
/* A certificate manager is required to establish trust. */
|
|
if (cm == NULL) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
/* Chain the signer's certificate to a trusted CA in the manager. */
|
|
ret = TspResponse_Verify(resp, NULL, 0, cm, tstInfo);
|
|
|
|
WOLFSSL_LEAVE("wc_TspResponse_VerifyWithCm", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* Verify the time-stamp token of a TimeStampResp and that it is over the data.
|
|
*
|
|
* Convenience over wc_TspResponse_Verify() that also confirms the time-stamp
|
|
* is over the given data - hashing the data with the token's message imprint
|
|
* algorithm and comparing to the imprint. The caller does not hash the data.
|
|
*
|
|
* @param [in] resp TimeStampResp object with a token to verify.
|
|
* @param [in] cert DER encoded certificate of the trusted TSA. May be
|
|
* NULL - see wc_TspResponse_Verify().
|
|
* @param [in] certSz Length of certificate in bytes.
|
|
* @param [in] data Data that was time-stamped.
|
|
* @param [in] dataSz Length of data in bytes.
|
|
* @param [out] tstInfo TSTInfo object to fill. May be NULL.
|
|
* @return 0 on success.
|
|
* @return BAD_FUNC_ARG when resp or data is NULL.
|
|
* @return TSP_VERIFY_E when the token does not verify or the data does not
|
|
* match the message imprint.
|
|
* @return HASH_TYPE_E when the imprint's hash algorithm is not supported.
|
|
* @return MEMORY_E on dynamic memory allocation failure.
|
|
*/
|
|
int wc_TspResponse_VerifyData(TspResponse* resp, const byte* cert,
|
|
word32 certSz, const byte* data, word32 dataSz, TspTstInfo* tstInfo)
|
|
{
|
|
int ret;
|
|
TspTstInfo tstLocal;
|
|
|
|
WOLFSSL_ENTER("wc_TspResponse_VerifyData");
|
|
|
|
/* Validate parameter - resp is checked by wc_TspResponse_Verify(). */
|
|
if (data == NULL) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
/* A TSTInfo is needed for the data check - use a local when not wanted. */
|
|
if (tstInfo == NULL) {
|
|
tstInfo = &tstLocal;
|
|
}
|
|
|
|
/* Verify the response and its token. */
|
|
ret = wc_TspResponse_Verify(resp, cert, certSz, tstInfo);
|
|
/* Confirm the time-stamp is over the given data. */
|
|
if (ret == 0) {
|
|
ret = wc_TspTstInfo_VerifyData(tstInfo, data, dataSz);
|
|
}
|
|
|
|
WOLFSSL_LEAVE("wc_TspResponse_VerifyData", ret);
|
|
return ret;
|
|
}
|
|
|
|
#endif /* WOLFSSL_TSP_VERIFIER */
|
|
#endif /* HAVE_PKCS7 */
|
|
|
|
#endif /* WOLFSSL_TSP && WOLFSSL_ASN_TEMPLATE && !NO_ASN */
|