1 /***************************************************************************
2 * Copyright (c) 2024 Microsoft Corporation
3 *
4 * This program and the accompanying materials are made available under the
5 * terms of the MIT License which is available at
6 * https://opensource.org/licenses/MIT.
7 *
8 * SPDX-License-Identifier: MIT
9 **************************************************************************/
10
11
12 /**************************************************************************/
13 /**************************************************************************/
14 /** */
15 /** NetX Secure Component */
16 /** */
17 /** X.509 Digital Certificates - PKCS#7 parsing */
18 /** */
19 /**************************************************************************/
20 /**************************************************************************/
21
22 #define NX_SECURE_SOURCE_CODE
23
24
25 #include "nx_secure_x509.h"
26
27 /**************************************************************************/
28 /* */
29 /* FUNCTION RELEASE */
30 /* */
31 /* _nx_secure_x509_pkcs7_decode PORTABLE C */
32 /* 6.1.6 */
33 /* AUTHOR */
34 /* */
35 /* Timothy Stapko, Microsoft Corporation */
36 /* */
37 /* DESCRIPTION */
38 /* */
39 /* This function decodes a PKCS#7 (RFC 5652) certificate signature */
40 /* and returns a pointer to the encapsulated hash for signature */
41 /* verification by the caller. Also returned is the OID for the */
42 /* signature algorithm. */
43 /* */
44 /* INPUT */
45 /* */
46 /* signature_pointer Pointer to PCKS#7 signature */
47 /* signature_length Length of entire signagure */
48 /* signature_oid Pointer to signature OID */
49 /* signature_oid_length Return length of OID */
50 /* hash_data Pointer to hash data */
51 /* hash_length Return length of hash */
52 /* */
53 /* OUTPUT */
54 /* */
55 /* status Signature validity status */
56 /* */
57 /* CALLS */
58 /* */
59 /* _nx_secure_x509_asn1_tlv_block_parse Parse ASN.1 block */
60 /* */
61 /* CALLED BY */
62 /* */
63 /* _nx_secure_x509_certificate_verify Verify a certificate */
64 /* _nx_secure_x509_crl_verify Verify revocation list */
65 /* _nx_secure_tls_process_server_key_exchange */
66 /* Process ServerKeyExchange */
67 /* */
68 /* RELEASE HISTORY */
69 /* */
70 /* DATE NAME DESCRIPTION */
71 /* */
72 /* 05-19-2020 Timothy Stapko Initial Version 6.0 */
73 /* 09-30-2020 Timothy Stapko Modified comment(s), */
74 /* resulting in version 6.1 */
75 /* 04-02-2021 Timothy Stapko Modified comment(s), */
76 /* removed dependency on TLS, */
77 /* resulting in version 6.1.6 */
78 /* */
79 /**************************************************************************/
_nx_secure_x509_pkcs7_decode(const UCHAR * signature_pointer,UINT signature_length,const UCHAR ** signature_oid,UINT * signature_oid_length,const UCHAR ** hash_data,UINT * hash_length)80 UINT _nx_secure_x509_pkcs7_decode(const UCHAR *signature_pointer, UINT signature_length,
81 const UCHAR **signature_oid, UINT *signature_oid_length,
82 const UCHAR **hash_data, UINT *hash_length)
83 {
84 UINT i;
85 USHORT tlv_type;
86 USHORT tlv_type_class;
87 ULONG tlv_length;
88 const UCHAR *tlv_data = NX_CRYPTO_NULL;
89 ULONG header_length;
90 ULONG seq_length;
91 UINT status;
92 const UCHAR *signature_data = NX_CRYPTO_NULL;
93 ULONG remaining_length;
94
95 /* Certificate signatures encrypted with RSA use PKCS#7 encoding (RFC 5652) which
96 * has the following format:
97 * 1 byte header (0x00)
98 * Padding type byte (should be < 2)
99 * <Padding>
100 * 1 byte padding terminator (always 0x00)
101 * ASN.1 encoded signature
102 * Signature Sequence
103 * OID sequence
104 * signature algorithm OID(s) (can be multiple)
105 * NULL ASN.1 object terminator
106 * Octet string of hash value
107 * 1 byte end padding (0x01)
108 */
109
110 signature_data = signature_pointer;
111 remaining_length = signature_length;
112
113 /* Check padding type. */
114 if (signature_data[1] >= 2)
115 {
116 /* Invalid PKCS#7 encoding or decryption failure. */
117 return(NX_SECURE_X509_PKCS7_PARSING_FAILED);
118 }
119
120 /* Loop through padding - skip the first 2 bytes of padding type.
121 * Also ensure that we don't run past the end of the buffer if
122 * the NULL terminator isn't found. */
123 i = 2;
124 while (i < signature_length)
125 {
126 if (signature_data[i] == 0x00)
127 {
128 break;
129 }
130 i++;
131 }
132
133 /* Skip over the padding null-terminator. */
134 i++;
135
136 /* Make sure we actually saw a NULL byte. */
137 if (i >= signature_length)
138 {
139 return(NX_SECURE_X509_PKCS7_PARSING_FAILED);
140 }
141
142 /* Advance our working pointer. */
143 signature_data = &signature_data[i];
144 remaining_length -= i;
145
146 /* Now we have our ASN.1-encoded signature. */
147 status = _nx_secure_x509_asn1_tlv_block_parse(signature_data, &remaining_length, &tlv_type, &tlv_type_class, &tlv_length, &tlv_data, &header_length);
148
149 /* Make sure we parsed a proper ASN.1 sequence. */
150 if (status != 0 || tlv_type != NX_SECURE_ASN_TAG_SEQUENCE || tlv_type_class != NX_SECURE_ASN_TAG_CLASS_UNIVERSAL)
151 {
152 return(NX_SECURE_X509_PKCS7_PARSING_FAILED);
153 }
154
155 /* Advance our working pointer and adjust remaining length. */
156 signature_data = tlv_data;
157 remaining_length = tlv_length;
158
159 /* Next up is the OID sequence. */
160 status = _nx_secure_x509_asn1_tlv_block_parse(signature_data, &remaining_length, &tlv_type, &tlv_type_class, &tlv_length, &tlv_data, &header_length);
161
162 /* Make sure we parsed a proper ASN.1 sequence. */
163 if (status != 0 || tlv_type != NX_SECURE_ASN_TAG_SEQUENCE || tlv_type_class != NX_SECURE_ASN_TAG_CLASS_UNIVERSAL)
164 {
165 return(NX_SECURE_X509_PKCS7_PARSING_FAILED);
166 }
167 signature_data = tlv_data;
168 seq_length = tlv_length;
169
170 /* Next we parse the OID(s). */
171 do
172 {
173 /* Parse at least 1 OID. */
174 status = _nx_secure_x509_asn1_tlv_block_parse(signature_data, &seq_length, &tlv_type, &tlv_type_class, &tlv_length, &tlv_data, &header_length);
175
176 /* Make sure we parsed a proper ASN.1 sequence. */
177 if (status != 0 || tlv_type_class != NX_SECURE_ASN_TAG_CLASS_UNIVERSAL)
178 {
179 return(NX_SECURE_X509_PKCS7_PARSING_FAILED);
180 }
181
182 /* Adjust our buffer pointer. */
183 signature_data = &signature_data[tlv_length + header_length];
184
185 /* If we see a NULL tag, we are at the end of the list. */
186 if (tlv_type == NX_SECURE_ASN_TAG_NULL)
187 {
188 break;
189 }
190
191 /* Save off the OID. */
192 *signature_oid = tlv_data;
193 *signature_oid_length = tlv_length;
194 } while (tlv_type == NX_SECURE_ASN_TAG_OID);
195
196 /* Finally, we should be at the signature hash itself. */
197 status = _nx_secure_x509_asn1_tlv_block_parse(signature_data, &remaining_length, &tlv_type, &tlv_type_class, &tlv_length, &tlv_data, &header_length);
198
199 /* Make sure we parsed a proper ASN.1 sequence. */
200 if (status != 0 || tlv_type != NX_SECURE_ASN_TAG_OCTET_STRING || tlv_type_class != NX_SECURE_ASN_TAG_CLASS_UNIVERSAL)
201 {
202 return(NX_SECURE_X509_PKCS7_PARSING_FAILED);
203 }
204
205 /* Return pointer to hash data and its length. */
206 *hash_data = tlv_data;
207 *hash_length = tlv_length;
208
209 /* Signature is valid. */
210 return(NX_SECURE_X509_SUCCESS);
211 }
212
213