diff --git a/doc/dox_comments/header_files/tsp.h b/doc/dox_comments/header_files/tsp.h index 58399c7568..25d4c37e6d 100644 --- a/doc/dox_comments/header_files/tsp.h +++ b/doc/dox_comments/header_files/tsp.h @@ -18,9 +18,8 @@ // hash the data to be time-stamped into hash wc_TspRequest_Init(&req); - req.imprint.hashAlgOID = SHA256h; - XMEMCPY(req.imprint.hash, hash, sizeof(hash)); - req.imprint.hashSz = (word32)sizeof(hash); + wc_TspRequest_SetHashType(&req, WC_HASH_TYPE_SHA256); + wc_TspRequest_SetHash(&req, hash, sizeof(hash)); req.certReq = 1; \endcode @@ -33,13 +32,13 @@ int wc_TspRequest_Init(TspRequest* req); /*! \ingroup TSP - \brief This function sets the message imprint hash algorithm and hash size - of a TimeStampReq from a hash type. After calling, fill - req->imprint.hash with the digest of the data to be time-stamped. + \brief This function sets the message imprint hash algorithm of a + TimeStampReq from a hash type. Any digest already set is discarded. \return 0 Returned on successfully setting the hash algorithm. \return BAD_FUNC_ARG Returned when req is NULL. - \return HASH_TYPE_E Returned when the hash algorithm is not available. + \return HASH_TYPE_E Returned when the hash algorithm is not available or + is not identified by its OID. \return BUFFER_E Returned when the digest is too big for the message imprint. @@ -54,11 +53,12 @@ int wc_TspRequest_Init(TspRequest* req); wc_TspRequest_Init(&req); wc_TspRequest_SetHashType(&req, WC_HASH_TYPE_SHA256); - XMEMCPY(req.imprint.hash, hash, sizeof(hash)); + wc_TspRequest_SetHash(&req, hash, sizeof(hash)); req.certReq = 1; \endcode \sa wc_TspRequest_Init + \sa wc_TspRequest_SetHash \sa wc_TspRequest_GetHashType \sa wc_TspRequest_Encode */ @@ -133,11 +133,15 @@ int wc_TspRequest_GetHash(const TspRequest* req, byte* hash, word32* hashSz); \brief This function sets the message imprint hash of a TimeStampReq. The hash and its length are copied into the message imprint. Set the hash - algorithm separately with wc_TspRequest_SetHashType(). + algorithm first with wc_TspRequest_SetHashType() - hashSz must be the + digest size of that algorithm. \return 0 Returned on successfully setting the hash. \return BAD_FUNC_ARG Returned when req or hash is NULL or hashSz is 0. - \return BUFFER_E Returned when hashSz is too big for the message imprint. + \return HASH_TYPE_E Returned when the hash algorithm is not set or not + available. + \return BUFFER_E Returned when hashSz is not the algorithm's digest size + or is too big for the message imprint. \param [in,out] req Pointer to the TspRequest structure to update. \param [in] hash Hash of the data to be time-stamped. @@ -374,9 +378,8 @@ void wc_TspRequest_SetCertReq(TspRequest* req, int val); have a leading zero byte. \return 0 Returned on successfully encoding the request. - \return BAD_FUNC_ARG Returned when req or outSz is NULL, the message - imprint hash is not set, a field is too long for its array or the nonce - has a leading zero byte. + \return BAD_FUNC_ARG Returned when req or outSz is NULL, or a field is + unset, the wrong length or not encodable as given. \return BUFFER_E Returned when out is not NULL and the encoding is longer than outSz. \return ASN_UNKNOWN_OID_E Returned when the hash algorithm is not @@ -636,7 +639,10 @@ int wc_TspTstInfo_GetMsgImprint(const TspTstInfo* tstInfo, word32* hashOID, \return 0 Returned on successfully setting the message imprint. \return BAD_FUNC_ARG Returned when tstInfo or hash is NULL or hashSz is 0. - \return BUFFER_E Returned when hashSz is too big for the message imprint. + \return HASH_TYPE_E Returned when the hash algorithm is not known or not + available. + \return BUFFER_E Returned when hashSz is too big for the message imprint + or not the digest size of hashOID. \param [in,out] tstInfo Pointer to the TspTstInfo structure to update. \param [in] hashOID Hash algorithm OID sum: SHA256h, etc. @@ -897,11 +903,8 @@ int wc_TspTstInfo_SetFromRequest(TspTstInfo* tstInfo, const TspRequest* req, wc_TspTstInfo_SignWithPkcs7() which encodes and signs in one call. \return 0 Returned on successfully encoding the TSTInfo. - \return BAD_FUNC_ARG Returned when tstInfo or outSz is NULL, a required - field is not set or empty, the hash is too long, the genTime is not a - valid GeneralizedTime, the tsa is empty, the serial number or nonce is - empty or has a leading zero byte or accuracy millis or micros is out of - range. + \return BAD_FUNC_ARG Returned when tstInfo or outSz is NULL, or a field + is unset, the wrong length or not encodable as given. \return BUFFER_E Returned when out is not NULL and the encoding is longer than outSz. \return ASN_UNKNOWN_OID_E Returned when the hash algorithm is not diff --git a/tests/api/test_tsp.c b/tests/api/test_tsp.c index 28635ae116..755fcf47c5 100644 --- a/tests/api/test_tsp.c +++ b/tests/api/test_tsp.c @@ -117,7 +117,9 @@ int test_wc_TspRequest_SetHashType(void) #if defined(WOLFSSL_TSP) && !defined(NO_SHA256) && \ defined(WOLFSSL_TSP_REQUESTER) TspRequest req; + byte zeros[WC_SHA256_DIGEST_SIZE]; + XMEMSET(zeros, 0, sizeof(zeros)); ExpectIntEQ(wc_TspRequest_Init(&req), 0); /* Bad argument. */ @@ -126,18 +128,36 @@ int test_wc_TspRequest_SetHashType(void) /* Hash type that is not a usable algorithm. */ ExpectIntEQ(wc_TspRequest_SetHashType(&req, WC_HASH_TYPE_NONE), WC_NO_ERR_TRACE(HASH_TYPE_E)); +#if !defined(NO_MD5) && !defined(NO_SHA) + /* MD5_SHA has no OID of its own - it maps to the MD5 OID. */ + ExpectIntEQ(wc_TspRequest_SetHashType(&req, WC_HASH_TYPE_MD5_SHA), + WC_NO_ERR_TRACE(HASH_TYPE_E)); +#endif +#ifndef NO_MD5 + /* Plain MD5 has its own OID and is still accepted. */ + ExpectIntEQ(wc_TspRequest_SetHashType(&req, WC_HASH_TYPE_MD5), 0); + ExpectIntEQ(req.imprint.hashAlgOID, MD5h); +#endif - /* SHA-256 sets the algorithm OID and the digest size. */ + /* SHA-256 sets the algorithm OID - the digest is set separately. */ ExpectIntEQ(wc_TspRequest_SetHashType(&req, WC_HASH_TYPE_SHA256), 0); ExpectIntEQ(req.imprint.hashAlgOID, SHA256h); - ExpectIntEQ(req.imprint.hashSz, WC_SHA256_DIGEST_SIZE); + ExpectIntEQ(req.imprint.hashSz, 0); #ifdef WOLFSSL_SHA384 - /* A different algorithm sets a different OID and size. */ + /* A different algorithm sets a different OID. */ ExpectIntEQ(wc_TspRequest_SetHashType(&req, WC_HASH_TYPE_SHA384), 0); ExpectIntEQ(req.imprint.hashAlgOID, SHA384h); - ExpectIntEQ(req.imprint.hashSz, WC_SHA384_DIGEST_SIZE); + ExpectIntEQ(req.imprint.hashSz, 0); #endif + + /* Setting the algorithm discards a digest already set. */ + ExpectIntEQ(wc_TspRequest_SetHashType(&req, WC_HASH_TYPE_SHA256), 0); + ExpectIntEQ(wc_TspRequest_SetHash(&req, tsHashedMsg, + (word32)sizeof(tsHashedMsg)), 0); + ExpectIntEQ(wc_TspRequest_SetHashType(&req, WC_HASH_TYPE_SHA256), 0); + ExpectIntEQ(req.imprint.hashSz, 0); + ExpectBufEQ(req.imprint.hash, zeros, (int)sizeof(zeros)); #endif return EXPECT_RESULT(); } @@ -200,6 +220,14 @@ int test_wc_TspRequest_GetSetHash(void) /* Hash too big for the message imprint. */ ExpectIntEQ(wc_TspRequest_SetHash(&req, hash, WC_TSP_MAX_HASH_SZ + 1), WC_NO_ERR_TRACE(BUFFER_E)); + /* Hash algorithm not set yet. */ + ExpectIntEQ(wc_TspRequest_SetHash(&req, hash, (word32)sizeof(hash)), + WC_NO_ERR_TRACE(HASH_TYPE_E)); + + ExpectIntEQ(wc_TspRequest_SetHashType(&req, WC_HASH_TYPE_SHA256), 0); + /* One byte short of the algorithm's digest size. */ + ExpectIntEQ(wc_TspRequest_SetHash(&req, hash, (word32)sizeof(hash) - 1), + WC_NO_ERR_TRACE(BUFFER_E)); /* Set the hash and length. */ ExpectIntEQ(wc_TspRequest_SetHash(&req, hash, (word32)sizeof(hash)), 0); @@ -434,7 +462,22 @@ int test_wc_TspRequest_Encode(void) req.imprint.hashSz = WC_TSP_MAX_HASH_SZ + 1; ExpectIntEQ(wc_TspRequest_Encode(&req, enc, &encSz), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); - req.imprint.hashSz = (word32)sizeof(tsHashedMsg); + /* Hash length that is not the algorithm's digest size. */ + req.imprint.hashSz = (word32)sizeof(tsHashedMsg) - 1; + ExpectIntEQ(wc_TspRequest_Encode(&req, enc, &encSz), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + /* Algorithm set but the digest never supplied - not encoded. */ + ExpectIntEQ(wc_TspRequest_Init(&req), 0); + ExpectIntEQ(wc_TspRequest_SetHashType(&req, WC_HASH_TYPE_SHA256), 0); + ExpectIntEQ(wc_TspRequest_Encode(&req, enc, &encSz), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + /* Setting the algorithm after the digest discards the digest. */ + ExpectIntEQ(wc_TspRequest_SetHash(&req, tsHashedMsg, + (word32)sizeof(tsHashedMsg)), 0); + ExpectIntEQ(wc_TspRequest_SetHashType(&req, WC_HASH_TYPE_SHA256), 0); + ExpectIntEQ(wc_TspRequest_Encode(&req, enc, &encSz), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + test_tsp_set_hash(&req.imprint); /* Policy too long. */ req.policySz = MAX_OID_SZ + 1; ExpectIntEQ(wc_TspRequest_Encode(&req, enc, &encSz), @@ -867,7 +910,8 @@ int test_wc_TspTstInfo_Setters(void) static const byte policy[] = { 0x2b, 0x06, 0x01, 0x04, 0x01, 0x87, 0x67, 0x01 }; - static const byte hash[] = { 0xde, 0xad, 0xbe, 0xef }; + /* SHA-256 sized hash - the length must match the algorithm. */ + static const byte hash[32] = { 0xde, 0xad, 0xbe, 0xef }; static const byte genTime[] = "20260610120000Z"; static const byte nonce[] = { 0x12, 0x34 }; /* Name of TSA: dNSName GeneralName. */ @@ -876,7 +920,9 @@ int test_wc_TspTstInfo_Setters(void) byte bigHash[WC_TSP_MAX_HASH_SZ + 1]; const byte* out = NULL; word32 outSz = 0; +#ifndef NO_SHA256 word32 hashOID = 0; +#endif word32 seconds = 0; word16 millis = 0; word16 micros = 0; @@ -906,12 +952,22 @@ int test_wc_TspTstInfo_Setters(void) WC_NO_ERR_TRACE(BAD_FUNC_ARG)); ExpectIntEQ(wc_TspTstInfo_SetMsgImprint(&tst, SHA256h, bigHash, (word32)sizeof(bigHash)), WC_NO_ERR_TRACE(BUFFER_E)); +#ifndef NO_SHA256 + /* One byte short of the digest size of the algorithm. */ + ExpectIntEQ(wc_TspTstInfo_SetMsgImprint(&tst, SHA256h, hash, + (word32)sizeof(hash) - 1), WC_NO_ERR_TRACE(BUFFER_E)); +#endif + /* An unknown algorithm has no digest size to check the length against. */ + ExpectIntEQ(wc_TspTstInfo_SetMsgImprint(&tst, 1, hash, + (word32)sizeof(hash) - 1), WC_NO_ERR_TRACE(HASH_TYPE_E)); +#ifndef NO_SHA256 ExpectIntEQ(wc_TspTstInfo_SetMsgImprint(&tst, SHA256h, hash, (word32)sizeof(hash)), 0); ExpectIntEQ(wc_TspTstInfo_GetMsgImprint(&tst, &hashOID, &out, &outSz), 0); ExpectIntEQ(hashOID, SHA256h); ExpectIntEQ(outSz, (word32)sizeof(hash)); ExpectBufEQ(out, hash, (int)sizeof(hash)); +#endif /* Time of the time-stamp - referenced, round trips. */ ExpectIntEQ(wc_TspTstInfo_SetGenTime(NULL, genTime, @@ -1033,6 +1089,10 @@ int test_wc_TspTstInfo_Encode(void) WC_NO_ERR_TRACE(BAD_FUNC_ARG)); /* Hash too long. */ tst.imprint.hashSz = WC_TSP_MAX_HASH_SZ + 1; + ExpectIntEQ(wc_TspTstInfo_Encode(&tst, enc, &encSz), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + /* Hash length that is not the algorithm's digest size. */ + tst.imprint.hashSz = (word32)sizeof(tsHashedMsg) - 1; ExpectIntEQ(wc_TspTstInfo_Encode(&tst, enc, &encSz), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); tst.imprint.hashSz = (word32)sizeof(tsHashedMsg); diff --git a/wolfcrypt/src/asn_tsp.c b/wolfcrypt/src/asn_tsp.c index dbe491253d..edb93324cf 100644 --- a/wolfcrypt/src/asn_tsp.c +++ b/wolfcrypt/src/asn_tsp.c @@ -112,9 +112,8 @@ enum { * @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 req or outSz is NULL, the message imprint hash - * is not set, a field is too long for its array or the nonce has a - * leading zero byte. + * @return BAD_FUNC_ARG when req or outSz is NULL, or a field is unset, the + * wrong length or not encodable as given. * @return BUFFER_E when out is not NULL and encoding is longer than outSz. * @return ASN_UNKNOWN_OID_E when the hash algorithm is not recognized. * @return MEMORY_E on dynamic memory allocation failure. @@ -124,6 +123,7 @@ int wc_TspRequest_Encode(const TspRequest* req, byte* out, word32* outSz) DECL_ASNSETDATA(dataASN, tspReqASN_Length); int ret = 0; word32 sz = 0; + int digestSz = 0; WOLFSSL_ENTER("wc_TspRequest_Encode"); @@ -131,10 +131,17 @@ int wc_TspRequest_Encode(const TspRequest* req, byte* out, word32* outSz) if ((req == NULL) || (outSz == NULL)) { ret = BAD_FUNC_ARG; } - /* The message imprint is the only required field. */ - if ((ret == 0) && ((req->imprint.hashSz == 0) || - (req->imprint.hashSz > sizeof(req->imprint.hash)))) { - ret = BAD_FUNC_ARG; + /* The message imprint is the only required field. Its length is checked + * against the hash algorithm when the algorithm is known and available. */ + if (ret == 0) { + digestSz = wc_HashGetDigestSize( + wc_OidGetHash((int)req->imprint.hashAlgOID)); + if ((req->imprint.hashSz == 0) || + (req->imprint.hashSz > sizeof(req->imprint.hash)) || + ((digestSz > 0) && + (req->imprint.hashSz != (word32)digestSz))) { + ret = BAD_FUNC_ARG; + } } /* Policy, when set, must fit. */ if ((ret == 0) && (req->policySz > sizeof(req->policy))) { @@ -447,11 +454,8 @@ enum { * @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 tstInfo or outSz is NULL, a required field of - * tstInfo is not set or empty, the hash is too long, the genTime - * is not a valid GeneralizedTime, the tsa is empty, the serial - * number or nonce is empty or has a leading zero byte or accuracy - * millis or micros is out of range. + * @return BAD_FUNC_ARG when tstInfo or outSz is NULL, or a field is unset, + * the wrong length or not encodable as given. * @return BUFFER_E when out is not NULL and encoding is longer than outSz. * @return ASN_UNKNOWN_OID_E when the hash algorithm is not recognized. * @return ASN_TIME_E when getting the current time failed. @@ -462,6 +466,7 @@ int wc_TspTstInfo_Encode(const TspTstInfo* tstInfo, byte* out, word32* outSz) DECL_ASNSETDATA(dataASN, tspTstInfoASN_Length); int ret = 0; word32 sz = 0; + int digestSz = 0; #if !defined(NO_ASN_TIME) && !defined(USER_TIME) && !defined(TIME_OVERRIDES) byte timeBuf[ASN_GENERALIZED_TIME_SIZE]; #endif @@ -480,6 +485,15 @@ int wc_TspTstInfo_Encode(const TspTstInfo* tstInfo, byte* out, word32* outSz) (tstInfo->serial == NULL))) { ret = BAD_FUNC_ARG; } + /* The imprint length must match the hash algorithm when the algorithm is + * known and available. */ + if (ret == 0) { + digestSz = wc_HashGetDigestSize( + wc_OidGetHash((int)tstInfo->imprint.hashAlgOID)); + if ((digestSz > 0) && (tstInfo->imprint.hashSz != (word32)digestSz)) { + ret = BAD_FUNC_ARG; + } + } /* genTime, when set, must be a valid GeneralizedTime of RFC 3161. */ if ((ret == 0) && (tstInfo->genTime != NULL) && (TspCheckGenTimeSyntax(tstInfo->genTime, tstInfo->genTimeSz) diff --git a/wolfcrypt/src/tsp.c b/wolfcrypt/src/tsp.c index 16016f88bc..71b34f3cbd 100644 --- a/wolfcrypt/src/tsp.c +++ b/wolfcrypt/src/tsp.c @@ -103,14 +103,14 @@ int wc_TspRequest_GetHashType(const TspRequest* req, enum wc_HashType* hashType) #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. + * Sets the hash algorithm OID and discards any digest already set. * * @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 HASH_TYPE_E when the hash algorithm is not available or is not + * identified by its OID. * @return BUFFER_E when the digest is too big for the message imprint. */ int wc_TspRequest_SetHashType(TspRequest* req, enum wc_HashType hashType) @@ -130,6 +130,11 @@ int wc_TspRequest_SetHashType(TspRequest* req, enum wc_HashType hashType) if (oid <= 0) { ret = HASH_TYPE_E; } + /* The OID must name the same algorithm - WC_HASH_TYPE_MD5_SHA shares + * the MD5 OID and cannot be identified in an imprint. */ + else if (wc_OidGetHash(oid) != hashType) { + ret = HASH_TYPE_E; + } } if (ret == 0) { /* The digest size is the length of the message imprint hash. */ @@ -143,7 +148,9 @@ int wc_TspRequest_SetHashType(TspRequest* req, enum wc_HashType hashType) } if (ret == 0) { req->imprint.hashAlgOID = (word32)oid; - req->imprint.hashSz = (word32)digestSz; + /* A digest of the previous algorithm is no longer valid. */ + XMEMSET(req->imprint.hash, 0, sizeof(req->imprint.hash)); + req->imprint.hashSz = 0; } return ret; @@ -188,18 +195,22 @@ int wc_TspRequest_GetHash(const TspRequest* req, byte* hash, word32* hashSz) /* 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(). + * must be set first with wc_TspRequest_SetHashType() and hashSz must be its + * digest size. * * @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. + * @return HASH_TYPE_E when the hash algorithm is not set or not available. + * @return BUFFER_E when hashSz is not the algorithm's digest size or is too + * big for the message imprint. */ int wc_TspRequest_SetHash(TspRequest* req, const byte* hash, word32 hashSz) { int ret = 0; + int digestSz = 0; /* Validate parameters. */ if ((req == NULL) || (hash == NULL) || (hashSz == 0)) { @@ -209,6 +220,17 @@ int wc_TspRequest_SetHash(TspRequest* req, const byte* hash, word32 hashSz) ret = BUFFER_E; } + if (ret == 0) { + /* The digest must be the length of the algorithm already set. */ + digestSz = wc_HashGetDigestSize( + wc_OidGetHash((int)req->imprint.hashAlgOID)); + if (digestSz <= 0) { + ret = HASH_TYPE_E; + } + else if (hashSz != (word32)digestSz) { + ret = BUFFER_E; + } + } if (ret == 0) { XMEMCPY(req->imprint.hash, hash, hashSz); req->imprint.hashSz = hashSz; @@ -594,11 +616,15 @@ int wc_TspTstInfo_GetMsgImprint(const TspTstInfo* tstInfo, word32* hashOID, * @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. + * @return HASH_TYPE_E when the hash algorithm is not known or not available. + * @return BUFFER_E when hashSz is too big for the message imprint or not + * the digest size of hashOID. */ int wc_TspTstInfo_SetMsgImprint(TspTstInfo* tstInfo, word32 hashOID, const byte* hash, word32 hashSz) { + int digestSz; + /* Validate parameters. */ if ((tstInfo == NULL) || (hash == NULL) || (hashSz == 0)) { return BAD_FUNC_ARG; @@ -606,6 +632,15 @@ int wc_TspTstInfo_SetMsgImprint(TspTstInfo* tstInfo, word32 hashOID, if (hashSz > sizeof(tstInfo->imprint.hash)) { return BUFFER_E; } + /* The algorithm must be one with a known digest size and the imprint + * must be that length. */ + digestSz = wc_HashGetDigestSize(wc_OidGetHash((int)hashOID)); + if (digestSz <= 0) { + return HASH_TYPE_E; + } + if (hashSz != (word32)digestSz) { + return BUFFER_E; + } tstInfo->imprint.hashAlgOID = hashOID; XMEMCPY(tstInfo->imprint.hash, hash, hashSz);