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 - 2015, Intel Corporation. All rights reserved.<BR>
9 This program and the accompanying materials
10 are licensed and made available under the terms and conditions of the BSD License
11 which accompanies this distribution. The full text of the license may be found at
12 http://opensource.org/licenses/bsd-license.php
14 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
15 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
19 #include "InternalCryptLib.h"
21 #include <openssl/asn1.h>
22 #include <openssl/asn1t.h>
23 #include <openssl/x509.h>
24 #include <openssl/x509v3.h>
25 #include <openssl/pkcs7.h>
28 // OID ASN.1 Value for SPC_RFC3161_OBJID ("1.3.6.1.4.1.311.3.3.1")
30 UINT8 mSpcRFC3161OidValue
[] = {
31 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x03, 0x03, 0x01
35 /// The messageImprint field SHOULD contain the hash of the datum to be
36 /// time-stamped. The hash is represented as an OCTET STRING. Its
37 /// length MUST match the length of the hash value for that algorithm
38 /// (e.g., 20 bytes for SHA-1 or 16 bytes for MD5).
40 /// MessageImprint ::= SEQUENCE {
41 /// hashAlgorithm AlgorithmIdentifier,
42 /// hashedMessage OCTET STRING }
45 X509_ALGOR
*HashAlgorithm
;
46 ASN1_OCTET_STRING
*HashedMessage
;
50 // ASN.1 Functions for TS_MESSAGE_IMPRINT
52 DECLARE_ASN1_FUNCTIONS (TS_MESSAGE_IMPRINT
)
53 ASN1_SEQUENCE (TS_MESSAGE_IMPRINT
) = {
54 ASN1_SIMPLE (TS_MESSAGE_IMPRINT
, HashAlgorithm
, X509_ALGOR
),
55 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 DECLARE_ASN1_FUNCTIONS (TS_ACCURACY
)
78 ASN1_SEQUENCE (TS_ACCURACY
) = {
79 ASN1_OPT (TS_ACCURACY
, Seconds
, ASN1_INTEGER
),
80 ASN1_IMP_OPT (TS_ACCURACY
, Millis
, ASN1_INTEGER
, 0),
81 ASN1_IMP_OPT (TS_ACCURACY
, Micros
, ASN1_INTEGER
, 1)
82 } ASN1_SEQUENCE_END (TS_ACCURACY
)
83 IMPLEMENT_ASN1_FUNCTIONS (TS_ACCURACY
)
86 /// The timestamp token info resulting from a successful timestamp request,
87 /// as defined in RFC 3161.
89 /// TSTInfo ::= SEQUENCE {
90 /// version INTEGER { v1(1) },
91 /// policy TSAPolicyId,
92 /// messageImprint MessageImprint,
93 /// -- MUST have the same value as the similar field in
95 /// serialNumber INTEGER,
96 /// -- Time-Stamping users MUST be ready to accommodate integers
97 /// -- up to 160 bits.
98 /// genTime GeneralizedTime,
99 /// accuracy Accuracy OPTIONAL,
100 /// ordering BOOLEAN DEFAULT FALSE,
101 /// nonce INTEGER OPTIONAL,
102 /// -- MUST be present if the similar field was present
103 /// -- in TimeStampReq. In that case it MUST have the same value.
104 /// tsa [0] GeneralName OPTIONAL,
105 /// extensions [1] IMPLICIT Extensions OPTIONAL }
108 ASN1_INTEGER
*Version
;
110 TS_MESSAGE_IMPRINT
*MessageImprint
;
111 ASN1_INTEGER
*SerialNumber
;
112 ASN1_GENERALIZEDTIME
*GenTime
;
113 TS_ACCURACY
*Accuracy
;
114 ASN1_BOOLEAN Ordering
;
117 STACK_OF(X509_EXTENSION
) *Extensions
;
121 // ASN.1 Functions for TS_TST_INFO
123 DECLARE_ASN1_FUNCTIONS (TS_TST_INFO
)
124 ASN1_SEQUENCE (TS_TST_INFO
) = {
125 ASN1_SIMPLE (TS_TST_INFO
, Version
, ASN1_INTEGER
),
126 ASN1_SIMPLE (TS_TST_INFO
, Policy
, ASN1_OBJECT
),
127 ASN1_SIMPLE (TS_TST_INFO
, MessageImprint
, TS_MESSAGE_IMPRINT
),
128 ASN1_SIMPLE (TS_TST_INFO
, SerialNumber
, ASN1_INTEGER
),
129 ASN1_SIMPLE (TS_TST_INFO
, GenTime
, ASN1_GENERALIZEDTIME
),
130 ASN1_OPT (TS_TST_INFO
, Accuracy
, TS_ACCURACY
),
131 ASN1_OPT (TS_TST_INFO
, Ordering
, ASN1_FBOOLEAN
),
132 ASN1_OPT (TS_TST_INFO
, Nonce
, ASN1_INTEGER
),
133 ASN1_EXP_OPT(TS_TST_INFO
, Tsa
, GENERAL_NAME
, 0),
134 ASN1_IMP_SEQUENCE_OF_OPT (TS_TST_INFO
, Extensions
, X509_EXTENSION
, 1)
135 } ASN1_SEQUENCE_END (TS_TST_INFO
)
136 IMPLEMENT_ASN1_FUNCTIONS (TS_TST_INFO
)
140 Verification callback function to override any existing callbacks in OpenSSL
141 for intermediate TSA certificate supports.
143 @param[in] Status Original status before calling this callback.
144 @param[in] Context X509 store context.
146 @retval 1 Current X509 certificate is verified successfully.
147 @retval 0 Verification failed.
153 IN X509_STORE_CTX
*Context
162 Error
= (INTN
) X509_STORE_CTX_get_error (Context
);
165 // X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT and X509_V_ERR_UNABLE_TO_GET_ISSUER_
166 // CERT_LOCALLY mean a X509 certificate is not self signed and its issuer
167 // can not be found in X509_verify_cert of X509_vfy.c.
168 // In order to support intermediate certificate node, we override the
169 // errors if the certification is obtained from X509 store, i.e. it is
170 // a trusted ceritifcate node that is enrolled by user.
171 // Besides,X509_V_ERR_CERT_UNTRUSTED and X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE
172 // are also ignored to enable such feature.
174 if ((Error
== X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT
) ||
175 (Error
== X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY
)) {
176 Obj
= (X509_OBJECT
*) malloc (sizeof (X509_OBJECT
));
181 Obj
->type
= X509_LU_X509
;
182 Obj
->data
.x509
= Context
->current_cert
;
184 CRYPTO_w_lock (CRYPTO_LOCK_X509_STORE
);
186 if (X509_OBJECT_retrieve_match (Context
->ctx
->objs
, Obj
)) {
190 // If any certificate in the chain is enrolled as trusted certificate,
191 // pass the certificate verification.
193 if (Error
== X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY
) {
194 Count
= (INTN
) sk_X509_num (Context
->chain
);
195 for (Index
= 0; Index
< Count
; Index
++) {
196 Obj
->data
.x509
= sk_X509_value (Context
->chain
, (int) Index
);
197 if (X509_OBJECT_retrieve_match (Context
->ctx
->objs
, Obj
)) {
205 CRYPTO_w_unlock (CRYPTO_LOCK_X509_STORE
);
208 if ((Error
== X509_V_ERR_CERT_UNTRUSTED
) ||
209 (Error
== X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE
)) {
221 Convert ASN.1 GeneralizedTime to EFI Time.
223 @param[in] Asn1Time Pointer to the ASN.1 GeneralizedTime to be converted.
224 @param[out] SigningTime Return the corresponding EFI Time.
226 @retval TRUE The time convertion succeeds.
227 @retval FALSE Invalid parameters.
232 ConvertAsn1TimeToEfiTime (
233 IN ASN1_TIME
*Asn1Time
,
234 OUT EFI_TIME
*EfiTime
240 if ((Asn1Time
== NULL
) || (EfiTime
== NULL
)) {
244 Str
= (CONST CHAR8
*)Asn1Time
->data
;
245 SetMem (EfiTime
, 0, sizeof (EFI_TIME
));
248 if (Asn1Time
->type
== V_ASN1_UTCTIME
) { /* two digit year */
249 EfiTime
->Year
= (Str
[Index
++] - '0') * 10;
250 EfiTime
->Year
+= (Str
[Index
++] - '0');
251 if (EfiTime
->Year
< 70) {
252 EfiTime
->Year
+= 100;
254 } else if (Asn1Time
->type
== V_ASN1_GENERALIZEDTIME
) { /* four digit year */
255 EfiTime
->Year
= (Str
[Index
++] - '0') * 1000;
256 EfiTime
->Year
+= (Str
[Index
++] - '0') * 100;
257 EfiTime
->Year
+= (Str
[Index
++] - '0') * 10;
258 EfiTime
->Year
+= (Str
[Index
++] - '0');
259 if ((EfiTime
->Year
< 1900) || (EfiTime
->Year
> 9999)) {
264 EfiTime
->Month
= (Str
[Index
++] - '0') * 10;
265 EfiTime
->Month
+= (Str
[Index
++] - '0');
266 if ((EfiTime
->Month
< 1) || (EfiTime
->Month
> 12)) {
270 EfiTime
->Day
= (Str
[Index
++] - '0') * 10;
271 EfiTime
->Day
+= (Str
[Index
++] - '0');
272 if ((EfiTime
->Day
< 1) || (EfiTime
->Day
> 31)) {
276 EfiTime
->Hour
= (Str
[Index
++] - '0') * 10;
277 EfiTime
->Hour
+= (Str
[Index
++] - '0');
278 if (EfiTime
->Hour
> 23) {
282 EfiTime
->Minute
= (Str
[Index
++] - '0') * 10;
283 EfiTime
->Minute
+= (Str
[Index
++] - '0');
284 if (EfiTime
->Minute
> 59) {
288 EfiTime
->Second
= (Str
[Index
++] - '0') * 10;
289 EfiTime
->Second
+= (Str
[Index
++] - '0');
290 if (EfiTime
->Second
> 59) {
294 /* Note: we did not adjust the time based on time zone information */
301 Check the validity of TimeStamp Token Information.
303 @param[in] TstInfo Pointer to the TS_TST_INFO structure.
304 @param[in] TimestampedData Pointer to the data to be time-stamped.
305 @param[in] DataSize Size of timestamped data in bytes.
307 @retval TRUE The TimeStamp Token Information is valid.
308 @retval FALSE Invalid TimeStamp Token Information.
314 IN CONST TS_TST_INFO
*TstInfo
,
315 IN CONST UINT8
*TimestampedData
,
320 TS_MESSAGE_IMPRINT
*Imprint
;
321 X509_ALGOR
*HashAlgo
;
335 // -- Check version number of Timestamp:
336 // The version field (currently v1) describes the version of the time-stamp token.
337 // Conforming time-stamping servers MUST be able to provide version 1 time-stamp tokens.
339 if ((ASN1_INTEGER_get (TstInfo
->Version
)) != 1) {
345 // The policy field MUST indicate the TSA's policy under which the response was produced.
347 if (TstInfo
->Policy
== NULL
) {
348 /// NOTE: Need to check if the requested and returned policies.
349 /// We have no information about the Requested TSA Policy.
354 // -- Compute & Check Message Imprint
356 Imprint
= TstInfo
->MessageImprint
;
357 HashAlgo
= X509_ALGOR_dup (Imprint
->HashAlgorithm
);
359 Md
= EVP_get_digestbyobj (HashAlgo
->algorithm
);
364 MdSize
= EVP_MD_size (Md
);
365 HashedMsg
= AllocateZeroPool (MdSize
);
366 if (HashedMsg
== NULL
) {
369 EVP_DigestInit (&MdCtx
, Md
);
370 EVP_DigestUpdate (&MdCtx
, TimestampedData
, DataSize
);
371 EVP_DigestFinal (&MdCtx
, HashedMsg
, NULL
);
372 if ((MdSize
== (UINTN
)ASN1_STRING_length (Imprint
->HashedMessage
)) &&
373 (CompareMem (HashedMsg
, ASN1_STRING_data (Imprint
->HashedMessage
), MdSize
) != 0)) {
380 if (TstInfo
->Nonce
!= NULL
) {
382 // Nonces is optional, No error if no nonce is returned;
387 // -- Check if the TSA name and signer certificate is matched.
389 if (TstInfo
->Tsa
!= NULL
) {
391 // Ignored the optional Tsa field checking.
398 X509_ALGOR_free (HashAlgo
);
399 if (HashedMsg
!= NULL
) {
400 FreePool (HashedMsg
);
407 Verifies the validility of a TimeStamp Token as described in RFC 3161 ("Internet
408 X.509 Public Key Infrastructure Time-Stamp Protocol (TSP)").
410 If TSToken is NULL, then return FALSE.
411 If TimestampedData is NULL, then return FALSE.
413 @param[in] TSToken Pointer to the RFC3161 TimeStamp Token, which is generated
414 by a TSA and located in the software publisher's SignerInfo
416 @param[in] TokenSize Size of the TimeStamp Token in bytes.
417 @param[in] TsaCert Pointer to a trusted/root TSA certificate encoded in DER.
418 @param[in] CertSize Size of the trusted TSA certificate in bytes.
419 @param[in] TimestampedData Pointer to the data to be time-stamped.
420 @param[in] DataSize Size of timestamped data in bytes.
421 @param[out] SigningTime Return the time of timestamp generation time if the timestamp
424 @retval TRUE The specified timestamp token is valid.
425 @retval FALSE Invalid timestamp token.
430 TimestampTokenVerify (
431 IN CONST UINT8
*TSToken
,
433 IN CONST UINT8
*TsaCert
,
435 IN CONST UINT8
*TimestampedData
,
437 OUT EFI_TIME
*SigningTime
441 CONST UINT8
*TokenTemp
;
444 CONST UINT8
*CertTemp
;
445 X509_STORE
*CertStore
;
449 CONST UINT8
*TstTemp
;
450 TS_TST_INFO
*TstInfo
;
455 // Check input parameters
457 if ((TSToken
== NULL
) || (TsaCert
== NULL
) || (TimestampedData
== NULL
) ||
458 (TokenSize
> INT_MAX
) || (CertSize
> INT_MAX
) || (DataSize
> INT_MAX
)) {
465 if (SigningTime
!= NULL
) {
466 SetMem (SigningTime
, sizeof (EFI_TIME
), 0);
476 // TimeStamp Token should contain one valid DER-encoded ASN.1 PKCS#7 structure.
479 Pkcs7
= d2i_PKCS7 (NULL
, (const unsigned char **) &TokenTemp
, (int) TokenSize
);
485 // The timestamp signature (TSA's response) will be one PKCS#7 signed data.
487 if (!PKCS7_type_is_signed (Pkcs7
)) {
492 // Read the trusted TSA certificate (DER-encoded), and Construct X509 Certificate.
495 Cert
= d2i_X509 (NULL
, &CertTemp
, (long) CertSize
);
501 // Setup X509 Store for trusted certificate.
503 CertStore
= X509_STORE_new ();
504 if ((CertStore
== NULL
) || !(X509_STORE_add_cert (CertStore
, Cert
))) {
509 // Register customized X509 verification callback function to support
510 // trusted intermediate TSA certificate anchor.
512 CertStore
->verify_cb
= TSVerifyCallback
;
514 X509_STORE_set_purpose (CertStore
, X509_PURPOSE_ANY
);
517 // Verifies the PKCS#7 signedData structure, and output the signed contents.
519 OutBio
= BIO_new (BIO_s_mem ());
520 if (OutBio
== NULL
) {
523 if (!PKCS7_verify (Pkcs7
, NULL
, CertStore
, NULL
, OutBio
, PKCS7_BINARY
)) {
528 // Read the signed contents detached in timestamp signature.
530 TstData
= AllocateZeroPool (2048);
531 if (TstData
== NULL
) {
534 TstSize
= BIO_read (OutBio
, (void *) TstData
, 2048);
537 // Construct TS_TST_INFO structure from the signed contents.
540 TstInfo
= d2i_TS_TST_INFO (NULL
, (const unsigned char **) &TstTemp
,
542 if (TstInfo
== NULL
) {
547 // Check TS_TST_INFO structure.
549 Status
= CheckTSTInfo (TstInfo
, TimestampedData
, DataSize
);
555 // Retrieve the signing time from TS_TST_INFO structure.
557 if (SigningTime
!= NULL
) {
558 SetMem (SigningTime
, sizeof (EFI_TIME
), 0);
559 Status
= ConvertAsn1TimeToEfiTime (TstInfo
->GenTime
, SigningTime
);
568 X509_STORE_free (CertStore
);
570 TS_TST_INFO_free (TstInfo
);
572 if (TstData
!= NULL
) {
580 Verifies the validility of a RFC3161 Timestamp CounterSignature embedded in PE/COFF Authenticode
583 If AuthData is NULL, then return FALSE.
585 @param[in] AuthData Pointer to the Authenticode Signature retrieved from signed
586 PE/COFF image to be verified.
587 @param[in] DataSize Size of the Authenticode Signature in bytes.
588 @param[in] TsaCert Pointer to a trusted/root TSA certificate encoded in DER, which
589 is used for TSA certificate chain verification.
590 @param[in] CertSize Size of the trusted certificate in bytes.
591 @param[out] SigningTime Return the time of timestamp generation time if the timestamp
594 @retval TRUE The specified Authenticode includes a valid RFC3161 Timestamp CounterSignature.
595 @retval FALSE No valid RFC3161 Timestamp CounterSignature in the specified Authenticode data.
600 ImageTimestampVerify (
601 IN CONST UINT8
*AuthData
,
603 IN CONST UINT8
*TsaCert
,
605 OUT EFI_TIME
*SigningTime
611 STACK_OF(PKCS7_SIGNER_INFO
) *SignerInfos
;
612 PKCS7_SIGNER_INFO
*SignInfo
;
614 STACK_OF(X509_ATTRIBUTE
) *Sk
;
617 ASN1_OCTET_STRING
*EncDigest
;
622 // Input Parameters Checking.
624 if ((AuthData
== NULL
) || (TsaCert
== NULL
)) {
628 if ((DataSize
> INT_MAX
) || (CertSize
> INT_MAX
)) {
633 // Register & Initialize necessary digest algorithms for PKCS#7 Handling.
635 if ((EVP_add_digest (EVP_md5 ()) == 0) || (EVP_add_digest (EVP_sha1 ()) == 0) ||
636 (EVP_add_digest (EVP_sha256 ()) == 0) || (EVP_add_digest_alias (SN_sha1WithRSAEncryption
, SN_sha1WithRSA
)) == 0) {
648 // Decode ASN.1-encoded Authenticode data into PKCS7 structure.
651 Pkcs7
= d2i_PKCS7 (NULL
, (const unsigned char **) &Temp
, (int) DataSize
);
657 // Check if there is one and only one signer.
659 SignerInfos
= PKCS7_get_signer_info (Pkcs7
);
660 if (!SignerInfos
|| (sk_PKCS7_SIGNER_INFO_num (SignerInfos
) != 1)) {
665 // Locate the TimeStamp CounterSignature.
667 SignInfo
= sk_PKCS7_SIGNER_INFO_value (SignerInfos
, 0);
668 if (SignInfo
== NULL
) {
673 // Locate Message Digest which will be the data to be time-stamped.
675 EncDigest
= SignInfo
->enc_digest
;
676 if (EncDigest
== NULL
) {
681 // The RFC3161 timestamp counterSignature is contained in unauthenticatedAttributes field
684 Sk
= SignInfo
->unauth_attr
;
685 if (Sk
== NULL
) { // No timestamp counterSignature.
690 for (Index
= 0; Index
< (UINTN
) sk_X509_ATTRIBUTE_num (Sk
); Index
++) {
692 // Search valid RFC3161 timestamp counterSignature based on OBJID.
694 Xa
= sk_X509_ATTRIBUTE_value (Sk
, (int)Index
);
695 if ((Xa
->object
->length
!= sizeof (mSpcRFC3161OidValue
)) ||
696 (CompareMem (Xa
->object
->data
, mSpcRFC3161OidValue
, sizeof (mSpcRFC3161OidValue
)) != 0)) {
699 Asn1Type
= sk_ASN1_TYPE_value (Xa
->value
.set
, 0);
702 if (Asn1Type
== NULL
) {
706 TSToken
= Asn1Type
->value
.octet_string
->data
;
707 TokenSize
= Asn1Type
->value
.octet_string
->length
;
710 // TimeStamp counterSignature (Token) verification.
712 Status
= TimestampTokenVerify (