1/** @file
2  Authenticode Portable Executable Signature Verification over OpenSSL.
3
4  Caution: This module requires additional review when modified.
5  This library will have external input - signature (e.g. PE/COFF Authenticode).
6  This external input must be validated carefully to avoid security issue like
7  buffer overflow, integer overflow.
8
9  AuthenticodeVerify() will get PE/COFF Authenticode and will do basic check for
10  data structure.
11
12Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR>
13This program and the accompanying materials
14are licensed and made available under the terms and conditions of the BSD License
15which accompanies this distribution.  The full text of the license may be found at
16http://opensource.org/licenses/bsd-license.php
17
18THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
19WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
20
21**/
22
23#include "InternalCryptLib.h"
24
25#include <openssl/objects.h>
26#include <openssl/x509.h>
27#include <openssl/pkcs7.h>
28
29//
30// OID ASN.1 Value for SPC_INDIRECT_DATA_OBJID
31//
32UINT8 mSpcIndirectOidValue[] = {
33  0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x01, 0x04
34  };
35
36/**
37  Verifies the validility of a PE/COFF Authenticode Signature as described in "Windows
38  Authenticode Portable Executable Signature Format".
39
40  If AuthData is NULL, then return FALSE.
41  If ImageHash is NULL, then return FALSE.
42
43  Caution: This function may receive untrusted input.
44  PE/COFF Authenticode is external input, so this function will do basic check for
45  Authenticode data structure.
46
47  @param[in]  AuthData     Pointer to the Authenticode Signature retrieved from signed
48                           PE/COFF image to be verified.
49  @param[in]  DataSize     Size of the Authenticode Signature in bytes.
50  @param[in]  TrustedCert  Pointer to a trusted/root certificate encoded in DER, which
51                           is used for certificate chain verification.
52  @param[in]  CertSize     Size of the trusted certificate in bytes.
53  @param[in]  ImageHash    Pointer to the original image file hash value. The procudure
54                           for calculating the image hash value is described in Authenticode
55                           specification.
56  @param[in]  HashSize     Size of Image hash value in bytes.
57
58  @retval  TRUE   The specified Authenticode Signature is valid.
59  @retval  FALSE  Invalid Authenticode Signature.
60
61**/
62BOOLEAN
63EFIAPI
64AuthenticodeVerify (
65  IN  CONST UINT8  *AuthData,
66  IN  UINTN        DataSize,
67  IN  CONST UINT8  *TrustedCert,
68  IN  UINTN        CertSize,
69  IN  CONST UINT8  *ImageHash,
70  IN  UINTN        HashSize
71  )
72{
73  BOOLEAN      Status;
74  PKCS7        *Pkcs7;
75  CONST UINT8  *Temp;
76  CONST UINT8  *OrigAuthData;
77  UINT8        *SpcIndirectDataContent;
78  UINT8        Asn1Byte;
79  UINTN        ContentSize;
80  CONST UINT8  *SpcIndirectDataOid;
81
82  //
83  // Check input parameters.
84  //
85  if ((AuthData == NULL) || (TrustedCert == NULL) || (ImageHash == NULL)) {
86    return FALSE;
87  }
88
89  if ((DataSize > INT_MAX) || (CertSize > INT_MAX) || (HashSize > INT_MAX)) {
90    return FALSE;
91  }
92
93  Status       = FALSE;
94  Pkcs7        = NULL;
95  OrigAuthData = AuthData;
96
97  //
98  // Retrieve & Parse PKCS#7 Data (DER encoding) from Authenticode Signature
99  //
100  Temp  = AuthData;
101  Pkcs7 = d2i_PKCS7 (NULL, &Temp, (int)DataSize);
102  if (Pkcs7 == NULL) {
103    goto _Exit;
104  }
105
106  //
107  // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)
108  //
109  if (!PKCS7_type_is_signed (Pkcs7)) {
110    goto _Exit;
111  }
112
113  //
114  // NOTE: OpenSSL PKCS7 Decoder didn't work for Authenticode-format signed data due to
115  //       some authenticode-specific structure. Use opaque ASN.1 string to retrieve
116  //       PKCS#7 ContentInfo here.
117  //
118  SpcIndirectDataOid = OBJ_get0_data(Pkcs7->d.sign->contents->type);
119  if (OBJ_length(Pkcs7->d.sign->contents->type) != sizeof(mSpcIndirectOidValue) ||
120      CompareMem (
121        SpcIndirectDataOid,
122        mSpcIndirectOidValue,
123        sizeof (mSpcIndirectOidValue)
124        ) != 0) {
125    //
126    // Un-matched SPC_INDIRECT_DATA_OBJID.
127    //
128    goto _Exit;
129  }
130
131
132  SpcIndirectDataContent = (UINT8 *)(Pkcs7->d.sign->contents->d.other->value.asn1_string->data);
133
134  //
135  // Retrieve the SEQUENCE data size from ASN.1-encoded SpcIndirectDataContent.
136  //
137  Asn1Byte = *(SpcIndirectDataContent + 1);
138
139  if ((Asn1Byte & 0x80) == 0) {
140    //
141    // Short Form of Length Encoding (Length < 128)
142    //
143    ContentSize = (UINTN) (Asn1Byte & 0x7F);
144    //
145    // Skip the SEQUENCE Tag;
146    //
147    SpcIndirectDataContent += 2;
148
149  } else if ((Asn1Byte & 0x81) == 0x81) {
150    //
151    // Long Form of Length Encoding (128 <= Length < 255, Single Octet)
152    //
153    ContentSize = (UINTN) (*(UINT8 *)(SpcIndirectDataContent + 2));
154    //
155    // Skip the SEQUENCE Tag;
156    //
157    SpcIndirectDataContent += 3;
158
159  } else if ((Asn1Byte & 0x82) == 0x82) {
160    //
161    // Long Form of Length Encoding (Length > 255, Two Octet)
162    //
163    ContentSize = (UINTN) (*(UINT8 *)(SpcIndirectDataContent + 2));
164    ContentSize = (ContentSize << 8) + (UINTN)(*(UINT8 *)(SpcIndirectDataContent + 3));
165    //
166    // Skip the SEQUENCE Tag;
167    //
168    SpcIndirectDataContent += 4;
169
170  } else {
171    goto _Exit;
172  }
173
174  //
175  // Compare the original file hash value to the digest retrieve from SpcIndirectDataContent
176  // defined in Authenticode
177  // NOTE: Need to double-check HashLength here!
178  //
179  if (CompareMem (SpcIndirectDataContent + ContentSize - HashSize, ImageHash, HashSize) != 0) {
180    //
181    // Un-matched PE/COFF Hash Value
182    //
183    goto _Exit;
184  }
185
186  //
187  // Verifies the PKCS#7 Signed Data in PE/COFF Authenticode Signature
188  //
189  Status = (BOOLEAN) Pkcs7Verify (OrigAuthData, DataSize, TrustedCert, CertSize, SpcIndirectDataContent, ContentSize);
190
191_Exit:
192  //
193  // Release Resources
194  //
195  PKCS7_free (Pkcs7);
196
197  return Status;
198}
199