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 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 DECLARE_ASN1_FUNCTIONS (TS_MESSAGE_IMPRINT
)
47 ASN1_SEQUENCE (TS_MESSAGE_IMPRINT
) = {
48 ASN1_SIMPLE (TS_MESSAGE_IMPRINT
, HashAlgorithm
, X509_ALGOR
),
49 ASN1_SIMPLE (TS_MESSAGE_IMPRINT
, HashedMessage
, ASN1_OCTET_STRING
)
50 } ASN1_SEQUENCE_END (TS_MESSAGE_IMPRINT
)
51 IMPLEMENT_ASN1_FUNCTIONS (TS_MESSAGE_IMPRINT
)
54 /// Accuracy represents the time deviation around the UTC time contained
55 /// in GeneralizedTime of time-stamp token.
57 /// Accuracy ::= SEQUENCE {
58 /// seconds INTEGER OPTIONAL,
59 /// millis [0] INTEGER (1..999) OPTIONAL,
60 /// micros [1] INTEGER (1..999) OPTIONAL }
63 ASN1_INTEGER
*Seconds
;
69 // ASN.1 Functions for TS_ACCURACY
71 DECLARE_ASN1_FUNCTIONS (TS_ACCURACY
)
72 ASN1_SEQUENCE (TS_ACCURACY
) = {
73 ASN1_OPT (TS_ACCURACY
, Seconds
, ASN1_INTEGER
),
74 ASN1_IMP_OPT (TS_ACCURACY
, Millis
, ASN1_INTEGER
, 0),
75 ASN1_IMP_OPT (TS_ACCURACY
, Micros
, ASN1_INTEGER
, 1)
76 } ASN1_SEQUENCE_END (TS_ACCURACY
)
77 IMPLEMENT_ASN1_FUNCTIONS (TS_ACCURACY
)
80 /// The timestamp token info resulting from a successful timestamp request,
81 /// as defined in RFC 3161.
83 /// TSTInfo ::= SEQUENCE {
84 /// version INTEGER { v1(1) },
85 /// policy TSAPolicyId,
86 /// messageImprint MessageImprint,
87 /// -- MUST have the same value as the similar field in
89 /// serialNumber INTEGER,
90 /// -- Time-Stamping users MUST be ready to accommodate integers
91 /// -- up to 160 bits.
92 /// genTime GeneralizedTime,
93 /// accuracy Accuracy OPTIONAL,
94 /// ordering BOOLEAN DEFAULT FALSE,
95 /// nonce INTEGER OPTIONAL,
96 /// -- MUST be present if the similar field was present
97 /// -- in TimeStampReq. In that case it MUST have the same value.
98 /// tsa [0] GeneralName OPTIONAL,
99 /// extensions [1] IMPLICIT Extensions OPTIONAL }
102 ASN1_INTEGER
*Version
;
104 TS_MESSAGE_IMPRINT
*MessageImprint
;
105 ASN1_INTEGER
*SerialNumber
;
106 ASN1_GENERALIZEDTIME
*GenTime
;
107 TS_ACCURACY
*Accuracy
;
108 ASN1_BOOLEAN Ordering
;
111 STACK_OF(X509_EXTENSION
) *Extensions
;
115 // ASN.1 Functions for TS_TST_INFO
117 DECLARE_ASN1_FUNCTIONS (TS_TST_INFO
)
118 ASN1_SEQUENCE (TS_TST_INFO
) = {
119 ASN1_SIMPLE (TS_TST_INFO
, Version
, ASN1_INTEGER
),
120 ASN1_SIMPLE (TS_TST_INFO
, Policy
, ASN1_OBJECT
),
121 ASN1_SIMPLE (TS_TST_INFO
, MessageImprint
, TS_MESSAGE_IMPRINT
),
122 ASN1_SIMPLE (TS_TST_INFO
, SerialNumber
, ASN1_INTEGER
),
123 ASN1_SIMPLE (TS_TST_INFO
, GenTime
, ASN1_GENERALIZEDTIME
),
124 ASN1_OPT (TS_TST_INFO
, Accuracy
, TS_ACCURACY
),
125 ASN1_OPT (TS_TST_INFO
, Ordering
, ASN1_FBOOLEAN
),
126 ASN1_OPT (TS_TST_INFO
, Nonce
, ASN1_INTEGER
),
127 ASN1_EXP_OPT(TS_TST_INFO
, Tsa
, GENERAL_NAME
, 0),
128 ASN1_IMP_SEQUENCE_OF_OPT (TS_TST_INFO
, Extensions
, X509_EXTENSION
, 1)
129 } ASN1_SEQUENCE_END (TS_TST_INFO
)
130 IMPLEMENT_ASN1_FUNCTIONS (TS_TST_INFO
)
134 Convert ASN.1 GeneralizedTime to EFI Time.
136 @param[in] Asn1Time Pointer to the ASN.1 GeneralizedTime to be converted.
137 @param[out] SigningTime Return the corresponding EFI Time.
139 @retval TRUE The time conversion succeeds.
140 @retval FALSE Invalid parameters.
145 ConvertAsn1TimeToEfiTime (
146 IN ASN1_TIME
*Asn1Time
,
147 OUT EFI_TIME
*EfiTime
153 if ((Asn1Time
== NULL
) || (EfiTime
== NULL
)) {
157 Str
= (CONST CHAR8
*)Asn1Time
->data
;
158 SetMem (EfiTime
, 0, sizeof (EFI_TIME
));
161 if (Asn1Time
->type
== V_ASN1_UTCTIME
) { /* two digit year */
162 EfiTime
->Year
= (Str
[Index
++] - '0') * 10;
163 EfiTime
->Year
+= (Str
[Index
++] - '0');
164 if (EfiTime
->Year
< 70) {
165 EfiTime
->Year
+= 100;
167 } else if (Asn1Time
->type
== V_ASN1_GENERALIZEDTIME
) { /* four digit year */
168 EfiTime
->Year
= (Str
[Index
++] - '0') * 1000;
169 EfiTime
->Year
+= (Str
[Index
++] - '0') * 100;
170 EfiTime
->Year
+= (Str
[Index
++] - '0') * 10;
171 EfiTime
->Year
+= (Str
[Index
++] - '0');
172 if ((EfiTime
->Year
< 1900) || (EfiTime
->Year
> 9999)) {
177 EfiTime
->Month
= (Str
[Index
++] - '0') * 10;
178 EfiTime
->Month
+= (Str
[Index
++] - '0');
179 if ((EfiTime
->Month
< 1) || (EfiTime
->Month
> 12)) {
183 EfiTime
->Day
= (Str
[Index
++] - '0') * 10;
184 EfiTime
->Day
+= (Str
[Index
++] - '0');
185 if ((EfiTime
->Day
< 1) || (EfiTime
->Day
> 31)) {
189 EfiTime
->Hour
= (Str
[Index
++] - '0') * 10;
190 EfiTime
->Hour
+= (Str
[Index
++] - '0');
191 if (EfiTime
->Hour
> 23) {
195 EfiTime
->Minute
= (Str
[Index
++] - '0') * 10;
196 EfiTime
->Minute
+= (Str
[Index
++] - '0');
197 if (EfiTime
->Minute
> 59) {
201 EfiTime
->Second
= (Str
[Index
++] - '0') * 10;
202 EfiTime
->Second
+= (Str
[Index
++] - '0');
203 if (EfiTime
->Second
> 59) {
207 /* Note: we did not adjust the time based on time zone information */
214 Check the validity of TimeStamp Token Information.
216 @param[in] TstInfo Pointer to the TS_TST_INFO structure.
217 @param[in] TimestampedData Pointer to the data to be time-stamped.
218 @param[in] DataSize Size of timestamped data in bytes.
220 @retval TRUE The TimeStamp Token Information is valid.
221 @retval FALSE Invalid TimeStamp Token Information.
227 IN CONST TS_TST_INFO
*TstInfo
,
228 IN CONST UINT8
*TimestampedData
,
233 TS_MESSAGE_IMPRINT
*Imprint
;
234 X509_ALGOR
*HashAlgo
;
249 // -- Check version number of Timestamp:
250 // The version field (currently v1) describes the version of the time-stamp token.
251 // Conforming time-stamping servers MUST be able to provide version 1 time-stamp tokens.
253 if ((ASN1_INTEGER_get (TstInfo
->Version
)) != 1) {
259 // The policy field MUST indicate the TSA's policy under which the response was produced.
261 if (TstInfo
->Policy
== NULL
) {
262 /// NOTE: Need to check if the requested and returned policies.
263 /// We have no information about the Requested TSA Policy.
268 // -- Compute & Check Message Imprint
270 Imprint
= TstInfo
->MessageImprint
;
271 HashAlgo
= X509_ALGOR_dup (Imprint
->HashAlgorithm
);
273 Md
= EVP_get_digestbyobj (HashAlgo
->algorithm
);
278 MdSize
= EVP_MD_size (Md
);
279 HashedMsg
= AllocateZeroPool (MdSize
);
280 if (HashedMsg
== NULL
) {
283 MdCtx
= EVP_MD_CTX_new ();
287 if ((EVP_DigestInit_ex (MdCtx
, Md
, NULL
) != 1) ||
288 (EVP_DigestUpdate (MdCtx
, TimestampedData
, DataSize
) != 1) ||
289 (EVP_DigestFinal (MdCtx
, HashedMsg
, NULL
) != 1)) {
292 if ((MdSize
== (UINTN
)ASN1_STRING_length (Imprint
->HashedMessage
)) &&
293 (CompareMem (HashedMsg
, ASN1_STRING_get0_data (Imprint
->HashedMessage
), MdSize
) != 0)) {
300 if (TstInfo
->Nonce
!= NULL
) {
302 // Nonces is optional, No error if no nonce is returned;
307 // -- Check if the TSA name and signer certificate is matched.
309 if (TstInfo
->Tsa
!= NULL
) {
311 // Ignored the optional Tsa field checking.
318 X509_ALGOR_free (HashAlgo
);
319 EVP_MD_CTX_free (MdCtx
);
320 if (HashedMsg
!= NULL
) {
321 FreePool (HashedMsg
);
328 Verifies the validity of a TimeStamp Token as described in RFC 3161 ("Internet
329 X.509 Public Key Infrastructure Time-Stamp Protocol (TSP)").
331 If TSToken is NULL, then return FALSE.
332 If TimestampedData is NULL, then return FALSE.
334 @param[in] TSToken Pointer to the RFC3161 TimeStamp Token, which is generated
335 by a TSA and located in the software publisher's SignerInfo
337 @param[in] TokenSize Size of the TimeStamp Token in bytes.
338 @param[in] TsaCert Pointer to a trusted/root TSA certificate encoded in DER.
339 @param[in] CertSize Size of the trusted TSA certificate in bytes.
340 @param[in] TimestampedData Pointer to the data to be time-stamped.
341 @param[in] DataSize Size of timestamped data in bytes.
342 @param[out] SigningTime Return the time of timestamp generation time if the timestamp
345 @retval TRUE The specified timestamp token is valid.
346 @retval FALSE Invalid timestamp token.
351 TimestampTokenVerify (
352 IN CONST UINT8
*TSToken
,
354 IN CONST UINT8
*TsaCert
,
356 IN CONST UINT8
*TimestampedData
,
358 OUT EFI_TIME
*SigningTime
362 CONST UINT8
*TokenTemp
;
365 CONST UINT8
*CertTemp
;
366 X509_STORE
*CertStore
;
370 CONST UINT8
*TstTemp
;
371 TS_TST_INFO
*TstInfo
;
376 // Check input parameters
378 if ((TSToken
== NULL
) || (TsaCert
== NULL
) || (TimestampedData
== NULL
) ||
379 (TokenSize
> INT_MAX
) || (CertSize
> INT_MAX
) || (DataSize
> INT_MAX
)) {
386 if (SigningTime
!= NULL
) {
387 SetMem (SigningTime
, sizeof (EFI_TIME
), 0);
397 // TimeStamp Token should contain one valid DER-encoded ASN.1 PKCS#7 structure.
400 Pkcs7
= d2i_PKCS7 (NULL
, (const unsigned char **) &TokenTemp
, (int) TokenSize
);
406 // The timestamp signature (TSA's response) will be one PKCS#7 signed data.
408 if (!PKCS7_type_is_signed (Pkcs7
)) {
413 // Read the trusted TSA certificate (DER-encoded), and Construct X509 Certificate.
416 Cert
= d2i_X509 (NULL
, &CertTemp
, (long) CertSize
);
422 // Setup X509 Store for trusted certificate.
424 CertStore
= X509_STORE_new ();
425 if ((CertStore
== NULL
) || !(X509_STORE_add_cert (CertStore
, Cert
))) {
430 // Allow partial certificate chains, terminated by a non-self-signed but
431 // still trusted intermediate certificate. Also disable time checks.
433 X509_STORE_set_flags (CertStore
,
434 X509_V_FLAG_PARTIAL_CHAIN
| X509_V_FLAG_NO_CHECK_TIME
);
436 X509_STORE_set_purpose (CertStore
, X509_PURPOSE_ANY
);
439 // Verifies the PKCS#7 signedData structure, and output the signed contents.
441 OutBio
= BIO_new (BIO_s_mem ());
442 if (OutBio
== NULL
) {
445 if (!PKCS7_verify (Pkcs7
, NULL
, CertStore
, NULL
, OutBio
, PKCS7_BINARY
)) {
450 // Read the signed contents detached in timestamp signature.
452 TstData
= AllocateZeroPool (2048);
453 if (TstData
== NULL
) {
456 TstSize
= BIO_read (OutBio
, (void *) TstData
, 2048);
459 // Construct TS_TST_INFO structure from the signed contents.
462 TstInfo
= d2i_TS_TST_INFO (NULL
, (const unsigned char **) &TstTemp
,
464 if (TstInfo
== NULL
) {
469 // Check TS_TST_INFO structure.
471 Status
= CheckTSTInfo (TstInfo
, TimestampedData
, DataSize
);
477 // Retrieve the signing time from TS_TST_INFO structure.
479 if (SigningTime
!= NULL
) {
480 SetMem (SigningTime
, sizeof (EFI_TIME
), 0);
481 Status
= ConvertAsn1TimeToEfiTime (TstInfo
->GenTime
, SigningTime
);
490 X509_STORE_free (CertStore
);
492 TS_TST_INFO_free (TstInfo
);
494 if (TstData
!= NULL
) {
502 Verifies the validity of a RFC3161 Timestamp CounterSignature embedded in PE/COFF Authenticode
505 If AuthData is NULL, then return FALSE.
507 @param[in] AuthData Pointer to the Authenticode Signature retrieved from signed
508 PE/COFF image to be verified.
509 @param[in] DataSize Size of the Authenticode Signature in bytes.
510 @param[in] TsaCert Pointer to a trusted/root TSA certificate encoded in DER, which
511 is used for TSA certificate chain verification.
512 @param[in] CertSize Size of the trusted certificate in bytes.
513 @param[out] SigningTime Return the time of timestamp generation time if the timestamp
516 @retval TRUE The specified Authenticode includes a valid RFC3161 Timestamp CounterSignature.
517 @retval FALSE No valid RFC3161 Timestamp CounterSignature in the specified Authenticode data.
522 ImageTimestampVerify (
523 IN CONST UINT8
*AuthData
,
525 IN CONST UINT8
*TsaCert
,
527 OUT EFI_TIME
*SigningTime
533 STACK_OF(PKCS7_SIGNER_INFO
) *SignerInfos
;
534 PKCS7_SIGNER_INFO
*SignInfo
;
536 STACK_OF(X509_ATTRIBUTE
) *Sk
;
540 ASN1_OCTET_STRING
*EncDigest
;
545 // Input Parameters Checking.
547 if ((AuthData
== NULL
) || (TsaCert
== NULL
)) {
551 if ((DataSize
> INT_MAX
) || (CertSize
> INT_MAX
)) {
556 // Register & Initialize necessary digest algorithms for PKCS#7 Handling.
558 if ((EVP_add_digest (EVP_md5 ()) == 0) || (EVP_add_digest (EVP_sha1 ()) == 0) ||
559 (EVP_add_digest (EVP_sha256 ()) == 0) || (EVP_add_digest_alias (SN_sha1WithRSAEncryption
, SN_sha1WithRSA
)) == 0) {
571 // Decode ASN.1-encoded Authenticode data into PKCS7 structure.
574 Pkcs7
= d2i_PKCS7 (NULL
, (const unsigned char **) &Temp
, (int) DataSize
);
580 // Check if there is one and only one signer.
582 SignerInfos
= PKCS7_get_signer_info (Pkcs7
);
583 if (!SignerInfos
|| (sk_PKCS7_SIGNER_INFO_num (SignerInfos
) != 1)) {
588 // Locate the TimeStamp CounterSignature.
590 SignInfo
= sk_PKCS7_SIGNER_INFO_value (SignerInfos
, 0);
591 if (SignInfo
== NULL
) {
596 // Locate Message Digest which will be the data to be time-stamped.
598 EncDigest
= SignInfo
->enc_digest
;
599 if (EncDigest
== NULL
) {
604 // The RFC3161 timestamp counterSignature is contained in unauthenticatedAttributes field
607 Sk
= SignInfo
->unauth_attr
;
608 if (Sk
== NULL
) { // No timestamp counterSignature.
613 for (Index
= 0; Index
< (UINTN
) sk_X509_ATTRIBUTE_num (Sk
); Index
++) {
615 // Search valid RFC3161 timestamp counterSignature based on OBJID.
617 Xa
= sk_X509_ATTRIBUTE_value (Sk
, (int)Index
);
621 XaObj
= X509_ATTRIBUTE_get0_object(Xa
);
625 if ((OBJ_length(XaObj
) != sizeof (mSpcRFC3161OidValue
)) ||
626 (CompareMem (OBJ_get0_data(XaObj
), mSpcRFC3161OidValue
, sizeof (mSpcRFC3161OidValue
)) != 0)) {
629 Asn1Type
= X509_ATTRIBUTE_get0_type(Xa
, 0);
632 if (Asn1Type
== NULL
) {
636 TSToken
= Asn1Type
->value
.octet_string
->data
;
637 TokenSize
= Asn1Type
->value
.octet_string
->length
;
640 // TimeStamp counterSignature (Token) verification.
642 Status
= TimestampTokenVerify (