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 Convert ASN.1 GeneralizedTime to EFI Time.
142 @param[in] Asn1Time Pointer to the ASN.1 GeneralizedTime to be converted.
143 @param[out] SigningTime Return the corresponding EFI Time.
145 @retval TRUE The time convertion succeeds.
146 @retval FALSE Invalid parameters.
151 ConvertAsn1TimeToEfiTime (
152 IN ASN1_TIME
*Asn1Time
,
153 OUT EFI_TIME
*EfiTime
159 if ((Asn1Time
== NULL
) || (EfiTime
== NULL
)) {
163 Str
= (CONST CHAR8
*)Asn1Time
->data
;
164 SetMem (EfiTime
, 0, sizeof (EFI_TIME
));
167 if (Asn1Time
->type
== V_ASN1_UTCTIME
) { /* two digit year */
168 EfiTime
->Year
= (Str
[Index
++] - '0') * 10;
169 EfiTime
->Year
+= (Str
[Index
++] - '0');
170 if (EfiTime
->Year
< 70) {
171 EfiTime
->Year
+= 100;
173 } else if (Asn1Time
->type
== V_ASN1_GENERALIZEDTIME
) { /* four digit year */
174 EfiTime
->Year
= (Str
[Index
++] - '0') * 1000;
175 EfiTime
->Year
+= (Str
[Index
++] - '0') * 100;
176 EfiTime
->Year
+= (Str
[Index
++] - '0') * 10;
177 EfiTime
->Year
+= (Str
[Index
++] - '0');
178 if ((EfiTime
->Year
< 1900) || (EfiTime
->Year
> 9999)) {
183 EfiTime
->Month
= (Str
[Index
++] - '0') * 10;
184 EfiTime
->Month
+= (Str
[Index
++] - '0');
185 if ((EfiTime
->Month
< 1) || (EfiTime
->Month
> 12)) {
189 EfiTime
->Day
= (Str
[Index
++] - '0') * 10;
190 EfiTime
->Day
+= (Str
[Index
++] - '0');
191 if ((EfiTime
->Day
< 1) || (EfiTime
->Day
> 31)) {
195 EfiTime
->Hour
= (Str
[Index
++] - '0') * 10;
196 EfiTime
->Hour
+= (Str
[Index
++] - '0');
197 if (EfiTime
->Hour
> 23) {
201 EfiTime
->Minute
= (Str
[Index
++] - '0') * 10;
202 EfiTime
->Minute
+= (Str
[Index
++] - '0');
203 if (EfiTime
->Minute
> 59) {
207 EfiTime
->Second
= (Str
[Index
++] - '0') * 10;
208 EfiTime
->Second
+= (Str
[Index
++] - '0');
209 if (EfiTime
->Second
> 59) {
213 /* Note: we did not adjust the time based on time zone information */
220 Check the validity of TimeStamp Token Information.
222 @param[in] TstInfo Pointer to the TS_TST_INFO structure.
223 @param[in] TimestampedData Pointer to the data to be time-stamped.
224 @param[in] DataSize Size of timestamped data in bytes.
226 @retval TRUE The TimeStamp Token Information is valid.
227 @retval FALSE Invalid TimeStamp Token Information.
233 IN CONST TS_TST_INFO
*TstInfo
,
234 IN CONST UINT8
*TimestampedData
,
239 TS_MESSAGE_IMPRINT
*Imprint
;
240 X509_ALGOR
*HashAlgo
;
254 // -- Check version number of Timestamp:
255 // The version field (currently v1) describes the version of the time-stamp token.
256 // Conforming time-stamping servers MUST be able to provide version 1 time-stamp tokens.
258 if ((ASN1_INTEGER_get (TstInfo
->Version
)) != 1) {
264 // The policy field MUST indicate the TSA's policy under which the response was produced.
266 if (TstInfo
->Policy
== NULL
) {
267 /// NOTE: Need to check if the requested and returned policies.
268 /// We have no information about the Requested TSA Policy.
273 // -- Compute & Check Message Imprint
275 Imprint
= TstInfo
->MessageImprint
;
276 HashAlgo
= X509_ALGOR_dup (Imprint
->HashAlgorithm
);
278 Md
= EVP_get_digestbyobj (HashAlgo
->algorithm
);
283 MdSize
= EVP_MD_size (Md
);
284 HashedMsg
= AllocateZeroPool (MdSize
);
285 if (HashedMsg
== NULL
) {
288 EVP_DigestInit (&MdCtx
, Md
);
289 EVP_DigestUpdate (&MdCtx
, TimestampedData
, DataSize
);
290 EVP_DigestFinal (&MdCtx
, HashedMsg
, NULL
);
291 if ((MdSize
== (UINTN
)ASN1_STRING_length (Imprint
->HashedMessage
)) &&
292 (CompareMem (HashedMsg
, ASN1_STRING_data (Imprint
->HashedMessage
), MdSize
) != 0)) {
299 if (TstInfo
->Nonce
!= NULL
) {
301 // Nonces is optional, No error if no nonce is returned;
306 // -- Check if the TSA name and signer certificate is matched.
308 if (TstInfo
->Tsa
!= NULL
) {
310 // Ignored the optional Tsa field checking.
317 X509_ALGOR_free (HashAlgo
);
318 if (HashedMsg
!= NULL
) {
319 FreePool (HashedMsg
);
326 Verifies the validility of a TimeStamp Token as described in RFC 3161 ("Internet
327 X.509 Public Key Infrastructure Time-Stamp Protocol (TSP)").
329 If TSToken is NULL, then return FALSE.
330 If TimestampedData is NULL, then return FALSE.
332 @param[in] TSToken Pointer to the RFC3161 TimeStamp Token, which is generated
333 by a TSA and located in the software publisher's SignerInfo
335 @param[in] TokenSize Size of the TimeStamp Token in bytes.
336 @param[in] TsaCert Pointer to a trusted/root TSA certificate encoded in DER.
337 @param[in] CertSize Size of the trusted TSA certificate in bytes.
338 @param[in] TimestampedData Pointer to the data to be time-stamped.
339 @param[in] DataSize Size of timestamped data in bytes.
340 @param[out] SigningTime Return the time of timestamp generation time if the timestamp
343 @retval TRUE The specified timestamp token is valid.
344 @retval FALSE Invalid timestamp token.
349 TimestampTokenVerify (
350 IN CONST UINT8
*TSToken
,
352 IN CONST UINT8
*TsaCert
,
354 IN CONST UINT8
*TimestampedData
,
356 OUT EFI_TIME
*SigningTime
360 CONST UINT8
*TokenTemp
;
363 CONST UINT8
*CertTemp
;
364 X509_STORE
*CertStore
;
368 CONST UINT8
*TstTemp
;
369 TS_TST_INFO
*TstInfo
;
374 // Check input parameters
376 if ((TSToken
== NULL
) || (TsaCert
== NULL
) || (TimestampedData
== NULL
) ||
377 (TokenSize
> INT_MAX
) || (CertSize
> INT_MAX
) || (DataSize
> INT_MAX
)) {
384 if (SigningTime
!= NULL
) {
385 SetMem (SigningTime
, sizeof (EFI_TIME
), 0);
395 // TimeStamp Token should contain one valid DER-encoded ASN.1 PKCS#7 structure.
398 Pkcs7
= d2i_PKCS7 (NULL
, (const unsigned char **) &TokenTemp
, (int) TokenSize
);
404 // The timestamp signature (TSA's response) will be one PKCS#7 signed data.
406 if (!PKCS7_type_is_signed (Pkcs7
)) {
411 // Read the trusted TSA certificate (DER-encoded), and Construct X509 Certificate.
414 Cert
= d2i_X509 (NULL
, &CertTemp
, (long) CertSize
);
420 // Setup X509 Store for trusted certificate.
422 CertStore
= X509_STORE_new ();
423 if ((CertStore
== NULL
) || !(X509_STORE_add_cert (CertStore
, Cert
))) {
428 // Allow partial certificate chains, terminated by a non-self-signed but
429 // still trusted intermediate certificate. Also disable time checks.
431 X509_STORE_set_flags (CertStore
,
432 X509_V_FLAG_PARTIAL_CHAIN
| X509_V_FLAG_NO_CHECK_TIME
);
434 X509_STORE_set_purpose (CertStore
, X509_PURPOSE_ANY
);
437 // Verifies the PKCS#7 signedData structure, and output the signed contents.
439 OutBio
= BIO_new (BIO_s_mem ());
440 if (OutBio
== NULL
) {
443 if (!PKCS7_verify (Pkcs7
, NULL
, CertStore
, NULL
, OutBio
, PKCS7_BINARY
)) {
448 // Read the signed contents detached in timestamp signature.
450 TstData
= AllocateZeroPool (2048);
451 if (TstData
== NULL
) {
454 TstSize
= BIO_read (OutBio
, (void *) TstData
, 2048);
457 // Construct TS_TST_INFO structure from the signed contents.
460 TstInfo
= d2i_TS_TST_INFO (NULL
, (const unsigned char **) &TstTemp
,
462 if (TstInfo
== NULL
) {
467 // Check TS_TST_INFO structure.
469 Status
= CheckTSTInfo (TstInfo
, TimestampedData
, DataSize
);
475 // Retrieve the signing time from TS_TST_INFO structure.
477 if (SigningTime
!= NULL
) {
478 SetMem (SigningTime
, sizeof (EFI_TIME
), 0);
479 Status
= ConvertAsn1TimeToEfiTime (TstInfo
->GenTime
, SigningTime
);
488 X509_STORE_free (CertStore
);
490 TS_TST_INFO_free (TstInfo
);
492 if (TstData
!= NULL
) {
500 Verifies the validility of a RFC3161 Timestamp CounterSignature embedded in PE/COFF Authenticode
503 If AuthData is NULL, then return FALSE.
505 @param[in] AuthData Pointer to the Authenticode Signature retrieved from signed
506 PE/COFF image to be verified.
507 @param[in] DataSize Size of the Authenticode Signature in bytes.
508 @param[in] TsaCert Pointer to a trusted/root TSA certificate encoded in DER, which
509 is used for TSA certificate chain verification.
510 @param[in] CertSize Size of the trusted certificate in bytes.
511 @param[out] SigningTime Return the time of timestamp generation time if the timestamp
514 @retval TRUE The specified Authenticode includes a valid RFC3161 Timestamp CounterSignature.
515 @retval FALSE No valid RFC3161 Timestamp CounterSignature in the specified Authenticode data.
520 ImageTimestampVerify (
521 IN CONST UINT8
*AuthData
,
523 IN CONST UINT8
*TsaCert
,
525 OUT EFI_TIME
*SigningTime
531 STACK_OF(PKCS7_SIGNER_INFO
) *SignerInfos
;
532 PKCS7_SIGNER_INFO
*SignInfo
;
534 STACK_OF(X509_ATTRIBUTE
) *Sk
;
538 ASN1_OCTET_STRING
*EncDigest
;
543 // Input Parameters Checking.
545 if ((AuthData
== NULL
) || (TsaCert
== NULL
)) {
549 if ((DataSize
> INT_MAX
) || (CertSize
> INT_MAX
)) {
554 // Register & Initialize necessary digest algorithms for PKCS#7 Handling.
556 if ((EVP_add_digest (EVP_md5 ()) == 0) || (EVP_add_digest (EVP_sha1 ()) == 0) ||
557 (EVP_add_digest (EVP_sha256 ()) == 0) || (EVP_add_digest_alias (SN_sha1WithRSAEncryption
, SN_sha1WithRSA
)) == 0) {
569 // Decode ASN.1-encoded Authenticode data into PKCS7 structure.
572 Pkcs7
= d2i_PKCS7 (NULL
, (const unsigned char **) &Temp
, (int) DataSize
);
578 // Check if there is one and only one signer.
580 SignerInfos
= PKCS7_get_signer_info (Pkcs7
);
581 if (!SignerInfos
|| (sk_PKCS7_SIGNER_INFO_num (SignerInfos
) != 1)) {
586 // Locate the TimeStamp CounterSignature.
588 SignInfo
= sk_PKCS7_SIGNER_INFO_value (SignerInfos
, 0);
589 if (SignInfo
== NULL
) {
594 // Locate Message Digest which will be the data to be time-stamped.
596 EncDigest
= SignInfo
->enc_digest
;
597 if (EncDigest
== NULL
) {
602 // The RFC3161 timestamp counterSignature is contained in unauthenticatedAttributes field
605 Sk
= SignInfo
->unauth_attr
;
606 if (Sk
== NULL
) { // No timestamp counterSignature.
611 for (Index
= 0; Index
< (UINTN
) sk_X509_ATTRIBUTE_num (Sk
); Index
++) {
613 // Search valid RFC3161 timestamp counterSignature based on OBJID.
615 Xa
= sk_X509_ATTRIBUTE_value (Sk
, (int)Index
);
619 XaObj
= X509_ATTRIBUTE_get0_object(Xa
);
623 if ((OBJ_length(XaObj
) != sizeof (mSpcRFC3161OidValue
)) ||
624 (CompareMem (OBJ_get0_data(XaObj
), mSpcRFC3161OidValue
, sizeof (mSpcRFC3161OidValue
)) != 0)) {
627 Asn1Type
= X509_ATTRIBUTE_get0_type(Xa
, 0);
630 if (Asn1Type
== NULL
) {
634 TSToken
= Asn1Type
->value
.octet_string
->data
;
635 TokenSize
= Asn1Type
->value
.octet_string
->length
;
638 // TimeStamp counterSignature (Token) verification.
640 Status
= TimestampTokenVerify (