1/** @file 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 6 revoked. 7 8Copyright (c) 2014 - 2015, Intel Corporation. All rights reserved.<BR> 9This program and the accompanying materials 10are licensed and made available under the terms and conditions of the BSD License 11which accompanies this distribution. The full text of the license may be found at 12http://opensource.org/licenses/bsd-license.php 13 14THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 15WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 16 17**/ 18 19#include "InternalCryptLib.h" 20 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> 26 27// 28// OID ASN.1 Value for SPC_RFC3161_OBJID ("1.3.6.1.4.1.311.3.3.1") 29// 30UINT8 mSpcRFC3161OidValue[] = { 31 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x03, 0x03, 0x01 32 }; 33 34/// 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). 39/// 40/// MessageImprint ::= SEQUENCE { 41/// hashAlgorithm AlgorithmIdentifier, 42/// hashedMessage OCTET STRING } 43/// 44typedef struct { 45 X509_ALGOR *HashAlgorithm; 46 ASN1_OCTET_STRING *HashedMessage; 47} TS_MESSAGE_IMPRINT; 48 49// 50// ASN.1 Functions for TS_MESSAGE_IMPRINT 51// 52DECLARE_ASN1_FUNCTIONS (TS_MESSAGE_IMPRINT) 53ASN1_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) 57IMPLEMENT_ASN1_FUNCTIONS (TS_MESSAGE_IMPRINT) 58 59/// 60/// Accuracy represents the time deviation around the UTC time contained 61/// in GeneralizedTime of time-stamp token. 62/// 63/// Accuracy ::= SEQUENCE { 64/// seconds INTEGER OPTIONAL, 65/// millis [0] INTEGER (1..999) OPTIONAL, 66/// micros [1] INTEGER (1..999) OPTIONAL } 67/// 68typedef struct { 69 ASN1_INTEGER *Seconds; 70 ASN1_INTEGER *Millis; 71 ASN1_INTEGER *Micros; 72} TS_ACCURACY; 73 74// 75// ASN.1 Functions for TS_ACCURACY 76// 77DECLARE_ASN1_FUNCTIONS (TS_ACCURACY) 78ASN1_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) 83IMPLEMENT_ASN1_FUNCTIONS (TS_ACCURACY) 84 85/// 86/// The timestamp token info resulting from a successful timestamp request, 87/// as defined in RFC 3161. 88/// 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 94/// -- TimeStampReq 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 } 106/// 107typedef struct { 108 ASN1_INTEGER *Version; 109 ASN1_OBJECT *Policy; 110 TS_MESSAGE_IMPRINT *MessageImprint; 111 ASN1_INTEGER *SerialNumber; 112 ASN1_GENERALIZEDTIME *GenTime; 113 TS_ACCURACY *Accuracy; 114 ASN1_BOOLEAN Ordering; 115 ASN1_INTEGER *Nonce; 116 GENERAL_NAME *Tsa; 117 STACK_OF(X509_EXTENSION) *Extensions; 118} TS_TST_INFO; 119 120// 121// ASN.1 Functions for TS_TST_INFO 122// 123DECLARE_ASN1_FUNCTIONS (TS_TST_INFO) 124ASN1_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) 136IMPLEMENT_ASN1_FUNCTIONS (TS_TST_INFO) 137 138 139/** 140 Convert ASN.1 GeneralizedTime to EFI Time. 141 142 @param[in] Asn1Time Pointer to the ASN.1 GeneralizedTime to be converted. 143 @param[out] SigningTime Return the corresponding EFI Time. 144 145 @retval TRUE The time convertion succeeds. 146 @retval FALSE Invalid parameters. 147 148**/ 149BOOLEAN 150EFIAPI 151ConvertAsn1TimeToEfiTime ( 152 IN ASN1_TIME *Asn1Time, 153 OUT EFI_TIME *EfiTime 154 ) 155{ 156 CONST CHAR8 *Str; 157 UINTN Index; 158 159 if ((Asn1Time == NULL) || (EfiTime == NULL)) { 160 return FALSE; 161 } 162 163 Str = (CONST CHAR8*)Asn1Time->data; 164 SetMem (EfiTime, 0, sizeof (EFI_TIME)); 165 166 Index = 0; 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; 172 } 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)) { 179 return FALSE; 180 } 181 } 182 183 EfiTime->Month = (Str[Index++] - '0') * 10; 184 EfiTime->Month += (Str[Index++] - '0'); 185 if ((EfiTime->Month < 1) || (EfiTime->Month > 12)) { 186 return FALSE; 187 } 188 189 EfiTime->Day = (Str[Index++] - '0') * 10; 190 EfiTime->Day += (Str[Index++] - '0'); 191 if ((EfiTime->Day < 1) || (EfiTime->Day > 31)) { 192 return FALSE; 193 } 194 195 EfiTime->Hour = (Str[Index++] - '0') * 10; 196 EfiTime->Hour += (Str[Index++] - '0'); 197 if (EfiTime->Hour > 23) { 198 return FALSE; 199 } 200 201 EfiTime->Minute = (Str[Index++] - '0') * 10; 202 EfiTime->Minute += (Str[Index++] - '0'); 203 if (EfiTime->Minute > 59) { 204 return FALSE; 205 } 206 207 EfiTime->Second = (Str[Index++] - '0') * 10; 208 EfiTime->Second += (Str[Index++] - '0'); 209 if (EfiTime->Second > 59) { 210 return FALSE; 211 } 212 213 /* Note: we did not adjust the time based on time zone information */ 214 215 return TRUE; 216} 217 218/** 219 220 Check the validity of TimeStamp Token Information. 221 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. 225 226 @retval TRUE The TimeStamp Token Information is valid. 227 @retval FALSE Invalid TimeStamp Token Information. 228 229**/ 230BOOLEAN 231EFIAPI 232CheckTSTInfo ( 233 IN CONST TS_TST_INFO *TstInfo, 234 IN CONST UINT8 *TimestampedData, 235 IN UINTN DataSize 236 ) 237{ 238 BOOLEAN Status; 239 TS_MESSAGE_IMPRINT *Imprint; 240 X509_ALGOR *HashAlgo; 241 CONST EVP_MD *Md; 242 EVP_MD_CTX MdCtx; 243 UINTN MdSize; 244 UINT8 *HashedMsg; 245 246 // 247 // Initialization 248 // 249 Status = FALSE; 250 HashAlgo = NULL; 251 HashedMsg = NULL; 252 253 // 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. 257 // 258 if ((ASN1_INTEGER_get (TstInfo->Version)) != 1) { 259 return FALSE; 260 } 261 262 // 263 // -- Check Policies 264 // The policy field MUST indicate the TSA's policy under which the response was produced. 265 // 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. 269 return FALSE; 270 } 271 272 // 273 // -- Compute & Check Message Imprint 274 // 275 Imprint = TstInfo->MessageImprint; 276 HashAlgo = X509_ALGOR_dup (Imprint->HashAlgorithm); 277 278 Md = EVP_get_digestbyobj (HashAlgo->algorithm); 279 if (Md == NULL) { 280 goto _Exit; 281 } 282 283 MdSize = EVP_MD_size (Md); 284 HashedMsg = AllocateZeroPool (MdSize); 285 if (HashedMsg == NULL) { 286 goto _Exit; 287 } 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)) { 293 goto _Exit; 294 } 295 296 // 297 // -- Check Nonces 298 // 299 if (TstInfo->Nonce != NULL) { 300 // 301 // Nonces is optional, No error if no nonce is returned; 302 // 303 } 304 305 // 306 // -- Check if the TSA name and signer certificate is matched. 307 // 308 if (TstInfo->Tsa != NULL) { 309 // 310 // Ignored the optional Tsa field checking. 311 // 312 } 313 314 Status = TRUE; 315 316_Exit: 317 X509_ALGOR_free (HashAlgo); 318 if (HashedMsg != NULL) { 319 FreePool (HashedMsg); 320 } 321 322 return Status; 323} 324 325/** 326 Verifies the validility of a TimeStamp Token as described in RFC 3161 ("Internet 327 X.509 Public Key Infrastructure Time-Stamp Protocol (TSP)"). 328 329 If TSToken is NULL, then return FALSE. 330 If TimestampedData is NULL, then return FALSE. 331 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 334 structure. 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 341 signature is valid. 342 343 @retval TRUE The specified timestamp token is valid. 344 @retval FALSE Invalid timestamp token. 345 346**/ 347BOOLEAN 348EFIAPI 349TimestampTokenVerify ( 350 IN CONST UINT8 *TSToken, 351 IN UINTN TokenSize, 352 IN CONST UINT8 *TsaCert, 353 IN UINTN CertSize, 354 IN CONST UINT8 *TimestampedData, 355 IN UINTN DataSize, 356 OUT EFI_TIME *SigningTime 357 ) 358{ 359 BOOLEAN Status; 360 CONST UINT8 *TokenTemp; 361 PKCS7 *Pkcs7; 362 X509 *Cert; 363 CONST UINT8 *CertTemp; 364 X509_STORE *CertStore; 365 BIO *OutBio; 366 UINT8 *TstData; 367 UINTN TstSize; 368 CONST UINT8 *TstTemp; 369 TS_TST_INFO *TstInfo; 370 371 Status = FALSE; 372 373 // 374 // Check input parameters 375 // 376 if ((TSToken == NULL) || (TsaCert == NULL) || (TimestampedData == NULL) || 377 (TokenSize > INT_MAX) || (CertSize > INT_MAX) || (DataSize > INT_MAX)) { 378 return FALSE; 379 } 380 381 // 382 // Initializations 383 // 384 if (SigningTime != NULL) { 385 SetMem (SigningTime, sizeof (EFI_TIME), 0); 386 } 387 Pkcs7 = NULL; 388 Cert = NULL; 389 CertStore = NULL; 390 OutBio = NULL; 391 TstData = NULL; 392 TstInfo = NULL; 393 394 // 395 // TimeStamp Token should contain one valid DER-encoded ASN.1 PKCS#7 structure. 396 // 397 TokenTemp = TSToken; 398 Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **) &TokenTemp, (int) TokenSize); 399 if (Pkcs7 == NULL) { 400 goto _Exit; 401 } 402 403 // 404 // The timestamp signature (TSA's response) will be one PKCS#7 signed data. 405 // 406 if (!PKCS7_type_is_signed (Pkcs7)) { 407 goto _Exit; 408 } 409 410 // 411 // Read the trusted TSA certificate (DER-encoded), and Construct X509 Certificate. 412 // 413 CertTemp = TsaCert; 414 Cert = d2i_X509 (NULL, &CertTemp, (long) CertSize); 415 if (Cert == NULL) { 416 goto _Exit; 417 } 418 419 // 420 // Setup X509 Store for trusted certificate. 421 // 422 CertStore = X509_STORE_new (); 423 if ((CertStore == NULL) || !(X509_STORE_add_cert (CertStore, Cert))) { 424 goto _Exit; 425 } 426 427 // 428 // Allow partial certificate chains, terminated by a non-self-signed but 429 // still trusted intermediate certificate. Also disable time checks. 430 // 431 X509_STORE_set_flags (CertStore, 432 X509_V_FLAG_PARTIAL_CHAIN | X509_V_FLAG_NO_CHECK_TIME); 433 434 X509_STORE_set_purpose (CertStore, X509_PURPOSE_ANY); 435 436 // 437 // Verifies the PKCS#7 signedData structure, and output the signed contents. 438 // 439 OutBio = BIO_new (BIO_s_mem ()); 440 if (OutBio == NULL) { 441 goto _Exit; 442 } 443 if (!PKCS7_verify (Pkcs7, NULL, CertStore, NULL, OutBio, PKCS7_BINARY)) { 444 goto _Exit; 445 } 446 447 // 448 // Read the signed contents detached in timestamp signature. 449 // 450 TstData = AllocateZeroPool (2048); 451 if (TstData == NULL) { 452 goto _Exit; 453 } 454 TstSize = BIO_read (OutBio, (void *) TstData, 2048); 455 456 // 457 // Construct TS_TST_INFO structure from the signed contents. 458 // 459 TstTemp = TstData; 460 TstInfo = d2i_TS_TST_INFO (NULL, (const unsigned char **) &TstTemp, 461 (int)TstSize); 462 if (TstInfo == NULL) { 463 goto _Exit; 464 } 465 466 // 467 // Check TS_TST_INFO structure. 468 // 469 Status = CheckTSTInfo (TstInfo, TimestampedData, DataSize); 470 if (!Status) { 471 goto _Exit; 472 } 473 474 // 475 // Retrieve the signing time from TS_TST_INFO structure. 476 // 477 if (SigningTime != NULL) { 478 SetMem (SigningTime, sizeof (EFI_TIME), 0); 479 Status = ConvertAsn1TimeToEfiTime (TstInfo->GenTime, SigningTime); 480 } 481 482_Exit: 483 // 484 // Release Resources 485 // 486 PKCS7_free (Pkcs7); 487 X509_free (Cert); 488 X509_STORE_free (CertStore); 489 BIO_free (OutBio); 490 TS_TST_INFO_free (TstInfo); 491 492 if (TstData != NULL) { 493 FreePool (TstData); 494 } 495 496 return Status; 497} 498 499/** 500 Verifies the validility of a RFC3161 Timestamp CounterSignature embedded in PE/COFF Authenticode 501 signature. 502 503 If AuthData is NULL, then return FALSE. 504 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 512 signature is valid. 513 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. 516 517**/ 518BOOLEAN 519EFIAPI 520ImageTimestampVerify ( 521 IN CONST UINT8 *AuthData, 522 IN UINTN DataSize, 523 IN CONST UINT8 *TsaCert, 524 IN UINTN CertSize, 525 OUT EFI_TIME *SigningTime 526 ) 527{ 528 BOOLEAN Status; 529 PKCS7 *Pkcs7; 530 CONST UINT8 *Temp; 531 STACK_OF(PKCS7_SIGNER_INFO) *SignerInfos; 532 PKCS7_SIGNER_INFO *SignInfo; 533 UINTN Index; 534 STACK_OF(X509_ATTRIBUTE) *Sk; 535 X509_ATTRIBUTE *Xa; 536 ASN1_OBJECT *XaObj; 537 ASN1_TYPE *Asn1Type; 538 ASN1_OCTET_STRING *EncDigest; 539 UINT8 *TSToken; 540 UINTN TokenSize; 541 542 // 543 // Input Parameters Checking. 544 // 545 if ((AuthData == NULL) || (TsaCert == NULL)) { 546 return FALSE; 547 } 548 549 if ((DataSize > INT_MAX) || (CertSize > INT_MAX)) { 550 return FALSE; 551 } 552 553 // 554 // Register & Initialize necessary digest algorithms for PKCS#7 Handling. 555 // 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) { 558 return FALSE; 559 } 560 561 // 562 // Initialization. 563 // 564 Status = FALSE; 565 Pkcs7 = NULL; 566 SignInfo = NULL; 567 568 // 569 // Decode ASN.1-encoded Authenticode data into PKCS7 structure. 570 // 571 Temp = AuthData; 572 Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **) &Temp, (int) DataSize); 573 if (Pkcs7 == NULL) { 574 goto _Exit; 575 } 576 577 // 578 // Check if there is one and only one signer. 579 // 580 SignerInfos = PKCS7_get_signer_info (Pkcs7); 581 if (!SignerInfos || (sk_PKCS7_SIGNER_INFO_num (SignerInfos) != 1)) { 582 goto _Exit; 583 } 584 585 // 586 // Locate the TimeStamp CounterSignature. 587 // 588 SignInfo = sk_PKCS7_SIGNER_INFO_value (SignerInfos, 0); 589 if (SignInfo == NULL) { 590 goto _Exit; 591 } 592 593 // 594 // Locate Message Digest which will be the data to be time-stamped. 595 // 596 EncDigest = SignInfo->enc_digest; 597 if (EncDigest == NULL) { 598 goto _Exit; 599 } 600 601 // 602 // The RFC3161 timestamp counterSignature is contained in unauthenticatedAttributes field 603 // of SignerInfo. 604 // 605 Sk = SignInfo->unauth_attr; 606 if (Sk == NULL) { // No timestamp counterSignature. 607 goto _Exit; 608 } 609 610 Asn1Type = NULL; 611 for (Index = 0; Index < (UINTN) sk_X509_ATTRIBUTE_num (Sk); Index++) { 612 // 613 // Search valid RFC3161 timestamp counterSignature based on OBJID. 614 // 615 Xa = sk_X509_ATTRIBUTE_value (Sk, (int)Index); 616 if (Xa == NULL) { 617 continue; 618 } 619 XaObj = X509_ATTRIBUTE_get0_object(Xa); 620 if (XaObj == NULL) { 621 continue; 622 } 623 if ((OBJ_length(XaObj) != sizeof (mSpcRFC3161OidValue)) || 624 (CompareMem (OBJ_get0_data(XaObj), mSpcRFC3161OidValue, sizeof (mSpcRFC3161OidValue)) != 0)) { 625 continue; 626 } 627 Asn1Type = X509_ATTRIBUTE_get0_type(Xa, 0); 628 } 629 630 if (Asn1Type == NULL) { 631 Status = FALSE; 632 goto _Exit; 633 } 634 TSToken = Asn1Type->value.octet_string->data; 635 TokenSize = Asn1Type->value.octet_string->length; 636 637 // 638 // TimeStamp counterSignature (Token) verification. 639 // 640 Status = TimestampTokenVerify ( 641 TSToken, 642 TokenSize, 643 TsaCert, 644 CertSize, 645 EncDigest->data, 646 EncDigest->length, 647 SigningTime 648 ); 649 650_Exit: 651 // 652 // Release Resources 653 // 654 PKCS7_free (Pkcs7); 655 656 return Status; 657} 658