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
)
133 Convert ASN.1 GeneralizedTime to EFI Time.
135 @param[in] Asn1Time Pointer to the ASN.1 GeneralizedTime to be converted.
136 @param[out] SigningTime Return the corresponding EFI Time.
138 @retval TRUE The time conversion succeeds.
139 @retval FALSE Invalid parameters.
144 ConvertAsn1TimeToEfiTime (
145 IN ASN1_TIME
*Asn1Time
,
146 OUT EFI_TIME
*EfiTime
152 if ((Asn1Time
== NULL
) || (EfiTime
== NULL
)) {
156 Str
= (CONST CHAR8
*)Asn1Time
->data
;
157 SetMem (EfiTime
, sizeof (EFI_TIME
), 0);
160 if (Asn1Time
->type
== V_ASN1_UTCTIME
) {
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
) {
168 /* four digit year */
169 EfiTime
->Year
= (Str
[Index
++] - '0') * 1000;
170 EfiTime
->Year
+= (Str
[Index
++] - '0') * 100;
171 EfiTime
->Year
+= (Str
[Index
++] - '0') * 10;
172 EfiTime
->Year
+= (Str
[Index
++] - '0');
173 if ((EfiTime
->Year
< 1900) || (EfiTime
->Year
> 9999)) {
178 EfiTime
->Month
= (Str
[Index
++] - '0') * 10;
179 EfiTime
->Month
+= (Str
[Index
++] - '0');
180 if ((EfiTime
->Month
< 1) || (EfiTime
->Month
> 12)) {
184 EfiTime
->Day
= (Str
[Index
++] - '0') * 10;
185 EfiTime
->Day
+= (Str
[Index
++] - '0');
186 if ((EfiTime
->Day
< 1) || (EfiTime
->Day
> 31)) {
190 EfiTime
->Hour
= (Str
[Index
++] - '0') * 10;
191 EfiTime
->Hour
+= (Str
[Index
++] - '0');
192 if (EfiTime
->Hour
> 23) {
196 EfiTime
->Minute
= (Str
[Index
++] - '0') * 10;
197 EfiTime
->Minute
+= (Str
[Index
++] - '0');
198 if (EfiTime
->Minute
> 59) {
202 EfiTime
->Second
= (Str
[Index
++] - '0') * 10;
203 EfiTime
->Second
+= (Str
[Index
++] - '0');
204 if (EfiTime
->Second
> 59) {
208 /* Note: we did not adjust the time based on time zone information */
215 Check the validity of TimeStamp Token Information.
217 @param[in] TstInfo Pointer to the TS_TST_INFO structure.
218 @param[in] TimestampedData Pointer to the data to be time-stamped.
219 @param[in] DataSize Size of timestamped data in bytes.
221 @retval TRUE The TimeStamp Token Information is valid.
222 @retval FALSE Invalid TimeStamp Token Information.
228 IN CONST TS_TST_INFO
*TstInfo
,
229 IN CONST UINT8
*TimestampedData
,
234 TS_MESSAGE_IMPRINT
*Imprint
;
235 X509_ALGOR
*HashAlgo
;
250 // -- Check version number of Timestamp:
251 // The version field (currently v1) describes the version of the time-stamp token.
252 // Conforming time-stamping servers MUST be able to provide version 1 time-stamp tokens.
254 if ((ASN1_INTEGER_get (TstInfo
->Version
)) != 1) {
260 // The policy field MUST indicate the TSA's policy under which the response was produced.
262 if (TstInfo
->Policy
== NULL
) {
263 /// NOTE: Need to check if the requested and returned policies.
264 /// We have no information about the Requested TSA Policy.
269 // -- Compute & Check Message Imprint
271 Imprint
= TstInfo
->MessageImprint
;
272 HashAlgo
= X509_ALGOR_dup (Imprint
->HashAlgorithm
);
274 Md
= EVP_get_digestbyobj (HashAlgo
->algorithm
);
279 MdSize
= EVP_MD_size (Md
);
280 HashedMsg
= AllocateZeroPool (MdSize
);
281 if (HashedMsg
== NULL
) {
285 MdCtx
= EVP_MD_CTX_new ();
290 if ((EVP_DigestInit_ex (MdCtx
, Md
, NULL
) != 1) ||
291 (EVP_DigestUpdate (MdCtx
, TimestampedData
, DataSize
) != 1) ||
292 (EVP_DigestFinal (MdCtx
, HashedMsg
, NULL
) != 1))
297 if ((MdSize
== (UINTN
)ASN1_STRING_length (Imprint
->HashedMessage
)) &&
298 (CompareMem (HashedMsg
, ASN1_STRING_get0_data (Imprint
->HashedMessage
), MdSize
) != 0))
306 if (TstInfo
->Nonce
!= NULL
) {
308 // Nonces is optional, No error if no nonce is returned;
313 // -- Check if the TSA name and signer certificate is matched.
315 if (TstInfo
->Tsa
!= NULL
) {
317 // Ignored the optional Tsa field checking.
324 X509_ALGOR_free (HashAlgo
);
325 EVP_MD_CTX_free (MdCtx
);
326 if (HashedMsg
!= NULL
) {
327 FreePool (HashedMsg
);
334 Verifies the validity of a TimeStamp Token as described in RFC 3161 ("Internet
335 X.509 Public Key Infrastructure Time-Stamp Protocol (TSP)").
337 If TSToken is NULL, then return FALSE.
338 If TimestampedData is NULL, then return FALSE.
340 @param[in] TSToken Pointer to the RFC3161 TimeStamp Token, which is generated
341 by a TSA and located in the software publisher's SignerInfo
343 @param[in] TokenSize Size of the TimeStamp Token in bytes.
344 @param[in] TsaCert Pointer to a trusted/root TSA certificate encoded in DER.
345 @param[in] CertSize Size of the trusted TSA certificate in bytes.
346 @param[in] TimestampedData Pointer to the data to be time-stamped.
347 @param[in] DataSize Size of timestamped data in bytes.
348 @param[out] SigningTime Return the time of timestamp generation time if the timestamp
351 @retval TRUE The specified timestamp token is valid.
352 @retval FALSE Invalid timestamp token.
357 TimestampTokenVerify (
358 IN CONST UINT8
*TSToken
,
360 IN CONST UINT8
*TsaCert
,
362 IN CONST UINT8
*TimestampedData
,
364 OUT EFI_TIME
*SigningTime
368 CONST UINT8
*TokenTemp
;
371 CONST UINT8
*CertTemp
;
372 X509_STORE
*CertStore
;
376 CONST UINT8
*TstTemp
;
377 TS_TST_INFO
*TstInfo
;
382 // Check input parameters
384 if ((TSToken
== NULL
) || (TsaCert
== NULL
) || (TimestampedData
== NULL
) ||
385 (TokenSize
> INT_MAX
) || (CertSize
> INT_MAX
) || (DataSize
> INT_MAX
))
393 if (SigningTime
!= NULL
) {
394 SetMem (SigningTime
, sizeof (EFI_TIME
), 0);
405 // TimeStamp Token should contain one valid DER-encoded ASN.1 PKCS#7 structure.
408 Pkcs7
= d2i_PKCS7 (NULL
, (const unsigned char **)&TokenTemp
, (int)TokenSize
);
414 // The timestamp signature (TSA's response) will be one PKCS#7 signed data.
416 if (!PKCS7_type_is_signed (Pkcs7
)) {
421 // Read the trusted TSA certificate (DER-encoded), and Construct X509 Certificate.
424 Cert
= d2i_X509 (NULL
, &CertTemp
, (long)CertSize
);
430 // Setup X509 Store for trusted certificate.
432 CertStore
= X509_STORE_new ();
433 if ((CertStore
== NULL
) || !(X509_STORE_add_cert (CertStore
, Cert
))) {
438 // Allow partial certificate chains, terminated by a non-self-signed but
439 // still trusted intermediate certificate. Also disable time checks.
441 X509_STORE_set_flags (
443 X509_V_FLAG_PARTIAL_CHAIN
| X509_V_FLAG_NO_CHECK_TIME
446 X509_STORE_set_purpose (CertStore
, X509_PURPOSE_ANY
);
449 // Verifies the PKCS#7 signedData structure, and output the signed contents.
451 OutBio
= BIO_new (BIO_s_mem ());
452 if (OutBio
== NULL
) {
456 if (!PKCS7_verify (Pkcs7
, NULL
, CertStore
, NULL
, OutBio
, PKCS7_BINARY
)) {
461 // Read the signed contents detached in timestamp signature.
463 TstData
= AllocateZeroPool (2048);
464 if (TstData
== NULL
) {
468 TstSize
= BIO_read (OutBio
, (void *)TstData
, 2048);
471 // Construct TS_TST_INFO structure from the signed contents.
474 TstInfo
= d2i_TS_TST_INFO (
476 (const unsigned char **)&TstTemp
,
479 if (TstInfo
== NULL
) {
484 // Check TS_TST_INFO structure.
486 Status
= CheckTSTInfo (TstInfo
, TimestampedData
, DataSize
);
492 // Retrieve the signing time from TS_TST_INFO structure.
494 if (SigningTime
!= NULL
) {
495 SetMem (SigningTime
, sizeof (EFI_TIME
), 0);
496 Status
= ConvertAsn1TimeToEfiTime (TstInfo
->GenTime
, SigningTime
);
505 X509_STORE_free (CertStore
);
507 TS_TST_INFO_free (TstInfo
);
509 if (TstData
!= NULL
) {
517 Verifies the validity of a RFC3161 Timestamp CounterSignature embedded in PE/COFF Authenticode
520 If AuthData is NULL, then return FALSE.
522 @param[in] AuthData Pointer to the Authenticode Signature retrieved from signed
523 PE/COFF image to be verified.
524 @param[in] DataSize Size of the Authenticode Signature in bytes.
525 @param[in] TsaCert Pointer to a trusted/root TSA certificate encoded in DER, which
526 is used for TSA certificate chain verification.
527 @param[in] CertSize Size of the trusted certificate in bytes.
528 @param[out] SigningTime Return the time of timestamp generation time if the timestamp
531 @retval TRUE The specified Authenticode includes a valid RFC3161 Timestamp CounterSignature.
532 @retval FALSE No valid RFC3161 Timestamp CounterSignature in the specified Authenticode data.
537 ImageTimestampVerify (
538 IN CONST UINT8
*AuthData
,
540 IN CONST UINT8
*TsaCert
,
542 OUT EFI_TIME
*SigningTime
549 STACK_OF (PKCS7_SIGNER_INFO
) *SignerInfos
;
550 PKCS7_SIGNER_INFO
*SignInfo
;
553 STACK_OF (X509_ATTRIBUTE
) *Sk
;
557 ASN1_OCTET_STRING
*EncDigest
;
562 // Input Parameters Checking.
564 if ((AuthData
== NULL
) || (TsaCert
== NULL
)) {
568 if ((DataSize
> INT_MAX
) || (CertSize
> INT_MAX
)) {
573 // Register & Initialize necessary digest algorithms for PKCS#7 Handling.
575 if ((EVP_add_digest (EVP_md5 ()) == 0) || (EVP_add_digest (EVP_sha1 ()) == 0) ||
576 (EVP_add_digest (EVP_sha256 ()) == 0) || ((EVP_add_digest_alias (SN_sha1WithRSAEncryption
, SN_sha1WithRSA
)) == 0))
589 // Decode ASN.1-encoded Authenticode data into PKCS7 structure.
592 Pkcs7
= d2i_PKCS7 (NULL
, (const unsigned char **)&Temp
, (int)DataSize
);
598 // Check if there is one and only one signer.
600 SignerInfos
= PKCS7_get_signer_info (Pkcs7
);
601 if (!SignerInfos
|| (sk_PKCS7_SIGNER_INFO_num (SignerInfos
) != 1)) {
606 // Locate the TimeStamp CounterSignature.
608 SignInfo
= sk_PKCS7_SIGNER_INFO_value (SignerInfos
, 0);
609 if (SignInfo
== NULL
) {
614 // Locate Message Digest which will be the data to be time-stamped.
616 EncDigest
= SignInfo
->enc_digest
;
617 if (EncDigest
== NULL
) {
622 // The RFC3161 timestamp counterSignature is contained in unauthenticatedAttributes field
625 Sk
= SignInfo
->unauth_attr
;
627 // No timestamp counterSignature.
632 for (Index
= 0; Index
< (UINTN
)sk_X509_ATTRIBUTE_num (Sk
); Index
++) {
634 // Search valid RFC3161 timestamp counterSignature based on OBJID.
636 Xa
= sk_X509_ATTRIBUTE_value (Sk
, (int)Index
);
641 XaObj
= X509_ATTRIBUTE_get0_object (Xa
);
646 if ((OBJ_length (XaObj
) != sizeof (mSpcRFC3161OidValue
)) ||
647 (CompareMem (OBJ_get0_data (XaObj
), mSpcRFC3161OidValue
, sizeof (mSpcRFC3161OidValue
)) != 0))
652 Asn1Type
= X509_ATTRIBUTE_get0_type (Xa
, 0);
655 if (Asn1Type
== NULL
) {
660 TSToken
= Asn1Type
->value
.octet_string
->data
;
661 TokenSize
= Asn1Type
->value
.octet_string
->length
;
664 // TimeStamp counterSignature (Token) verification.
666 Status
= TimestampTokenVerify (