2 RFC3161 Timestamp Countersignature Verification over OpenSSL.
3 The timestamp is generated by a TimeStamping Authority (TSA) and asserts that a
4 publisher's signature existed before the specified time. The timestamp extends
5 the lifetime of the signature when a signing certificate expires or is later
8 Copyright (c) 2014 - 2017, Intel Corporation. All rights reserved.<BR>
9 SPDX-License-Identifier: BSD-2-Clause-Patent
13 #include "InternalCryptLib.h"
15 #include <openssl/asn1.h>
16 #include <openssl/asn1t.h>
17 #include <openssl/x509.h>
18 #include <openssl/x509v3.h>
19 #include <openssl/pkcs7.h>
22 // OID ASN.1 Value for SPC_RFC3161_OBJID ("1.3.6.1.4.1.311.3.3.1")
24 GLOBAL_REMOVE_IF_UNREFERENCED
const UINT8 mSpcRFC3161OidValue
[] = {
25 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x03, 0x03, 0x01
29 /// The messageImprint field SHOULD contain the hash of the datum to be
30 /// time-stamped. The hash is represented as an OCTET STRING. Its
31 /// length MUST match the length of the hash value for that algorithm
32 /// (e.g., 20 bytes for SHA-1 or 16 bytes for MD5).
34 /// MessageImprint ::= SEQUENCE {
35 /// hashAlgorithm AlgorithmIdentifier,
36 /// hashedMessage OCTET STRING }
39 X509_ALGOR
*HashAlgorithm
;
40 ASN1_OCTET_STRING
*HashedMessage
;
44 // ASN.1 Functions for TS_MESSAGE_IMPRINT
46 GLOBAL_REMOVE_IF_UNREFERENCED
47 DECLARE_ASN1_FUNCTIONS (
50 ASN1_SEQUENCE (TS_MESSAGE_IMPRINT
) =
52 ASN1_SIMPLE (TS_MESSAGE_IMPRINT
, HashAlgorithm
, X509_ALGOR
),
53 ASN1_SIMPLE (TS_MESSAGE_IMPRINT
, HashedMessage
, ASN1_OCTET_STRING
)
56 ASN1_SEQUENCE_END (TS_MESSAGE_IMPRINT
)
57 IMPLEMENT_ASN1_FUNCTIONS (TS_MESSAGE_IMPRINT
)
60 /// Accuracy represents the time deviation around the UTC time contained
61 /// in GeneralizedTime of time-stamp token.
63 /// Accuracy ::= SEQUENCE {
64 /// seconds INTEGER OPTIONAL,
65 /// millis [0] INTEGER (1..999) OPTIONAL,
66 /// micros [1] INTEGER (1..999) OPTIONAL }
69 ASN1_INTEGER
*Seconds
;
75 // ASN.1 Functions for TS_ACCURACY
77 GLOBAL_REMOVE_IF_UNREFERENCED
78 DECLARE_ASN1_FUNCTIONS (
81 ASN1_SEQUENCE (TS_ACCURACY
) =
83 ASN1_OPT (TS_ACCURACY
, Seconds
, ASN1_INTEGER
),
84 ASN1_IMP_OPT (TS_ACCURACY
, Millis
, ASN1_INTEGER
, 0),
85 ASN1_IMP_OPT (TS_ACCURACY
, Micros
, ASN1_INTEGER
, 1)
88 ASN1_SEQUENCE_END (TS_ACCURACY
)
89 IMPLEMENT_ASN1_FUNCTIONS (TS_ACCURACY
)
92 /// The timestamp token info resulting from a successful timestamp request,
93 /// as defined in RFC 3161.
95 /// TSTInfo ::= SEQUENCE {
96 /// version INTEGER { v1(1) },
97 /// policy TSAPolicyId,
98 /// messageImprint MessageImprint,
99 /// -- MUST have the same value as the similar field in
101 /// serialNumber INTEGER,
102 /// -- Time-Stamping users MUST be ready to accommodate integers
103 /// -- up to 160 bits.
104 /// genTime GeneralizedTime,
105 /// accuracy Accuracy OPTIONAL,
106 /// ordering BOOLEAN DEFAULT FALSE,
107 /// nonce INTEGER OPTIONAL,
108 /// -- MUST be present if the similar field was present
109 /// -- in TimeStampReq. In that case it MUST have the same value.
110 /// tsa [0] GeneralName OPTIONAL,
111 /// extensions [1] IMPLICIT Extensions OPTIONAL }
114 ASN1_INTEGER
*Version
;
116 TS_MESSAGE_IMPRINT
*MessageImprint
;
117 ASN1_INTEGER
*SerialNumber
;
118 ASN1_GENERALIZEDTIME
*GenTime
;
119 TS_ACCURACY
*Accuracy
;
120 ASN1_BOOLEAN Ordering
;
123 STACK_OF (X509_EXTENSION
) *Extensions
;
127 // ASN.1 Functions for TS_TST_INFO
129 GLOBAL_REMOVE_IF_UNREFERENCED
130 DECLARE_ASN1_FUNCTIONS (
133 ASN1_SEQUENCE (TS_TST_INFO
) =
135 ASN1_SIMPLE (TS_TST_INFO
, Version
, ASN1_INTEGER
),
136 ASN1_SIMPLE (TS_TST_INFO
, Policy
, ASN1_OBJECT
),
137 ASN1_SIMPLE (TS_TST_INFO
, MessageImprint
, TS_MESSAGE_IMPRINT
),
138 ASN1_SIMPLE (TS_TST_INFO
, SerialNumber
, ASN1_INTEGER
),
139 ASN1_SIMPLE (TS_TST_INFO
, GenTime
, ASN1_GENERALIZEDTIME
),
140 ASN1_OPT (TS_TST_INFO
, Accuracy
, TS_ACCURACY
),
141 ASN1_OPT (TS_TST_INFO
, Ordering
, ASN1_FBOOLEAN
),
142 ASN1_OPT (TS_TST_INFO
, Nonce
, ASN1_INTEGER
),
143 ASN1_EXP_OPT (TS_TST_INFO
, Tsa
, GENERAL_NAME
, 0),
144 ASN1_IMP_SEQUENCE_OF_OPT (TS_TST_INFO
, Extensions
, X509_EXTENSION
, 1)
147 ASN1_SEQUENCE_END (TS_TST_INFO
)
148 IMPLEMENT_ASN1_FUNCTIONS (TS_TST_INFO
)
151 Convert ASN.1 GeneralizedTime to EFI Time.
153 @param[in] Asn1Time Pointer to the ASN.1 GeneralizedTime to be converted.
154 @param[out] SigningTime Return the corresponding EFI Time.
156 @retval TRUE The time conversion succeeds.
157 @retval FALSE Invalid parameters.
162 ConvertAsn1TimeToEfiTime (
163 IN ASN1_TIME
*Asn1Time
,
164 OUT EFI_TIME
*EfiTime
170 if ((Asn1Time
== NULL
) || (EfiTime
== NULL
)) {
174 Str
= (CONST CHAR8
*)Asn1Time
->data
;
175 SetMem (EfiTime
, sizeof (EFI_TIME
), 0);
178 if (Asn1Time
->type
== V_ASN1_UTCTIME
) {
180 EfiTime
->Year
= (Str
[Index
++] - '0') * 10;
181 EfiTime
->Year
+= (Str
[Index
++] - '0');
182 if (EfiTime
->Year
< 70) {
183 EfiTime
->Year
+= 100;
185 } else if (Asn1Time
->type
== V_ASN1_GENERALIZEDTIME
) {
186 /* four digit year */
187 EfiTime
->Year
= (Str
[Index
++] - '0') * 1000;
188 EfiTime
->Year
+= (Str
[Index
++] - '0') * 100;
189 EfiTime
->Year
+= (Str
[Index
++] - '0') * 10;
190 EfiTime
->Year
+= (Str
[Index
++] - '0');
191 if ((EfiTime
->Year
< 1900) || (EfiTime
->Year
> 9999)) {
196 EfiTime
->Month
= (Str
[Index
++] - '0') * 10;
197 EfiTime
->Month
+= (Str
[Index
++] - '0');
198 if ((EfiTime
->Month
< 1) || (EfiTime
->Month
> 12)) {
202 EfiTime
->Day
= (Str
[Index
++] - '0') * 10;
203 EfiTime
->Day
+= (Str
[Index
++] - '0');
204 if ((EfiTime
->Day
< 1) || (EfiTime
->Day
> 31)) {
208 EfiTime
->Hour
= (Str
[Index
++] - '0') * 10;
209 EfiTime
->Hour
+= (Str
[Index
++] - '0');
210 if (EfiTime
->Hour
> 23) {
214 EfiTime
->Minute
= (Str
[Index
++] - '0') * 10;
215 EfiTime
->Minute
+= (Str
[Index
++] - '0');
216 if (EfiTime
->Minute
> 59) {
220 EfiTime
->Second
= (Str
[Index
++] - '0') * 10;
221 EfiTime
->Second
+= (Str
[Index
++] - '0');
222 if (EfiTime
->Second
> 59) {
226 /* Note: we did not adjust the time based on time zone information */
233 Check the validity of TimeStamp Token Information.
235 @param[in] TstInfo Pointer to the TS_TST_INFO structure.
236 @param[in] TimestampedData Pointer to the data to be time-stamped.
237 @param[in] DataSize Size of timestamped data in bytes.
239 @retval TRUE The TimeStamp Token Information is valid.
240 @retval FALSE Invalid TimeStamp Token Information.
246 IN CONST TS_TST_INFO
*TstInfo
,
247 IN CONST UINT8
*TimestampedData
,
252 TS_MESSAGE_IMPRINT
*Imprint
;
253 X509_ALGOR
*HashAlgo
;
268 // -- Check version number of Timestamp:
269 // The version field (currently v1) describes the version of the time-stamp token.
270 // Conforming time-stamping servers MUST be able to provide version 1 time-stamp tokens.
272 if ((ASN1_INTEGER_get (TstInfo
->Version
)) != 1) {
278 // The policy field MUST indicate the TSA's policy under which the response was produced.
280 if (TstInfo
->Policy
== NULL
) {
281 /// NOTE: Need to check if the requested and returned policies.
282 /// We have no information about the Requested TSA Policy.
287 // -- Compute & Check Message Imprint
289 Imprint
= TstInfo
->MessageImprint
;
290 HashAlgo
= X509_ALGOR_dup (Imprint
->HashAlgorithm
);
292 Md
= EVP_get_digestbyobj (HashAlgo
->algorithm
);
297 MdSize
= EVP_MD_size (Md
);
298 HashedMsg
= AllocateZeroPool (MdSize
);
299 if (HashedMsg
== NULL
) {
303 MdCtx
= EVP_MD_CTX_new ();
308 if ((EVP_DigestInit_ex (MdCtx
, Md
, NULL
) != 1) ||
309 (EVP_DigestUpdate (MdCtx
, TimestampedData
, DataSize
) != 1) ||
310 (EVP_DigestFinal (MdCtx
, HashedMsg
, NULL
) != 1))
315 if ((MdSize
== (UINTN
)ASN1_STRING_length (Imprint
->HashedMessage
)) &&
316 (CompareMem (HashedMsg
, ASN1_STRING_get0_data (Imprint
->HashedMessage
), MdSize
) != 0))
324 if (TstInfo
->Nonce
!= NULL
) {
326 // Nonces is optional, No error if no nonce is returned;
331 // -- Check if the TSA name and signer certificate is matched.
333 if (TstInfo
->Tsa
!= NULL
) {
335 // Ignored the optional Tsa field checking.
342 X509_ALGOR_free (HashAlgo
);
343 EVP_MD_CTX_free (MdCtx
);
344 if (HashedMsg
!= NULL
) {
345 FreePool (HashedMsg
);
352 Verifies the validity of a TimeStamp Token as described in RFC 3161 ("Internet
353 X.509 Public Key Infrastructure Time-Stamp Protocol (TSP)").
355 If TSToken is NULL, then return FALSE.
356 If TimestampedData is NULL, then return FALSE.
358 @param[in] TSToken Pointer to the RFC3161 TimeStamp Token, which is generated
359 by a TSA and located in the software publisher's SignerInfo
361 @param[in] TokenSize Size of the TimeStamp Token in bytes.
362 @param[in] TsaCert Pointer to a trusted/root TSA certificate encoded in DER.
363 @param[in] CertSize Size of the trusted TSA certificate in bytes.
364 @param[in] TimestampedData Pointer to the data to be time-stamped.
365 @param[in] DataSize Size of timestamped data in bytes.
366 @param[out] SigningTime Return the time of timestamp generation time if the timestamp
369 @retval TRUE The specified timestamp token is valid.
370 @retval FALSE Invalid timestamp token.
375 TimestampTokenVerify (
376 IN CONST UINT8
*TSToken
,
378 IN CONST UINT8
*TsaCert
,
380 IN CONST UINT8
*TimestampedData
,
382 OUT EFI_TIME
*SigningTime
386 CONST UINT8
*TokenTemp
;
389 CONST UINT8
*CertTemp
;
390 X509_STORE
*CertStore
;
394 CONST UINT8
*TstTemp
;
395 TS_TST_INFO
*TstInfo
;
400 // Check input parameters
402 if ((TSToken
== NULL
) || (TsaCert
== NULL
) || (TimestampedData
== NULL
) ||
403 (TokenSize
> INT_MAX
) || (CertSize
> INT_MAX
) || (DataSize
> INT_MAX
))
411 if (SigningTime
!= NULL
) {
412 SetMem (SigningTime
, sizeof (EFI_TIME
), 0);
423 // TimeStamp Token should contain one valid DER-encoded ASN.1 PKCS#7 structure.
426 Pkcs7
= d2i_PKCS7 (NULL
, (const unsigned char **)&TokenTemp
, (int)TokenSize
);
432 // The timestamp signature (TSA's response) will be one PKCS#7 signed data.
434 if (!PKCS7_type_is_signed (Pkcs7
)) {
439 // Read the trusted TSA certificate (DER-encoded), and Construct X509 Certificate.
442 Cert
= d2i_X509 (NULL
, &CertTemp
, (long)CertSize
);
448 // Setup X509 Store for trusted certificate.
450 CertStore
= X509_STORE_new ();
451 if ((CertStore
== NULL
) || !(X509_STORE_add_cert (CertStore
, Cert
))) {
456 // Allow partial certificate chains, terminated by a non-self-signed but
457 // still trusted intermediate certificate. Also disable time checks.
459 X509_STORE_set_flags (
461 X509_V_FLAG_PARTIAL_CHAIN
| X509_V_FLAG_NO_CHECK_TIME
464 X509_STORE_set_purpose (CertStore
, X509_PURPOSE_ANY
);
467 // Verifies the PKCS#7 signedData structure, and output the signed contents.
469 OutBio
= BIO_new (BIO_s_mem ());
470 if (OutBio
== NULL
) {
474 if (!PKCS7_verify (Pkcs7
, NULL
, CertStore
, NULL
, OutBio
, PKCS7_BINARY
)) {
479 // Read the signed contents detached in timestamp signature.
481 TstData
= AllocateZeroPool (2048);
482 if (TstData
== NULL
) {
486 TstSize
= BIO_read (OutBio
, (void *)TstData
, 2048);
489 // Construct TS_TST_INFO structure from the signed contents.
492 TstInfo
= d2i_TS_TST_INFO (
494 (const unsigned char **)&TstTemp
,
497 if (TstInfo
== NULL
) {
502 // Check TS_TST_INFO structure.
504 Status
= CheckTSTInfo (TstInfo
, TimestampedData
, DataSize
);
510 // Retrieve the signing time from TS_TST_INFO structure.
512 if (SigningTime
!= NULL
) {
513 SetMem (SigningTime
, sizeof (EFI_TIME
), 0);
514 Status
= ConvertAsn1TimeToEfiTime (TstInfo
->GenTime
, SigningTime
);
523 X509_STORE_free (CertStore
);
525 TS_TST_INFO_free (TstInfo
);
527 if (TstData
!= NULL
) {
535 Verifies the validity of a RFC3161 Timestamp CounterSignature embedded in PE/COFF Authenticode
538 If AuthData is NULL, then return FALSE.
540 @param[in] AuthData Pointer to the Authenticode Signature retrieved from signed
541 PE/COFF image to be verified.
542 @param[in] DataSize Size of the Authenticode Signature in bytes.
543 @param[in] TsaCert Pointer to a trusted/root TSA certificate encoded in DER, which
544 is used for TSA certificate chain verification.
545 @param[in] CertSize Size of the trusted certificate in bytes.
546 @param[out] SigningTime Return the time of timestamp generation time if the timestamp
549 @retval TRUE The specified Authenticode includes a valid RFC3161 Timestamp CounterSignature.
550 @retval FALSE No valid RFC3161 Timestamp CounterSignature in the specified Authenticode data.
555 ImageTimestampVerify (
556 IN CONST UINT8
*AuthData
,
558 IN CONST UINT8
*TsaCert
,
560 OUT EFI_TIME
*SigningTime
567 STACK_OF (PKCS7_SIGNER_INFO
) *SignerInfos
;
568 PKCS7_SIGNER_INFO
*SignInfo
;
571 STACK_OF (X509_ATTRIBUTE
) *Sk
;
575 ASN1_OCTET_STRING
*EncDigest
;
580 // Input Parameters Checking.
582 if ((AuthData
== NULL
) || (TsaCert
== NULL
)) {
586 if ((DataSize
> INT_MAX
) || (CertSize
> INT_MAX
)) {
591 // Register & Initialize necessary digest algorithms for PKCS#7 Handling.
593 if ((EVP_add_digest (EVP_md5 ()) == 0) || (EVP_add_digest (EVP_sha1 ()) == 0) ||
594 (EVP_add_digest (EVP_sha256 ()) == 0) || ((EVP_add_digest_alias (SN_sha1WithRSAEncryption
, SN_sha1WithRSA
)) == 0))
607 // Decode ASN.1-encoded Authenticode data into PKCS7 structure.
610 Pkcs7
= d2i_PKCS7 (NULL
, (const unsigned char **)&Temp
, (int)DataSize
);
616 // Check if there is one and only one signer.
618 SignerInfos
= PKCS7_get_signer_info (Pkcs7
);
619 if (!SignerInfos
|| (sk_PKCS7_SIGNER_INFO_num (SignerInfos
) != 1)) {
624 // Locate the TimeStamp CounterSignature.
626 SignInfo
= sk_PKCS7_SIGNER_INFO_value (SignerInfos
, 0);
627 if (SignInfo
== NULL
) {
632 // Locate Message Digest which will be the data to be time-stamped.
634 EncDigest
= SignInfo
->enc_digest
;
635 if (EncDigest
== NULL
) {
640 // The RFC3161 timestamp counterSignature is contained in unauthenticatedAttributes field
643 Sk
= SignInfo
->unauth_attr
;
645 // No timestamp counterSignature.
650 for (Index
= 0; Index
< (UINTN
)sk_X509_ATTRIBUTE_num (Sk
); Index
++) {
652 // Search valid RFC3161 timestamp counterSignature based on OBJID.
654 Xa
= sk_X509_ATTRIBUTE_value (Sk
, (int)Index
);
659 XaObj
= X509_ATTRIBUTE_get0_object (Xa
);
664 if ((OBJ_length (XaObj
) != sizeof (mSpcRFC3161OidValue
)) ||
665 (CompareMem (OBJ_get0_data (XaObj
), mSpcRFC3161OidValue
, sizeof (mSpcRFC3161OidValue
)) != 0))
670 Asn1Type
= X509_ATTRIBUTE_get0_type (Xa
, 0);
673 if (Asn1Type
== NULL
) {
678 TSToken
= Asn1Type
->value
.octet_string
->data
;
679 TokenSize
= Asn1Type
->value
.octet_string
->length
;
682 // TimeStamp counterSignature (Token) verification.
684 Status
= TimestampTokenVerify (