VirtualBox

source: vbox/trunk/src/VBox/Runtime/tools/RTSignTool.cpp@ 106464

Last change on this file since 106464 was 106264, checked in by vboxsync, 2 months ago

RTSignTool: Extended the extract-exe-signer-cert, extract-timestamp-root and extract-signer-root commands with a --as-c-array=name option that gets all the designated certificate in a form a C compiler can understand. bugref:10771

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 306.9 KB
Line 
1/* $Id: RTSignTool.cpp 106264 2024-10-09 20:50:12Z vboxsync $ */
2/** @file
3 * IPRT - Signing Tool.
4 */
5
6/*
7 * Copyright (C) 2006-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include <iprt/assert.h>
42#include <iprt/buildconfig.h>
43#include <iprt/ctype.h>
44#include <iprt/err.h>
45#include <iprt/getopt.h>
46#include <iprt/file.h>
47#include <iprt/initterm.h>
48#include <iprt/ldr.h>
49#include <iprt/message.h>
50#include <iprt/mem.h>
51#include <iprt/path.h>
52#include <iprt/stream.h>
53#include <iprt/string.h>
54#ifdef RT_OS_WINDOWS
55# include <iprt/utf16.h>
56#endif
57#include <iprt/uuid.h>
58#include <iprt/zero.h>
59#include <iprt/formats/asn1.h>
60#include <iprt/formats/mach-o.h>
61#ifndef RT_OS_WINDOWS
62# include <iprt/formats/pecoff.h>
63#else
64# define WIN_CERTIFICATE_ALIGNMENT UINT32_C(8) /* from pecoff.h */
65#endif
66#include <iprt/crypto/applecodesign.h>
67#include <iprt/crypto/digest.h>
68#include <iprt/crypto/key.h>
69#include <iprt/crypto/x509.h>
70#include <iprt/crypto/pkcs7.h>
71#include <iprt/crypto/store.h>
72#include <iprt/crypto/spc.h>
73#include <iprt/crypto/tsp.h>
74#include <iprt/cpp/ministring.h>
75#ifdef VBOX
76# include <VBox/sup.h> /* Certificates */
77#endif
78#ifdef RT_OS_WINDOWS
79# include <iprt/win/windows.h>
80# include <iprt/win/imagehlp.h>
81# include <wincrypt.h>
82# include <ncrypt.h>
83#endif
84#include "internal/ldr.h" /* for IMAGE_XX_SIGNATURE defines */
85
86
87/*********************************************************************************************************************************
88* Defined Constants And Macros *
89*********************************************************************************************************************************/
90#define OPT_OFF_CERT_FILE 0 /**< signtool /f file */
91#define OPT_OFF_CERT_SHA1 1 /**< signtool /sha1 thumbprint */
92#define OPT_OFF_CERT_SUBJECT 2 /**< signtool /n name */
93#define OPT_OFF_CERT_STORE 3 /**< signtool /s store */
94#define OPT_OFF_CERT_STORE_MACHINE 4 /**< signtool /sm */
95#define OPT_OFF_KEY_FILE 5 /**< no signtool equivalent, other than maybe /f. */
96#define OPT_OFF_KEY_PASSWORD 6 /**< signtool /p pass */
97#define OPT_OFF_KEY_PASSWORD_FILE 7 /**< no signtool equivalent. */
98#define OPT_OFF_KEY_NAME 8 /**< signtool /kc name */
99#define OPT_OFF_KEY_PROVIDER 9 /**< signtool /csp name (CSP = cryptographic service provider) */
100
101#define OPT_CERT_KEY_SWITCH_CASES(a_Instance, a_uBase, a_chOpt, a_ValueUnion, a_rcExit) \
102 case (a_uBase) + OPT_OFF_CERT_FILE: \
103 case (a_uBase) + OPT_OFF_CERT_SHA1: \
104 case (a_uBase) + OPT_OFF_CERT_SUBJECT: \
105 case (a_uBase) + OPT_OFF_CERT_STORE: \
106 case (a_uBase) + OPT_OFF_CERT_STORE_MACHINE: \
107 case (a_uBase) + OPT_OFF_KEY_FILE: \
108 case (a_uBase) + OPT_OFF_KEY_PASSWORD: \
109 case (a_uBase) + OPT_OFF_KEY_PASSWORD_FILE: \
110 case (a_uBase) + OPT_OFF_KEY_NAME: \
111 case (a_uBase) + OPT_OFF_KEY_PROVIDER: \
112 a_rcExit = a_Instance.handleOption((a_chOpt) - (a_uBase), &(a_ValueUnion)); \
113 break
114
115#define OPT_CERT_KEY_GETOPTDEF_ENTRIES(a_szPrefix, a_szSuffix, a_uBase) \
116 { a_szPrefix "cert-file" a_szSuffix, (a_uBase) + OPT_OFF_CERT_FILE, RTGETOPT_REQ_STRING }, \
117 { a_szPrefix "cert-sha1" a_szSuffix, (a_uBase) + OPT_OFF_CERT_SHA1, RTGETOPT_REQ_STRING }, \
118 { a_szPrefix "cert-subject" a_szSuffix, (a_uBase) + OPT_OFF_CERT_SUBJECT, RTGETOPT_REQ_STRING }, \
119 { a_szPrefix "cert-store" a_szSuffix, (a_uBase) + OPT_OFF_CERT_STORE, RTGETOPT_REQ_STRING }, \
120 { a_szPrefix "cert-machine-store" a_szSuffix, (a_uBase) + OPT_OFF_CERT_STORE_MACHINE, RTGETOPT_REQ_NOTHING }, \
121 { a_szPrefix "key-file" a_szSuffix, (a_uBase) + OPT_OFF_KEY_FILE, RTGETOPT_REQ_STRING }, \
122 { a_szPrefix "key-password" a_szSuffix, (a_uBase) + OPT_OFF_KEY_PASSWORD, RTGETOPT_REQ_STRING }, \
123 { a_szPrefix "key-password-file" a_szSuffix, (a_uBase) + OPT_OFF_KEY_PASSWORD_FILE, RTGETOPT_REQ_STRING }, \
124 { a_szPrefix "key-name" a_szSuffix, (a_uBase) + OPT_OFF_KEY_NAME, RTGETOPT_REQ_STRING }, \
125 { a_szPrefix "key-provider" a_szSuffix, (a_uBase) + OPT_OFF_KEY_PROVIDER, RTGETOPT_REQ_STRING }
126
127#define OPT_CERT_KEY_GETOPTDEF_COMPAT_ENTRIES(a_uBase) \
128 { "/f", (a_uBase) + OPT_OFF_CERT_FILE, RTGETOPT_REQ_STRING }, \
129 { "/sha1", (a_uBase) + OPT_OFF_CERT_SHA1, RTGETOPT_REQ_STRING }, \
130 { "/n", (a_uBase) + OPT_OFF_CERT_SUBJECT, RTGETOPT_REQ_STRING }, \
131 { "/s", (a_uBase) + OPT_OFF_CERT_STORE, RTGETOPT_REQ_STRING }, \
132 { "/sm", (a_uBase) + OPT_OFF_CERT_STORE_MACHINE, RTGETOPT_REQ_NOTHING }, \
133 { "/p", (a_uBase) + OPT_OFF_KEY_PASSWORD, RTGETOPT_REQ_STRING }, \
134 { "/kc", (a_uBase) + OPT_OFF_KEY_NAME, RTGETOPT_REQ_STRING }, \
135 { "/csp", (a_uBase) + OPT_OFF_KEY_PROVIDER, RTGETOPT_REQ_STRING }
136
137#define OPT_CERT_KEY_SYNOPSIS(a_szPrefix, a_szSuffix) \
138 "[" a_szPrefix "cert-file" a_szSuffix " <file.pem|file.crt>] " \
139 "[" a_szPrefix "cert-sha1" a_szSuffix " <fingerprint>] " \
140 "[" a_szPrefix "cert-subject" a_szSuffix " <part-name>] " \
141 "[" a_szPrefix "cert-store" a_szSuffix " <store>] " \
142 "[" a_szPrefix "cert-machine-store" a_szSuffix "] " \
143 "[" a_szPrefix "key-file" a_szSuffix " <file.pem|file.p12>] " \
144 "[" a_szPrefix "key-password" a_szSuffix " <password>] " \
145 "[" a_szPrefix "key-password-file" a_szSuffix " <file>|stdin] " \
146 "[" a_szPrefix "key-name" a_szSuffix " <name>] " \
147 "[" a_szPrefix "key-provider" a_szSuffix " <csp>] "
148
149#define OPT_HASH_PAGES 1200
150#define OPT_NO_HASH_PAGES 1201
151#define OPT_ADD_CERT 1202
152#define OPT_TIMESTAMP_TYPE 1203
153#define OPT_TIMESTAMP_TYPE_2 1204
154#define OPT_TIMESTAMP_OVERRIDE 1205
155#define OPT_NO_SIGNING_TIME 1206
156#define OPT_FILE_TYPE 1207
157#define OPT_IGNORED 1208
158
159
160/*********************************************************************************************************************************
161* Structures and Typedefs *
162*********************************************************************************************************************************/
163/** Help detail levels. */
164typedef enum RTSIGNTOOLHELP
165{
166 RTSIGNTOOLHELP_USAGE,
167 RTSIGNTOOLHELP_FULL
168} RTSIGNTOOLHELP;
169
170
171/** Filetypes. */
172typedef enum RTSIGNTOOLFILETYPE
173{
174 RTSIGNTOOLFILETYPE_INVALID = 0,
175 RTSIGNTOOLFILETYPE_DETECT,
176 RTSIGNTOOLFILETYPE_EXE,
177 RTSIGNTOOLFILETYPE_CAT,
178 RTSIGNTOOLFILETYPE_UNKNOWN,
179 RTSIGNTOOLFILETYPE_END
180} RTSIGNTOOLFILETYPE;
181
182
183/**
184 * PKCS\#7 signature data.
185 */
186typedef struct SIGNTOOLPKCS7
187{
188 /** The file type. */
189 RTSIGNTOOLFILETYPE enmType;
190 /** The raw signature. */
191 uint8_t *pbBuf;
192 /** Size of the raw signature. */
193 size_t cbBuf;
194 /** The filename. */
195 const char *pszFilename;
196 /** The outer content info wrapper. */
197 RTCRPKCS7CONTENTINFO ContentInfo;
198 /** Pointer to the decoded SignedData inside the ContentInfo member. */
199 PRTCRPKCS7SIGNEDDATA pSignedData;
200
201 /** Newly encoded raw signature.
202 * @sa SignToolPkcs7_Encode() */
203 uint8_t *pbNewBuf;
204 /** Size of newly encoded raw signature. */
205 size_t cbNewBuf;
206
207} SIGNTOOLPKCS7;
208typedef SIGNTOOLPKCS7 *PSIGNTOOLPKCS7;
209
210
211/**
212 * PKCS\#7 signature data for executable.
213 */
214typedef struct SIGNTOOLPKCS7EXE : public SIGNTOOLPKCS7
215{
216 /** The module handle. */
217 RTLDRMOD hLdrMod;
218} SIGNTOOLPKCS7EXE;
219typedef SIGNTOOLPKCS7EXE *PSIGNTOOLPKCS7EXE;
220
221
222/**
223 * Data for the show exe (signature) command.
224 */
225typedef struct SHOWEXEPKCS7 : public SIGNTOOLPKCS7EXE
226{
227 /** The verbosity. */
228 unsigned cVerbosity;
229 /** The prefix buffer. */
230 char szPrefix[256];
231 /** Temporary buffer. */
232 char szTmp[4096];
233} SHOWEXEPKCS7;
234typedef SHOWEXEPKCS7 *PSHOWEXEPKCS7;
235
236
237/*********************************************************************************************************************************
238* Internal Functions *
239*********************************************************************************************************************************/
240static RTEXITCODE HandleHelp(int cArgs, char **papszArgs);
241static RTEXITCODE HelpHelp(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel);
242static RTEXITCODE HandleVersion(int cArgs, char **papszArgs);
243static int HandleShowExeWorkerPkcs7DisplaySignerInfo(PSHOWEXEPKCS7 pThis, size_t offPrefix, PCRTCRPKCS7SIGNERINFO pSignerInfo);
244static int HandleShowExeWorkerPkcs7Display(PSHOWEXEPKCS7 pThis, PRTCRPKCS7SIGNEDDATA pSignedData, size_t offPrefix,
245 PCRTCRPKCS7CONTENTINFO pContentInfo);
246
247
248/*********************************************************************************************************************************
249* Certificate and Private Key Handling (options, ++). *
250*********************************************************************************************************************************/
251#ifdef RT_OS_WINDOWS
252
253/** @todo create a better fake certificate. */
254const unsigned char g_abFakeCertificate[] =
255{
256 0x30, 0x82, 0x03, 0xb2, 0x30, 0x82, 0x02, 0x9a, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x31, /* 0x00000000: 0...0..........1 */
257 0xba, 0xd6, 0xbc, 0x5d, 0x9a, 0xe0, 0xb0, 0x4e, 0xd4, 0xfa, 0xcc, 0xfb, 0x47, 0x00, 0x5c, 0x30, /* 0x00000010: ...]...N....G.\0 */
258 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x71, /* 0x00000020: ...*.H........0q */
259 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x13, 0x54, 0x69, 0x6d, 0x65, 0x73, /* 0x00000030: 1.0...U....Times */
260 0x74, 0x61, 0x6d, 0x70, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x32, 0x31, 0x0c, /* 0x00000040: tamp Signing 21. */
261 0x30, 0x0a, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x03, 0x44, 0x65, 0x76, 0x31, 0x15, 0x30, 0x13, /* 0x00000050: 0...U....Dev1.0. */
262 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0c, 0x54, 0x65, 0x73, 0x74, 0x20, 0x43, 0x6f, 0x6d, 0x70, /* 0x00000060: ..U....Test Comp */
263 0x61, 0x6e, 0x79, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x09, 0x53, 0x74, /* 0x00000070: any1.0...U....St */
264 0x75, 0x74, 0x74, 0x67, 0x61, 0x72, 0x74, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, /* 0x00000080: uttgart1.0...U.. */
265 0x0c, 0x02, 0x42, 0x42, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x44, /* 0x00000090: ..BB1.0...U....D */
266 0x45, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x30, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x31, 0x30, /* 0x000000a0: E0...00010100010 */
267 0x31, 0x5a, 0x17, 0x0d, 0x33, 0x36, 0x31, 0x32, 0x33, 0x31, 0x32, 0x32, 0x35, 0x39, 0x35, 0x39, /* 0x000000b0: 1Z..361231225959 */
268 0x5a, 0x30, 0x71, 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x13, 0x54, 0x69, /* 0x000000c0: Z0q1.0...U....Ti */
269 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x20, /* 0x000000d0: mestamp Signing */
270 0x32, 0x31, 0x0c, 0x30, 0x0a, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x03, 0x44, 0x65, 0x76, 0x31, /* 0x000000e0: 21.0...U....Dev1 */
271 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0c, 0x54, 0x65, 0x73, 0x74, 0x20, 0x43, /* 0x000000f0: .0...U....Test C */
272 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, /* 0x00000100: ompany1.0...U... */
273 0x09, 0x53, 0x74, 0x75, 0x74, 0x74, 0x67, 0x61, 0x72, 0x74, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, /* 0x00000110: .Stuttgart1.0... */
274 0x55, 0x04, 0x08, 0x0c, 0x02, 0x42, 0x42, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, /* 0x00000120: U....BB1.0...U.. */
275 0x13, 0x02, 0x44, 0x45, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, /* 0x00000130: ..DE0.."0...*.H. */
276 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, /* 0x00000140: ............0... */
277 0x02, 0x82, 0x01, 0x01, 0x00, 0xdb, 0x18, 0x63, 0x33, 0xf2, 0x08, 0x90, 0x5a, 0xab, 0xda, 0x88, /* 0x00000150: .......c3...Z... */
278 0x73, 0x86, 0x49, 0xea, 0x8b, 0xaf, 0xcf, 0x67, 0x15, 0xa5, 0x39, 0xe6, 0xa2, 0x94, 0x0c, 0x3f, /* 0x00000160: s.I....g..9....? */
279 0xa1, 0x2e, 0x6c, 0xd2, 0xdf, 0x01, 0x65, 0x6d, 0xed, 0x6c, 0x4c, 0xac, 0xe7, 0x77, 0x7a, 0x45, /* 0x00000170: ..l...em.lL..wzE */
280 0x05, 0x6b, 0x24, 0xf3, 0xaf, 0x45, 0x35, 0x6e, 0x64, 0x0a, 0xac, 0x1d, 0x37, 0xe1, 0x33, 0xa4, /* 0x00000180: .k$..E5nd...7.3. */
281 0x92, 0xec, 0x45, 0xe8, 0x99, 0xc1, 0xde, 0x6f, 0xab, 0x7c, 0xf0, 0xdc, 0xe2, 0xc5, 0x42, 0xa3, /* 0x00000190: ..E....o.|....B. */
282 0xea, 0xf5, 0x8a, 0xf9, 0x0e, 0xe7, 0xb3, 0x35, 0xa2, 0x75, 0x5e, 0x87, 0xd2, 0x2a, 0xd1, 0x27, /* 0x000001a0: .......5.u^..*.' */
283 0xa6, 0x79, 0x9e, 0xfe, 0x90, 0xbf, 0x97, 0xa4, 0xa1, 0xd8, 0xf7, 0xd7, 0x05, 0x59, 0x44, 0x27, /* 0x000001b0: .y...........YD' */
284 0x39, 0x6e, 0x33, 0x01, 0x2e, 0x46, 0x92, 0x47, 0xbe, 0x50, 0x91, 0x26, 0x27, 0xe5, 0x4b, 0x3a, /* 0x000001c0: 9n3..F.G.P.&'.K: */
285 0x76, 0x26, 0x64, 0x92, 0x0c, 0xa0, 0x54, 0x43, 0x6f, 0x56, 0xcc, 0x7b, 0xd0, 0xe3, 0xd8, 0x39, /* 0x000001d0: v&d...TCoV.{...9 */
286 0x5f, 0xb9, 0x41, 0xda, 0x1c, 0x62, 0x88, 0x0c, 0x45, 0x03, 0x63, 0xf8, 0xff, 0xe5, 0x3e, 0x87, /* 0x000001e0: _.A..b..E.c...>. */
287 0x0c, 0x75, 0xc9, 0xdd, 0xa2, 0xc0, 0x1b, 0x63, 0x19, 0xeb, 0x09, 0x9d, 0xa1, 0xbb, 0x0f, 0x63, /* 0x000001f0: .u.....c.......c */
288 0x67, 0x1c, 0xa3, 0xfd, 0x2f, 0xd1, 0x2a, 0xda, 0xd8, 0x93, 0x66, 0x45, 0x54, 0xef, 0x8b, 0x6d, /* 0x00000200: g.....*...fET..m */
289 0x12, 0x15, 0x0f, 0xd4, 0xb5, 0x04, 0x17, 0x30, 0x5b, 0xfa, 0x12, 0x96, 0x48, 0x5b, 0x38, 0x65, /* 0x00000210: .......0[...H[8e */
290 0xfd, 0x8f, 0x0c, 0xa3, 0x11, 0x46, 0x49, 0xe0, 0x62, 0xc3, 0xcc, 0x34, 0xe6, 0xfb, 0xab, 0x51, /* 0x00000220: .....FI.b..4...Q */
291 0xc3, 0xd4, 0x0b, 0xdc, 0x39, 0x93, 0x87, 0x90, 0x10, 0x9f, 0xce, 0x43, 0x27, 0x31, 0xd5, 0x4e, /* 0x00000230: ....9......C'1.N */
292 0x52, 0x60, 0xf1, 0x93, 0xd5, 0x06, 0xc4, 0x4e, 0x65, 0xb6, 0x35, 0x4a, 0x64, 0x15, 0xf8, 0xaf, /* 0x00000240: R`.....Ne.5Jd... */
293 0x71, 0xb2, 0x42, 0x50, 0x89, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x46, 0x30, 0x44, 0x30, 0x0e, /* 0x00000250: q.BP.......F0D0. */
294 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x07, 0x80, 0x30, 0x13, /* 0x00000260: ..U...........0. */
295 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x0c, 0x30, 0x0a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, /* 0x00000270: ..U.%..0...+.... */
296 0x07, 0x03, 0x08, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x52, 0x9d, /* 0x00000280: ...0...U......R. */
297 0x4d, 0xcd, 0x41, 0xe1, 0xd2, 0x68, 0x22, 0xd3, 0x10, 0x33, 0x01, 0xca, 0xff, 0x00, 0x1d, 0x27, /* 0x00000290: M.A..h"..3.....' */
298 0xa4, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, /* 0x000002a0: ..0...*.H....... */
299 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0xc5, 0x5a, 0x51, 0x83, 0x68, 0x3f, 0x06, 0x39, 0x79, 0x13, /* 0x000002b0: .......ZQ.h?.9y. */
300 0xa6, 0xf0, 0x1a, 0xf9, 0x29, 0x16, 0x2d, 0xa2, 0x07, 0xaa, 0x9b, 0xc3, 0x13, 0x88, 0x39, 0x69, /* 0x000002c0: ....).-.......9i */
301 0xba, 0xf7, 0x0d, 0xfb, 0xc0, 0x6e, 0x3a, 0x0b, 0x49, 0x10, 0xd1, 0xbe, 0x36, 0x91, 0x3f, 0x9d, /* 0x000002d0: .....n:.I...6.?. */
302 0xa1, 0xe8, 0xc4, 0x91, 0xf9, 0x02, 0xe1, 0xf1, 0x01, 0x15, 0x09, 0xb7, 0xa1, 0xf1, 0xec, 0x43, /* 0x000002e0: ...............C */
303 0x0d, 0x73, 0xd1, 0x31, 0x02, 0x4a, 0xce, 0x21, 0xf2, 0xa7, 0x99, 0x7c, 0xee, 0x85, 0x54, 0xc0, /* 0x000002f0: .s.1.J.!...|..T. */
304 0x55, 0x9b, 0x19, 0x37, 0xe8, 0xcf, 0x94, 0x41, 0x10, 0x6e, 0x67, 0xdd, 0x86, 0xaf, 0xb7, 0xfe, /* 0x00000300: U..7...A.ng..... */
305 0x50, 0x05, 0xf6, 0xfb, 0x0a, 0xdf, 0x88, 0xb5, 0x59, 0x69, 0x98, 0x27, 0xf8, 0x81, 0x6a, 0x4a, /* 0x00000310: P.......Yi.'..jJ */
306 0x7c, 0xf3, 0x63, 0xa9, 0x41, 0x78, 0x76, 0x12, 0xdb, 0x0e, 0x94, 0x0a, 0xdb, 0x1d, 0x3c, 0x87, /* 0x00000320: |.c.Axv.......<. */
307 0x35, 0xca, 0x28, 0xeb, 0xb0, 0x62, 0x27, 0x69, 0xe2, 0xf3, 0x84, 0x48, 0xa2, 0x2d, 0xd7, 0x0e, /* 0x00000330: 5.(..b'i...H.-.. */
308 0x4b, 0x6d, 0x39, 0xa7, 0x3e, 0x04, 0x94, 0x8e, 0xb6, 0x4b, 0x91, 0x01, 0x68, 0xf9, 0xd2, 0x75, /* 0x00000340: Km9.>....K..h..u */
309 0x1b, 0xac, 0x42, 0x3b, 0x85, 0xfc, 0x5b, 0x48, 0x3a, 0x13, 0xe7, 0x1c, 0x17, 0xcd, 0x84, 0x89, /* 0x00000350: ..B;..[H:....... */
310 0x9e, 0x5f, 0xe3, 0x77, 0xc0, 0xae, 0x34, 0xc3, 0x87, 0x76, 0x4a, 0x23, 0x30, 0xa0, 0xe1, 0x45, /* 0x00000360: ._.w..4..vJ#0..E */
311 0x94, 0x2a, 0x5b, 0x6b, 0x5a, 0xf0, 0x1a, 0x7e, 0xa6, 0xc4, 0xed, 0xe4, 0xac, 0x5d, 0xdf, 0x87, /* 0x00000370: .*[kZ..~.....].. */
312 0x8f, 0xc5, 0xb4, 0x8c, 0xbc, 0x70, 0xc1, 0xf7, 0xb2, 0x72, 0xbd, 0x73, 0xc9, 0x4e, 0xed, 0x8d, /* 0x00000380: .....p...r.s.N.. */
313 0x29, 0x33, 0xe9, 0x14, 0xc1, 0x5e, 0xff, 0x39, 0xa8, 0xe7, 0x9a, 0x3b, 0x7a, 0x3c, 0xce, 0x5d, /* 0x00000390: )3...^.9...;z<.] */
314 0x0f, 0x3c, 0x82, 0x90, 0xff, 0x81, 0x82, 0x00, 0x82, 0x5f, 0xba, 0x08, 0x79, 0xb1, 0x97, 0xc3, /* 0x000003a0: .<......._..y... */
315 0x09, 0x75, 0xc0, 0x04, 0x9b, 0x67, /* 0x000003b0: .u...g */
316};
317
318const unsigned char g_abFakeRsaKey[] =
319{
320 0x30, 0x82, 0x04, 0xa4, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00, 0xdb, 0x18, 0x63, 0x33, /* 0x00000000: 0.............c3 */
321 0xf2, 0x08, 0x90, 0x5a, 0xab, 0xda, 0x88, 0x73, 0x86, 0x49, 0xea, 0x8b, 0xaf, 0xcf, 0x67, 0x15, /* 0x00000010: ...Z...s.I....g. */
322 0xa5, 0x39, 0xe6, 0xa2, 0x94, 0x0c, 0x3f, 0xa1, 0x2e, 0x6c, 0xd2, 0xdf, 0x01, 0x65, 0x6d, 0xed, /* 0x00000020: .9....?..l...em. */
323 0x6c, 0x4c, 0xac, 0xe7, 0x77, 0x7a, 0x45, 0x05, 0x6b, 0x24, 0xf3, 0xaf, 0x45, 0x35, 0x6e, 0x64, /* 0x00000030: lL..wzE.k$..E5nd */
324 0x0a, 0xac, 0x1d, 0x37, 0xe1, 0x33, 0xa4, 0x92, 0xec, 0x45, 0xe8, 0x99, 0xc1, 0xde, 0x6f, 0xab, /* 0x00000040: ...7.3...E....o. */
325 0x7c, 0xf0, 0xdc, 0xe2, 0xc5, 0x42, 0xa3, 0xea, 0xf5, 0x8a, 0xf9, 0x0e, 0xe7, 0xb3, 0x35, 0xa2, /* 0x00000050: |....B........5. */
326 0x75, 0x5e, 0x87, 0xd2, 0x2a, 0xd1, 0x27, 0xa6, 0x79, 0x9e, 0xfe, 0x90, 0xbf, 0x97, 0xa4, 0xa1, /* 0x00000060: u^..*.'.y....... */
327 0xd8, 0xf7, 0xd7, 0x05, 0x59, 0x44, 0x27, 0x39, 0x6e, 0x33, 0x01, 0x2e, 0x46, 0x92, 0x47, 0xbe, /* 0x00000070: ....YD'9n3..F.G. */
328 0x50, 0x91, 0x26, 0x27, 0xe5, 0x4b, 0x3a, 0x76, 0x26, 0x64, 0x92, 0x0c, 0xa0, 0x54, 0x43, 0x6f, /* 0x00000080: P.&'.K:v&d...TCo */
329 0x56, 0xcc, 0x7b, 0xd0, 0xe3, 0xd8, 0x39, 0x5f, 0xb9, 0x41, 0xda, 0x1c, 0x62, 0x88, 0x0c, 0x45, /* 0x00000090: V.{...9_.A..b..E */
330 0x03, 0x63, 0xf8, 0xff, 0xe5, 0x3e, 0x87, 0x0c, 0x75, 0xc9, 0xdd, 0xa2, 0xc0, 0x1b, 0x63, 0x19, /* 0x000000a0: .c...>..u.....c. */
331 0xeb, 0x09, 0x9d, 0xa1, 0xbb, 0x0f, 0x63, 0x67, 0x1c, 0xa3, 0xfd, 0x2f, 0xd1, 0x2a, 0xda, 0xd8, /* 0x000000b0: ......cg.....*.. */
332 0x93, 0x66, 0x45, 0x54, 0xef, 0x8b, 0x6d, 0x12, 0x15, 0x0f, 0xd4, 0xb5, 0x04, 0x17, 0x30, 0x5b, /* 0x000000c0: .fET..m.......0[ */
333 0xfa, 0x12, 0x96, 0x48, 0x5b, 0x38, 0x65, 0xfd, 0x8f, 0x0c, 0xa3, 0x11, 0x46, 0x49, 0xe0, 0x62, /* 0x000000d0: ...H[8e.....FI.b */
334 0xc3, 0xcc, 0x34, 0xe6, 0xfb, 0xab, 0x51, 0xc3, 0xd4, 0x0b, 0xdc, 0x39, 0x93, 0x87, 0x90, 0x10, /* 0x000000e0: ..4...Q....9.... */
335 0x9f, 0xce, 0x43, 0x27, 0x31, 0xd5, 0x4e, 0x52, 0x60, 0xf1, 0x93, 0xd5, 0x06, 0xc4, 0x4e, 0x65, /* 0x000000f0: ..C'1.NR`.....Ne */
336 0xb6, 0x35, 0x4a, 0x64, 0x15, 0xf8, 0xaf, 0x71, 0xb2, 0x42, 0x50, 0x89, 0x02, 0x03, 0x01, 0x00, /* 0x00000100: .5Jd...q.BP..... */
337 0x01, 0x02, 0x82, 0x01, 0x01, 0x00, 0xd0, 0x5e, 0x09, 0x3a, 0xc5, 0xdc, 0xcf, 0x2c, 0xec, 0x74, /* 0x00000110: .......^.:...,.t */
338 0x11, 0x81, 0x8d, 0x1d, 0x8f, 0x2a, 0xfa, 0x31, 0x4d, 0xe0, 0x90, 0x1a, 0xd8, 0xf5, 0x95, 0xc7, /* 0x00000120: .....*.1M....... */
339 0x70, 0x5c, 0x62, 0x42, 0xac, 0xe9, 0xd9, 0xf2, 0x14, 0xf1, 0xd0, 0x25, 0xbb, 0xeb, 0x06, 0xfe, /* 0x00000130: p\bB.......%.... */
340 0x09, 0xd6, 0x75, 0x67, 0xd7, 0x39, 0xc1, 0xa0, 0x67, 0x34, 0x4d, 0xd2, 0x12, 0x97, 0xaa, 0x5d, /* 0x00000140: ..ug.9..g4M....] */
341 0xeb, 0x0e, 0xb0, 0x16, 0x6c, 0x78, 0x8e, 0xa0, 0x75, 0xa3, 0xaa, 0x57, 0x88, 0x3b, 0x43, 0x4f, /* 0x00000150: ....lx..u..W.;CO */
342 0x75, 0x85, 0x67, 0xb0, 0x9b, 0xdd, 0x49, 0x0e, 0x6e, 0xdb, 0xea, 0xb3, 0xd4, 0x88, 0x54, 0xa0, /* 0x00000160: u.g...I.n.....T. */
343 0x46, 0x0d, 0x55, 0x6d, 0x98, 0xbd, 0x20, 0xf9, 0x9f, 0x61, 0x2d, 0x6f, 0xc7, 0xd7, 0x16, 0x66, /* 0x00000170: F.Um.. ..a-o...f */
344 0x72, 0xc7, 0x73, 0xbe, 0x9e, 0x48, 0xdc, 0x65, 0x12, 0x46, 0x35, 0x69, 0x55, 0xd8, 0x6b, 0x81, /* 0x00000180: r.s..H.e.F5iU.k. */
345 0x78, 0x40, 0x15, 0x93, 0x60, 0x31, 0x4e, 0x87, 0x15, 0x2a, 0x74, 0x74, 0x7b, 0xa0, 0x1f, 0x59, /* 0x00000190: x@..`1N..*tt{..Y */
346 0x8d, 0xc8, 0x3f, 0xdd, 0xf0, 0x13, 0x88, 0x2a, 0x4a, 0xf2, 0xf5, 0xf1, 0x9e, 0xf3, 0x2d, 0x9c, /* 0x000001a0: ..?....*J.....-. */
347 0x8e, 0xbc, 0xb1, 0x21, 0x45, 0xc7, 0x44, 0x0c, 0x6a, 0xfe, 0x4c, 0x20, 0xdc, 0x73, 0xda, 0x62, /* 0x000001b0: ...!E.D.j.L .s.b */
348 0x21, 0xcb, 0xdf, 0x06, 0xfc, 0x90, 0xc2, 0xbd, 0xd6, 0xde, 0xfb, 0xf6, 0x08, 0x69, 0x5d, 0xea, /* 0x000001c0: !............i]. */
349 0xb3, 0x7f, 0x93, 0x61, 0xf2, 0xc1, 0xd0, 0x61, 0x4f, 0xd5, 0x5b, 0x63, 0xba, 0xb0, 0x3b, 0x07, /* 0x000001d0: ...a...aO.[c..;. */
350 0x7a, 0x55, 0xcd, 0xa1, 0xae, 0x8a, 0x92, 0x21, 0xcc, 0x2f, 0x5b, 0xf8, 0x40, 0x6a, 0xcd, 0xd5, /* 0x000001e0: zU.....!..[.@j.. */
351 0x5f, 0x15, 0xf4, 0xb6, 0xbd, 0xe5, 0x91, 0xb9, 0xa8, 0xcc, 0x2a, 0xa8, 0xa6, 0x67, 0x57, 0x2b, /* 0x000001f0: _.........*..gW+ */
352 0x4b, 0xe9, 0x88, 0xe0, 0xbb, 0x58, 0xac, 0x69, 0x5f, 0x3c, 0x76, 0x28, 0xa6, 0x9d, 0xbc, 0x71, /* 0x00000200: K....X.i_<v(...q */
353 0x7f, 0xcb, 0x0c, 0xc0, 0xbd, 0x61, 0x02, 0x81, 0x81, 0x00, 0xfc, 0x62, 0x79, 0x5b, 0xac, 0xf6, /* 0x00000210: .....a.....by[.. */
354 0x9b, 0x8c, 0xaa, 0x76, 0x2a, 0x30, 0x0e, 0xcf, 0x6b, 0x88, 0x72, 0x54, 0x8c, 0xdf, 0xf3, 0x9d, /* 0x00000220: ...v*0..k.rT.... */
355 0x84, 0xbb, 0xe7, 0x9d, 0xd4, 0x04, 0x29, 0x3c, 0xb5, 0x9d, 0x60, 0x9a, 0xcc, 0x12, 0xf3, 0xfa, /* 0x00000230: ......)<..`..... */
356 0x64, 0x30, 0x23, 0x47, 0xc6, 0xa4, 0x8b, 0x6c, 0x73, 0x6c, 0x6b, 0x78, 0x82, 0xec, 0x05, 0x19, /* 0x00000240: d0#G...lslkx.... */
357 0xde, 0xdd, 0xde, 0x52, 0xc5, 0x20, 0xd1, 0x11, 0x58, 0x19, 0x07, 0x5a, 0x90, 0xdd, 0x22, 0x91, /* 0x00000250: ...R. ..X..Z..". */
358 0x89, 0x22, 0x3f, 0x12, 0x54, 0x1a, 0xb8, 0x79, 0xd8, 0x6c, 0xbc, 0xf5, 0x0d, 0xc7, 0x73, 0x5c, /* 0x00000260: ."?.T..y.l....s\ */
359 0xed, 0xba, 0x40, 0x2b, 0x72, 0x34, 0x34, 0x97, 0xfa, 0x49, 0xf6, 0x43, 0x7c, 0xbc, 0x61, 0x30, /* 0x00000270: ..@+r44..I.C|.a0 */
360 0x54, 0x22, 0x21, 0x5f, 0x77, 0x68, 0x6b, 0x83, 0x95, 0xc6, 0x8d, 0xb8, 0x25, 0x3a, 0xd3, 0xb2, /* 0x00000280: T"!_whk.....%:.. */
361 0xbe, 0x29, 0x94, 0x01, 0x15, 0xf0, 0x36, 0x9d, 0x3e, 0xff, 0x02, 0x81, 0x81, 0x00, 0xde, 0x3b, /* 0x00000290: .)....6.>......; */
362 0xd6, 0x4b, 0x38, 0x69, 0x9b, 0x71, 0x29, 0x89, 0xd4, 0x6d, 0x8c, 0x41, 0xee, 0xe2, 0x4d, 0xfc, /* 0x000002a0: .K8i.q)..m.A..M. */
363 0xf0, 0x9a, 0x73, 0xf1, 0x15, 0x94, 0xac, 0x1b, 0x68, 0x5f, 0x79, 0x15, 0x3a, 0x41, 0x55, 0x09, /* 0x000002b0: ..s.....h_y.:AU. */
364 0xc7, 0x1e, 0xec, 0x27, 0x67, 0xe2, 0xdc, 0x54, 0xa8, 0x09, 0xe6, 0x46, 0x92, 0x92, 0x03, 0x8d, /* 0x000002c0: ...'g..T...F.... */
365 0xe5, 0x96, 0xfb, 0x1a, 0xdd, 0x59, 0x6f, 0x92, 0xf1, 0xf6, 0x8f, 0x76, 0xb0, 0xc5, 0xe6, 0xd7, /* 0x000002d0: .....Yo....v.... */
366 0x1b, 0x25, 0xaf, 0x04, 0x9f, 0xd8, 0x71, 0x27, 0x97, 0x99, 0x23, 0x09, 0x7d, 0xef, 0x06, 0x13, /* 0x000002e0: .%....q'..#.}... */
367 0xab, 0xdc, 0xa2, 0xd8, 0x5f, 0xc5, 0xec, 0xf3, 0x62, 0x20, 0x72, 0x7b, 0xa8, 0xc7, 0x09, 0x24, /* 0x000002f0: ...._...b r{...$ */
368 0xaf, 0x72, 0xc9, 0xea, 0xb8, 0x2d, 0xda, 0x00, 0xc8, 0xfe, 0xb4, 0x9f, 0x9f, 0xc7, 0xa9, 0xf7, /* 0x00000300: .r...-.......... */
369 0x1d, 0xce, 0xb1, 0xdb, 0xc5, 0x8a, 0x4e, 0xe8, 0x88, 0x77, 0x68, 0xdd, 0xf8, 0x77, 0x02, 0x81, /* 0x00000310: ......N..wh..w.. */
370 0x80, 0x5b, 0xa5, 0x8e, 0x98, 0x01, 0xa8, 0xd3, 0x37, 0x33, 0x37, 0x11, 0x7e, 0xbe, 0x02, 0x07, /* 0x00000320: .[......737.~... */
371 0xf4, 0x56, 0x3f, 0xe9, 0x9f, 0xf1, 0x20, 0xc3, 0xf0, 0x4f, 0xdc, 0xf9, 0xfe, 0x40, 0xd3, 0x30, /* 0x00000330: .V?... [email protected] */
372 0xc7, 0xe3, 0x2a, 0x92, 0xec, 0x56, 0xf8, 0x17, 0xa5, 0x7b, 0x4a, 0x37, 0x11, 0xcd, 0x27, 0x26, /* 0x00000340: ..*..V...{J7..'& */
373 0x8a, 0xba, 0x43, 0xda, 0x96, 0xc6, 0x0b, 0x6c, 0xe8, 0x78, 0x30, 0xea, 0x30, 0x4e, 0x7a, 0xd3, /* 0x00000350: ..C....l.x0.0Nz. */
374 0xd8, 0xd2, 0xd8, 0xca, 0x3d, 0xe2, 0xad, 0xa2, 0x74, 0x73, 0x1e, 0xbe, 0xb7, 0xad, 0x41, 0x61, /* 0x00000360: ....=...ts....Aa */
375 0x9b, 0xaa, 0xc9, 0xf9, 0xa4, 0xf1, 0x79, 0x4f, 0x42, 0x10, 0xc7, 0x36, 0x03, 0x4b, 0x0d, 0xdc, /* 0x00000370: ......yOB..6.K.. */
376 0xef, 0x3a, 0xa3, 0xab, 0x09, 0xe4, 0xe8, 0xdd, 0xc4, 0x3f, 0x06, 0x21, 0xa0, 0x23, 0x5a, 0x76, /* 0x00000380: .:.......?.!.#Zv */
377 0xea, 0xd0, 0xcf, 0x8b, 0x85, 0x5f, 0x16, 0x4b, 0x03, 0x62, 0x21, 0x3a, 0xcc, 0x2d, 0xa8, 0xd0, /* 0x00000390: ....._.K.b!:.-.. */
378 0x15, 0x02, 0x81, 0x80, 0x51, 0xf6, 0x89, 0xbb, 0xa6, 0x6b, 0xb4, 0xcb, 0xd0, 0xc1, 0x27, 0xda, /* 0x000003a0: ....Q....k....'. */
379 0xdb, 0x6e, 0xf9, 0xd6, 0xf7, 0x62, 0x81, 0xae, 0xc5, 0x72, 0x36, 0x3e, 0x66, 0x17, 0x99, 0xb0, /* 0x000003b0: .n...b...r6>f... */
380 0x14, 0xad, 0x52, 0x96, 0x03, 0xf2, 0x1e, 0x41, 0x76, 0x61, 0xb6, 0x3c, 0x02, 0x7d, 0x2a, 0x98, /* 0x000003c0: ..R....Ava.<.}*. */
381 0xb4, 0x18, 0x75, 0x38, 0x6b, 0x1d, 0x2b, 0x7f, 0x3a, 0xcf, 0x96, 0xb1, 0xc4, 0xa7, 0xd2, 0x9b, /* 0x000003d0: ..u8k.+.:....... */
382 0xd8, 0x1f, 0xb3, 0x64, 0xda, 0x15, 0x9d, 0xca, 0x91, 0x39, 0x48, 0x67, 0x00, 0x9c, 0xd4, 0x99, /* 0x000003e0: ...d.....9Hg.... */
383 0xc3, 0x45, 0x5d, 0xf0, 0x09, 0x32, 0xba, 0x21, 0x1e, 0xe2, 0x64, 0xb8, 0x50, 0x03, 0x17, 0xbe, /* 0x000003f0: .E]..2.!..d.P... */
384 0xd5, 0xda, 0x6b, 0xce, 0x34, 0xbe, 0x16, 0x03, 0x65, 0x1b, 0x2f, 0xa0, 0xa1, 0x95, 0xc6, 0x8b, /* 0x00000400: ..k.4...e....... */
385 0xc2, 0x3c, 0x59, 0x26, 0xbf, 0xb6, 0x07, 0x85, 0x53, 0x2d, 0xb6, 0x36, 0xa3, 0x91, 0xb9, 0xbb, /* 0x00000410: .<Y&....S-.6.... */
386 0x28, 0xaf, 0x2d, 0x53, 0x02, 0x81, 0x81, 0x00, 0xd7, 0xbc, 0x70, 0xd8, 0x18, 0x4f, 0x65, 0x8c, /* 0x00000420: (.-S......p..Oe. */
387 0x68, 0xca, 0x35, 0x77, 0x43, 0x50, 0x9b, 0xa1, 0xa3, 0x9a, 0x0e, 0x2d, 0x7b, 0x38, 0xf8, 0xba, /* 0x00000430: h.5wCP.....-{8.. */
388 0x14, 0x91, 0x3b, 0xc3, 0x3b, 0x1b, 0xa0, 0x6d, 0x45, 0xe4, 0xa8, 0x28, 0x97, 0xf6, 0x89, 0x13, /* 0x00000440: ..;.;..mE..(.... */
389 0xb6, 0x16, 0x6d, 0x65, 0x47, 0x8c, 0xa6, 0x21, 0xf8, 0x6a, 0xce, 0x4e, 0x44, 0x5e, 0x81, 0x47, /* 0x00000450: ..meG..!.j.ND^.G */
390 0xd9, 0xad, 0x8a, 0xb9, 0xd9, 0xe9, 0x3e, 0x33, 0x1e, 0x5f, 0xe9, 0xe9, 0xa7, 0xea, 0x60, 0x75, /* 0x00000460: ......>3._....`u */
391 0x02, 0x57, 0x71, 0xb5, 0xed, 0x47, 0x77, 0xda, 0x1a, 0x40, 0x38, 0xab, 0x82, 0xd2, 0x0d, 0xf5, /* 0x00000470: .Wq..Gw..@8..... */
392 0x0e, 0x8e, 0xa9, 0x24, 0xdc, 0x30, 0xc9, 0x98, 0xa2, 0x05, 0xcd, 0xca, 0x01, 0xcf, 0xae, 0x1d, /* 0x00000480: ...$.0.......... */
393 0xe9, 0x02, 0x47, 0x0e, 0x46, 0x1d, 0x52, 0x02, 0x9a, 0x99, 0x22, 0x23, 0x7f, 0xf8, 0x9e, 0xc2, /* 0x00000490: ..G.F.R..."#.... */
394 0x16, 0x86, 0xca, 0xa0, 0xa7, 0x34, 0xfb, 0xbc, /* 0x000004a0: .....4.. */
395};
396
397#endif /* RT_OS_WINDOWS */
398
399
400/**
401 * Certificate w/ public key + private key pair for signing.
402 */
403class SignToolKeyPair
404{
405protected:
406 /* Context: */
407 const char *m_pszWhat;
408 bool m_fMandatory;
409
410 /* Parameters kept till finalizing parsing: */
411 const char *m_pszCertFile;
412 const char *m_pszCertSha1;
413 uint8_t m_abCertSha1[RTSHA1_HASH_SIZE];
414 const char *m_pszCertSubject;
415 const char *m_pszCertStore;
416 bool m_fMachineStore; /**< false = personal store */
417
418 const char *m_pszKeyFile;
419 const char *m_pszKeyPassword;
420 const char *m_pszKeyName;
421 const char *m_pszKeyProvider;
422
423 /** String buffer for m_pszKeyPassword when read from file. */
424 RTCString m_strPassword;
425 /** Storage for pCertificate when it's loaded from a file. */
426 RTCRX509CERTIFICATE m_DecodedCert;
427#ifdef RT_OS_WINDOWS
428 /** For the fake certificate */
429 RTCRX509CERTIFICATE m_DecodedFakeCert;
430 /** The certificate store. */
431 HCERTSTORE m_hStore;
432 /** The windows certificate context. */
433 PCCERT_CONTEXT m_pCertCtx;
434 /** Whether hNCryptPrivateKey/hLegacyPrivateKey needs freeing or not. */
435 BOOL m_fFreePrivateHandle;
436#endif
437
438 /** Set if already finalized. */
439 bool m_fFinalized;
440
441 /** Store containing the intermediate certificates available to the host.
442 * */
443 static RTCRSTORE s_hStoreIntermediate;
444 /** Instance counter for helping cleaning up m_hStoreIntermediate. */
445 static uint32_t s_cInstances;
446
447public: /* used to be a struct, thus not prefix either. */
448 /* Result: */
449 PCRTCRX509CERTIFICATE pCertificate;
450 RTCRKEY hPrivateKey;
451#ifdef RT_OS_WINDOWS
452 PCRTCRX509CERTIFICATE pCertificateReal;
453 NCRYPT_KEY_HANDLE hNCryptPrivateKey;
454 HCRYPTPROV hLegacyPrivateKey;
455#endif
456
457public:
458 SignToolKeyPair(const char *a_pszWhat, bool a_fMandatory = false)
459 : m_pszWhat(a_pszWhat)
460 , m_fMandatory(a_fMandatory)
461 , m_pszCertFile(NULL)
462 , m_pszCertSha1(NULL)
463 , m_pszCertSubject(NULL)
464 , m_pszCertStore("MY")
465 , m_fMachineStore(false)
466 , m_pszKeyFile(NULL)
467 , m_pszKeyPassword(NULL)
468 , m_pszKeyName(NULL)
469 , m_pszKeyProvider(NULL)
470#ifdef RT_OS_WINDOWS
471 , m_hStore(NULL)
472 , m_pCertCtx(NULL)
473 , m_fFreePrivateHandle(FALSE)
474#endif
475 , m_fFinalized(false)
476 , pCertificate(NULL)
477 , hPrivateKey(NIL_RTCRKEY)
478#ifdef RT_OS_WINDOWS
479 , pCertificateReal(NULL)
480 , hNCryptPrivateKey(0)
481 , hLegacyPrivateKey(0)
482#endif
483 {
484 RT_ZERO(m_DecodedCert);
485#ifdef RT_OS_WINDOWS
486 RT_ZERO(m_DecodedFakeCert);
487#endif
488 s_cInstances++;
489 }
490
491 virtual ~SignToolKeyPair()
492 {
493 if (hPrivateKey != NIL_RTCRKEY)
494 {
495 RTCrKeyRelease(hPrivateKey);
496 hPrivateKey = NIL_RTCRKEY;
497 }
498 if (pCertificate == &m_DecodedCert)
499 {
500 RTCrX509Certificate_Delete(&m_DecodedCert);
501 pCertificate = NULL;
502 }
503#ifdef RT_OS_WINDOWS
504 if (pCertificate == &m_DecodedFakeCert)
505 {
506 RTCrX509Certificate_Delete(&m_DecodedFakeCert);
507 RTCrX509Certificate_Delete(&m_DecodedCert);
508 pCertificate = NULL;
509 pCertificateReal = NULL;
510 }
511#endif
512#ifdef RT_OS_WINDOWS
513 if (m_pCertCtx != NULL)
514 {
515 CertFreeCertificateContext(m_pCertCtx);
516 m_pCertCtx = NULL;
517 }
518 if (m_hStore != NULL)
519 {
520 CertCloseStore(m_hStore, 0);
521 m_hStore = NULL;
522 }
523#endif
524 s_cInstances--;
525 if (s_cInstances == 0)
526 {
527 RTCrStoreRelease(s_hStoreIntermediate);
528 s_hStoreIntermediate = NIL_RTCRSTORE;
529 }
530 }
531
532 bool isComplete(void) const
533 {
534 return pCertificate && hPrivateKey != NIL_RTCRKEY;
535 }
536
537 bool isNull(void) const
538 {
539 return pCertificate == NULL && hPrivateKey == NIL_RTCRKEY;
540 }
541
542 RTEXITCODE handleOption(unsigned offOpt, PRTGETOPTUNION pValueUnion)
543 {
544 AssertReturn(!m_fFinalized, RTMsgErrorExitFailure("Cannot handle options after finalizeOptions was called!"));
545 switch (offOpt)
546 {
547 case OPT_OFF_CERT_FILE:
548 m_pszCertFile = pValueUnion->psz;
549 m_pszCertSha1 = NULL;
550 m_pszCertSubject = NULL;
551 break;
552 case OPT_OFF_CERT_SHA1:
553 {
554 /* Crude normalization of input separators to colons, since it's likely
555 to use spaces and our conversion function only does colons or nothing. */
556 char szDigest[RTSHA1_DIGEST_LEN * 3 + 1];
557 int rc = RTStrCopy(szDigest, sizeof(szDigest), pValueUnion->psz);
558 if (RT_SUCCESS(rc))
559 {
560 char *pszDigest = RTStrStrip(szDigest);
561 size_t offDst = 0;
562 size_t offSrc = 0;
563 char ch;
564 while ((ch = pszDigest[offSrc++]) != '\0')
565 {
566 if (ch == ' ' || ch == '\t' || ch == ':')
567 {
568 while ((ch = pszDigest[offSrc]) == ' ' || ch == '\t' || ch == ':')
569 offSrc++;
570 ch = ch ? ':' : '\0';
571 }
572 pszDigest[offDst++] = ch;
573 }
574 pszDigest[offDst] = '\0';
575
576 /** @todo add a more relaxed input mode to RTStrConvertHexBytes that can deal
577 * with spaces as well as multi-byte cluster of inputs. */
578 rc = RTStrConvertHexBytes(pszDigest, m_abCertSha1, RTSHA1_HASH_SIZE, RTSTRCONVERTHEXBYTES_F_SEP_COLON);
579 if (RT_SUCCESS(rc))
580 {
581 m_pszCertFile = NULL;
582 m_pszCertSha1 = pValueUnion->psz;
583 m_pszCertSubject = NULL;
584 break;
585 }
586 }
587 return RTMsgErrorExitFailure("malformed SHA-1 certificate fingerprint (%Rrc): %s", rc, pValueUnion->psz);
588 }
589 case OPT_OFF_CERT_SUBJECT:
590 m_pszCertFile = NULL;
591 m_pszCertSha1 = NULL;
592 m_pszCertSubject = pValueUnion->psz;
593 break;
594 case OPT_OFF_CERT_STORE:
595 m_pszCertStore = pValueUnion->psz;
596 break;
597 case OPT_OFF_CERT_STORE_MACHINE:
598 m_fMachineStore = true;
599 break;
600
601 case OPT_OFF_KEY_FILE:
602 m_pszKeyFile = pValueUnion->psz;
603 m_pszKeyName = NULL;
604 break;
605 case OPT_OFF_KEY_NAME:
606 m_pszKeyFile = NULL;
607 m_pszKeyName = pValueUnion->psz;
608 break;
609 case OPT_OFF_KEY_PROVIDER:
610 m_pszKeyProvider = pValueUnion->psz;
611 break;
612 case OPT_OFF_KEY_PASSWORD:
613 m_pszKeyPassword = pValueUnion->psz;
614 break;
615 case OPT_OFF_KEY_PASSWORD_FILE:
616 {
617 m_pszKeyPassword = NULL;
618
619 size_t const cchMax = 512;
620 int rc = m_strPassword.reserveNoThrow(cchMax + 1);
621 if (RT_FAILURE(rc))
622 return RTMsgErrorExitFailure("out of memory");
623
624 PRTSTREAM pStrm = g_pStdIn;
625 bool const fClose = strcmp(pValueUnion->psz, "stdin") != 0;
626 if (fClose)
627 {
628 rc = RTStrmOpen(pValueUnion->psz, "r", &pStrm);
629 if (RT_FAILURE(rc))
630 return RTMsgErrorExitFailure("Failed to open password file '%s' for reading: %Rrc", pValueUnion->psz, rc);
631 }
632 rc = RTStrmGetLine(pStrm, m_strPassword.mutableRaw(), cchMax);
633 if (fClose)
634 RTStrmClose(pStrm);
635 if (rc == VERR_BUFFER_OVERFLOW || rc == VINF_BUFFER_OVERFLOW)
636 return RTMsgErrorExitFailure("Password from '%s' is too long (max %zu)", pValueUnion->psz, cchMax);
637 if (RT_FAILURE(rc))
638 return RTMsgErrorExitFailure("Error reading password from '%s': %Rrc", pValueUnion->psz, rc);
639
640 m_strPassword.jolt();
641 m_strPassword.stripRight();
642 m_pszKeyPassword = m_strPassword.c_str();
643 break;
644 }
645 default:
646 AssertFailedReturn(RTMsgErrorExitFailure("Invalid offOpt=%u!\n", offOpt));
647 }
648 return RTEXITCODE_SUCCESS;
649 }
650
651 RTEXITCODE finalizeOptions(unsigned cVerbosity)
652 {
653 RT_NOREF(cVerbosity);
654
655 /* Only do this once. */
656 if (m_fFinalized)
657 return RTEXITCODE_SUCCESS;
658 m_fFinalized = true;
659
660 /*
661 * Got a cert? Is it required?
662 */
663 bool const fHasKey = ( m_pszKeyFile != NULL
664 || m_pszKeyName != NULL);
665 bool const fHasCert = ( m_pszCertFile != NULL
666 || m_pszCertSha1 != NULL
667 || m_pszCertSubject != NULL);
668 if (!fHasCert)
669 {
670 if (m_fMandatory)
671 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Specifying a %s certificiate is required.", m_pszWhat);
672 return RTEXITCODE_SUCCESS;
673 }
674
675 /*
676 * Get the certificate.
677 */
678 RTERRINFOSTATIC ErrInfo;
679 /* From file: */
680 if (m_pszCertFile)
681 {
682 int rc = RTCrX509Certificate_ReadFromFile(&m_DecodedCert, m_pszCertFile, 0, &g_RTAsn1DefaultAllocator,
683 RTErrInfoInitStatic(&ErrInfo));
684 if (RT_FAILURE(rc))
685 return RTMsgErrorExitFailure("Error reading %s certificate from '%s': %Rrc%#RTeim",
686 m_pszWhat, m_pszCertFile, rc, &ErrInfo.Core);
687 pCertificate = &m_DecodedCert;
688 }
689 /* From certificate store by name (substring) or fingerprint: */
690 else
691 {
692#ifdef RT_OS_WINDOWS
693 m_hStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, X509_ASN_ENCODING, NULL,
694 CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG | CERT_STORE_READONLY_FLAG
695 | CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_ENUM_ARCHIVED_FLAG
696 | (m_fMachineStore ? CERT_SYSTEM_STORE_LOCAL_MACHINE : CERT_SYSTEM_STORE_CURRENT_USER),
697 m_pszCertStore);
698 if (m_hStore == NULL)
699 return RTMsgErrorExitFailure("Failed to open %s store '%s': %Rwc (%u)", m_fMachineStore ? "machine" : "user",
700 m_pszCertStore, GetLastError(), GetLastError());
701
702 CRYPT_HASH_BLOB Thumbprint = { RTSHA1_HASH_SIZE, m_abCertSha1 };
703 PRTUTF16 pwszSubject = NULL;
704 void const *pvFindParam = &Thumbprint;
705 DWORD fFind = CERT_FIND_SHA1_HASH;
706 if (!m_pszCertSha1)
707 {
708 int rc = RTStrToUtf16(m_pszCertSubject, &pwszSubject);
709 if (RT_FAILURE(rc))
710 return RTMsgErrorExitFailure("RTStrToUtf16 failed: %Rrc, input %.*Rhxs",
711 rc, strlen(m_pszCertSubject), m_pszCertSubject);
712 pvFindParam = pwszSubject;
713 fFind = CERT_FIND_SUBJECT_STR;
714 }
715
716 while ((m_pCertCtx = CertFindCertificateInStore(m_hStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0 /*fFlags*/,
717 fFind, pvFindParam, m_pCertCtx)) != NULL)
718 {
719 if (m_pCertCtx->dwCertEncodingType & X509_ASN_ENCODING)
720 {
721 RTASN1CURSORPRIMARY PrimaryCursor;
722 RTAsn1CursorInitPrimary(&PrimaryCursor, m_pCertCtx->pbCertEncoded, m_pCertCtx->cbCertEncoded,
723 RTErrInfoInitStatic(&ErrInfo),
724 &g_RTAsn1DefaultAllocator, RTASN1CURSOR_FLAGS_DER, "CurCtx");
725 int rc = RTCrX509Certificate_DecodeAsn1(&PrimaryCursor.Cursor, 0, &m_DecodedCert, "Cert");
726 if (RT_SUCCESS(rc))
727 {
728 pCertificate = &m_DecodedCert;
729 break;
730 }
731 RTMsgError("failed to decode certificate %p: %Rrc%#RTeim", m_pCertCtx, rc, &ErrInfo.Core);
732 }
733 }
734
735 RTUtf16Free(pwszSubject);
736 if (!m_pCertCtx)
737 return RTMsgErrorExitFailure("No certificate found matching %s '%s' (%Rwc / %u)",
738 m_pszCertSha1 ? "thumbprint" : "subject substring",
739 m_pszCertSha1 ? m_pszCertSha1 : m_pszCertSubject, GetLastError(), GetLastError());
740
741 /* Use this for private key too? */
742 if (!fHasKey)
743 {
744 HCRYPTPROV_OR_NCRYPT_KEY_HANDLE hTmpPrivateKey = 0;
745 DWORD dwKeySpec = 0;
746 if (CryptAcquireCertificatePrivateKey(m_pCertCtx,
747 CRYPT_ACQUIRE_SILENT_FLAG | CRYPT_ACQUIRE_COMPARE_KEY_FLAG
748 | CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG
749 | CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG,
750 NULL, &hTmpPrivateKey, &dwKeySpec, &m_fFreePrivateHandle))
751 {
752 if (cVerbosity > 1)
753 RTMsgInfo("hTmpPrivateKey=%p m_fFreePrivateHandle=%d dwKeySpec=%#x",
754 hTmpPrivateKey, m_fFreePrivateHandle, dwKeySpec);
755 Assert(dwKeySpec == CERT_NCRYPT_KEY_SPEC);
756 if (dwKeySpec == CERT_NCRYPT_KEY_SPEC)
757 hNCryptPrivateKey = hTmpPrivateKey;
758 else
759 hLegacyPrivateKey = hTmpPrivateKey; /** @todo remove or drop CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG */
760 return loadFakePrivateKeyAndCert();
761 }
762 return RTMsgErrorExitFailure("CryptAcquireCertificatePrivateKey failed: %Rwc (%d)", GetLastError(), GetLastError());
763 }
764#else
765 return RTMsgErrorExitFailure("Certificate store support is missing on this host");
766#endif
767 }
768
769 /*
770 * Get hold of the private key (if someone above already did, they'd returned already).
771 */
772 Assert(hPrivateKey == NIL_RTCRKEY);
773 /* Use cert file if nothing else specified. */
774 if (!fHasKey && m_pszCertFile)
775 m_pszKeyFile = m_pszCertFile;
776
777 /* Load from file:*/
778 if (m_pszKeyFile)
779 {
780 int rc = RTCrKeyCreateFromFile(&hPrivateKey, 0 /*fFlags*/, m_pszKeyFile, m_pszKeyPassword,
781 RTErrInfoInitStatic(&ErrInfo));
782 if (RT_FAILURE(rc))
783 return RTMsgErrorExitFailure("Error reading the %s private key from '%s': %Rrc%#RTeim",
784 m_pszWhat, m_pszKeyFile, rc, &ErrInfo.Core);
785 }
786 /* From key store: */
787 else
788 {
789 return RTMsgErrorExitFailure("Key store support is missing on this host");
790 }
791
792 return RTEXITCODE_SUCCESS;
793 }
794
795 /** Returns the real certificate. */
796 PCRTCRX509CERTIFICATE getRealCertificate() const
797 {
798#ifdef RT_OS_WINDOWS
799 if (pCertificateReal)
800 return pCertificateReal;
801#endif
802 return pCertificate;
803 }
804
805#ifdef RT_OS_WINDOWS
806 RTEXITCODE loadFakePrivateKeyAndCert()
807 {
808 int rc = RTCrX509Certificate_ReadFromBuffer(&m_DecodedFakeCert, g_abFakeCertificate, sizeof(g_abFakeCertificate),
809 0 /*fFlags*/, &g_RTAsn1DefaultAllocator, NULL, NULL);
810 if (RT_FAILURE(rc))
811 return RTMsgErrorExitFailure("RTCrX509Certificate_ReadFromBuffer/g_abFakeCertificate failed: %Rrc", rc);
812 pCertificateReal = pCertificate;
813 pCertificate = &m_DecodedFakeCert;
814
815 rc = RTCrKeyCreateFromBuffer(&hPrivateKey, 0 /*fFlags*/, g_abFakeRsaKey, sizeof(g_abFakeRsaKey), NULL, NULL, NULL);
816 if (RT_FAILURE(rc))
817 return RTMsgErrorExitFailure("RTCrKeyCreateFromBuffer/g_abFakeRsaKey failed: %Rrc", rc);
818 return RTEXITCODE_SUCCESS;
819 }
820
821#endif
822
823 /**
824 * Search for intermediate CA.
825 *
826 * Currently this only do a single certificate path, so this may go south if
827 * there are multiple paths available. It may work fine for a cross signing
828 * path, as long as the cross over is at the level immediately below the root.
829 */
830 PCRTCRCERTCTX findNextIntermediateCert(PCRTCRCERTCTX pPrev)
831 {
832 /*
833 * Make sure the store is loaded before we start.
834 */
835 if (s_hStoreIntermediate == NIL_RTCRSTORE)
836 {
837 Assert(!pPrev);
838 RTERRINFOSTATIC ErrInfo;
839 int rc = RTCrStoreCreateSnapshotById(&s_hStoreIntermediate,
840 !m_fMachineStore
841 ? RTCRSTOREID_USER_INTERMEDIATE_CAS : RTCRSTOREID_SYSTEM_INTERMEDIATE_CAS,
842 RTErrInfoInitStatic(&ErrInfo));
843 if (RT_FAILURE(rc))
844 {
845 RTMsgError("RTCrStoreCreateSnapshotById/%s-intermediate-CAs failed: %Rrc%#RTeim",
846 m_fMachineStore ? "user" : "machine", rc, &ErrInfo.Core);
847 return NULL;
848 }
849 }
850
851 /*
852 * Open the search handle for the parent of the previous/end certificate.
853 *
854 * We don't need to consider RTCRCERTCTX::pTaInfo here as we're not
855 * after trust anchors, only intermediate certificates.
856 */
857#ifdef RT_OS_WINDOWS
858 PCRTCRX509CERTIFICATE pChildCert = pPrev ? pPrev->pCert : pCertificateReal ? pCertificateReal : pCertificate;
859#else
860 PCRTCRX509CERTIFICATE pChildCert = pPrev ? pPrev->pCert : pCertificate;
861#endif
862 AssertReturnStmt(pChildCert, RTCrCertCtxRelease(pPrev), NULL);
863
864 RTCRSTORECERTSEARCH Search;
865 int rc = RTCrStoreCertFindBySubjectOrAltSubjectByRfc5280(s_hStoreIntermediate, &pChildCert->TbsCertificate.Issuer,
866 &Search);
867 if (RT_FAILURE(rc))
868 {
869 RTMsgError("RTCrStoreCertFindBySubjectOrAltSubjectByRfc5280 failed: %Rrc", rc);
870 return NULL;
871 }
872
873 /*
874 * We only gave the subject so, we have to check the serial number our selves.
875 */
876 PCRTCRCERTCTX pCertCtx;
877 while ((pCertCtx = RTCrStoreCertSearchNext(s_hStoreIntermediate, &Search)) != NULL)
878 {
879 if ( pCertCtx->pCert
880 && RTAsn1BitString_Compare(&pCertCtx->pCert->TbsCertificate.T1.IssuerUniqueId,
881 &pChildCert->TbsCertificate.T1.IssuerUniqueId) == 0 /* compares presentness too */
882 && !RTCrX509Certificate_IsSelfSigned(pCertCtx->pCert))
883 {
884 break; /** @todo compare valid periode too and keep a best match when outside the desired period? */
885 }
886 RTCrCertCtxRelease(pCertCtx);
887 }
888
889 RTCrStoreCertSearchDestroy(s_hStoreIntermediate, & Search);
890 RTCrCertCtxRelease(pPrev);
891 return pCertCtx;
892 }
893
894 /**
895 * Merges the user specified certificates with the signing certificate and any
896 * intermediate CAs we can find in the system store.
897 *
898 * @returns Merged store, NIL_RTCRSTORE on failure (messaged).
899 * @param hUserSpecifiedCertificates The user certificate store.
900 */
901 RTCRSTORE assembleAllAdditionalCertificates(RTCRSTORE hUserSpecifiedCertificates)
902 {
903 RTCRSTORE hRetStore;
904 int rc = RTCrStoreCreateInMemEx(&hRetStore, 0, hUserSpecifiedCertificates);
905 if (RT_SUCCESS(rc))
906 {
907 /* Add the signing certificate: */
908 RTERRINFOSTATIC ErrInfo;
909 rc = RTCrStoreCertAddX509(hRetStore, RTCRCERTCTX_F_ENC_X509_DER | RTCRCERTCTX_F_ADD_IF_NOT_FOUND,
910#ifdef RT_OS_WINDOWS
911 (PRTCRX509CERTIFICATE)(pCertificateReal ? pCertificateReal : pCertificate),
912#else
913 (PRTCRX509CERTIFICATE)pCertificate,
914#endif
915 RTErrInfoInitStatic(&ErrInfo));
916 if (RT_SUCCESS(rc))
917 {
918 /* Add all intermediate CAs certificates we can find. */
919 PCRTCRCERTCTX pInterCaCert = NULL;
920 while ((pInterCaCert = findNextIntermediateCert(pInterCaCert)) != NULL)
921 {
922 rc = RTCrStoreCertAddEncoded(hRetStore, RTCRCERTCTX_F_ENC_X509_DER | RTCRCERTCTX_F_ADD_IF_NOT_FOUND,
923 pInterCaCert->pabEncoded, pInterCaCert->cbEncoded,
924 RTErrInfoInitStatic(&ErrInfo));
925 if (RT_FAILURE(rc))
926 {
927 RTMsgError("RTCrStoreCertAddEncoded/InterCA failed: %Rrc%#RTeim", rc, &ErrInfo.Core);
928 RTCrCertCtxRelease(pInterCaCert);
929 break;
930 }
931 }
932 if (RT_SUCCESS(rc))
933 return hRetStore;
934 }
935 else
936 RTMsgError("RTCrStoreCertAddX509/signer failed: %Rrc%#RTeim", rc, &ErrInfo.Core);
937 RTCrStoreRelease(hRetStore);
938 }
939 else
940 RTMsgError("RTCrStoreCreateInMemEx failed: %Rrc", rc);
941 return NIL_RTCRSTORE;
942 }
943
944};
945
946/*static*/ RTCRSTORE SignToolKeyPair::s_hStoreIntermediate = NIL_RTCRSTORE;
947/*static*/ uint32_t SignToolKeyPair::s_cInstances = 0;
948
949
950/*********************************************************************************************************************************
951*
952*********************************************************************************************************************************/
953/** Timestamp type. */
954typedef enum
955{
956 /** Old timestamp style.
957 * This is just a counter signature with a trustworthy SigningTime attribute.
958 * Specificially it's the SignerInfo part of a detached PKCS#7 covering the
959 * SignerInfo.EncryptedDigest. */
960 kTimestampType_Old = 1,
961 /** This is a whole PKCS#7 signature of an TSTInfo from RFC-3161 (see page 7).
962 * Currently not supported. */
963 kTimestampType_New
964} TIMESTAMPTYPE;
965
966/**
967 * Timestamping options.
968 *
969 * Certificate w/ public key + private key pair for signing and signature type.
970 */
971class SignToolTimestampOpts : public SignToolKeyPair
972{
973public:
974 /** Type timestamp type. */
975 TIMESTAMPTYPE m_enmType;
976
977 SignToolTimestampOpts(const char *a_pszWhat, TIMESTAMPTYPE a_enmType = kTimestampType_Old)
978 : SignToolKeyPair(a_pszWhat)
979 , m_enmType(a_enmType)
980 {
981 }
982
983 bool isOldType() const { return m_enmType == kTimestampType_Old; }
984 bool isNewType() const { return m_enmType == kTimestampType_New; }
985};
986
987
988
989/*********************************************************************************************************************************
990* Crypto Store Auto Cleanup Wrapper. *
991*********************************************************************************************************************************/
992class CryptoStore
993{
994public:
995 RTCRSTORE m_hStore;
996
997 CryptoStore()
998 : m_hStore(NIL_RTCRSTORE)
999 {
1000 }
1001
1002 ~CryptoStore()
1003 {
1004 if (m_hStore != NIL_RTCRSTORE)
1005 {
1006 uint32_t cRefs = RTCrStoreRelease(m_hStore);
1007 Assert(cRefs == 0); RT_NOREF(cRefs);
1008 m_hStore = NIL_RTCRSTORE;
1009 }
1010 }
1011
1012 /**
1013 * Adds one or more certificates from the given file.
1014 *
1015 * @returns boolean success indicator.
1016 */
1017 bool addFromFile(const char *pszFilename, PRTERRINFOSTATIC pStaticErrInfo)
1018 {
1019 int rc = RTCrStoreCertAddFromFile(this->m_hStore, RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR,
1020 pszFilename, RTErrInfoInitStatic(pStaticErrInfo));
1021 if (RT_SUCCESS(rc))
1022 {
1023 if (RTErrInfoIsSet(&pStaticErrInfo->Core))
1024 RTMsgWarning("Warnings loading certificate '%s': %s", pszFilename, pStaticErrInfo->Core.pszMsg);
1025 return true;
1026 }
1027 RTMsgError("Error loading certificate '%s': %Rrc%#RTeim", pszFilename, rc, &pStaticErrInfo->Core);
1028 return false;
1029 }
1030
1031 /**
1032 * Adds trusted self-signed certificates from the system.
1033 *
1034 * @returns boolean success indicator.
1035 * @note The selection is self-signed rather than CAs here so that test signing
1036 * certificates will be included.
1037 */
1038 bool addSelfSignedRootsFromSystem(PRTERRINFOSTATIC pStaticErrInfo)
1039 {
1040 CryptoStore Tmp;
1041 int rc = RTCrStoreCreateSnapshotOfUserAndSystemTrustedCAsAndCerts(&Tmp.m_hStore, RTErrInfoInitStatic(pStaticErrInfo));
1042 if (RT_SUCCESS(rc))
1043 {
1044 RTCRSTORECERTSEARCH Search;
1045 rc = RTCrStoreCertFindAll(Tmp.m_hStore, &Search);
1046 if (RT_SUCCESS(rc))
1047 {
1048 PCRTCRCERTCTX pCertCtx;
1049 while ((pCertCtx = RTCrStoreCertSearchNext(Tmp.m_hStore, &Search)) != NULL)
1050 {
1051 /* Add it if it's a full fledged self-signed certificate, otherwise just skip: */
1052 if ( pCertCtx->pCert
1053 && RTCrX509Certificate_IsSelfSigned(pCertCtx->pCert))
1054 {
1055 int rc2 = RTCrStoreCertAddEncoded(this->m_hStore,
1056 pCertCtx->fFlags | RTCRCERTCTX_F_ADD_IF_NOT_FOUND,
1057 pCertCtx->pabEncoded, pCertCtx->cbEncoded, NULL);
1058 if (RT_FAILURE(rc2))
1059 RTMsgWarning("RTCrStoreCertAddEncoded failed for a certificate: %Rrc", rc2);
1060 }
1061 RTCrCertCtxRelease(pCertCtx);
1062 }
1063
1064 int rc2 = RTCrStoreCertSearchDestroy(Tmp.m_hStore, &Search);
1065 AssertRC(rc2);
1066 return true;
1067 }
1068 RTMsgError("RTCrStoreCertFindAll failed: %Rrc", rc);
1069 }
1070 else
1071 RTMsgError("RTCrStoreCreateSnapshotOfUserAndSystemTrustedCAsAndCerts failed: %Rrc%#RTeim", rc, &pStaticErrInfo->Core);
1072 return false;
1073 }
1074
1075 /**
1076 * Adds trusted self-signed certificates from the system.
1077 *
1078 * @returns boolean success indicator.
1079 */
1080 bool addIntermediateCertsFromSystem(PRTERRINFOSTATIC pStaticErrInfo)
1081 {
1082 bool fRc = true;
1083 RTCRSTOREID const s_aenmStoreIds[] = { RTCRSTOREID_SYSTEM_INTERMEDIATE_CAS, RTCRSTOREID_USER_INTERMEDIATE_CAS };
1084 for (size_t i = 0; i < RT_ELEMENTS(s_aenmStoreIds); i++)
1085 {
1086 CryptoStore Tmp;
1087 int rc = RTCrStoreCreateSnapshotById(&Tmp.m_hStore, s_aenmStoreIds[i], RTErrInfoInitStatic(pStaticErrInfo));
1088 if (RT_SUCCESS(rc))
1089 {
1090 RTCRSTORECERTSEARCH Search;
1091 rc = RTCrStoreCertFindAll(Tmp.m_hStore, &Search);
1092 if (RT_SUCCESS(rc))
1093 {
1094 PCRTCRCERTCTX pCertCtx;
1095 while ((pCertCtx = RTCrStoreCertSearchNext(Tmp.m_hStore, &Search)) != NULL)
1096 {
1097 /* Skip selfsigned certs as they're useless as intermediate certs (IIRC). */
1098 if ( pCertCtx->pCert
1099 && !RTCrX509Certificate_IsSelfSigned(pCertCtx->pCert))
1100 {
1101 int rc2 = RTCrStoreCertAddEncoded(this->m_hStore,
1102 pCertCtx->fFlags | RTCRCERTCTX_F_ADD_IF_NOT_FOUND,
1103 pCertCtx->pabEncoded, pCertCtx->cbEncoded, NULL);
1104 if (RT_FAILURE(rc2))
1105 RTMsgWarning("RTCrStoreCertAddEncoded failed for a certificate: %Rrc", rc2);
1106 }
1107 RTCrCertCtxRelease(pCertCtx);
1108 }
1109
1110 int rc2 = RTCrStoreCertSearchDestroy(Tmp.m_hStore, &Search);
1111 AssertRC(rc2);
1112 }
1113 else
1114 {
1115 RTMsgError("RTCrStoreCertFindAll/%d failed: %Rrc", s_aenmStoreIds[i], rc);
1116 fRc = false;
1117 }
1118 }
1119 else
1120 {
1121 RTMsgError("RTCrStoreCreateSnapshotById/%d failed: %Rrc%#RTeim", s_aenmStoreIds[i], rc, &pStaticErrInfo->Core);
1122 fRc = false;
1123 }
1124 }
1125 return fRc;
1126 }
1127
1128};
1129
1130
1131
1132/*********************************************************************************************************************************
1133* Workers. *
1134*********************************************************************************************************************************/
1135
1136
1137/**
1138 * Deletes the structure.
1139 *
1140 * @param pThis The structure to initialize.
1141 */
1142static void SignToolPkcs7_Delete(PSIGNTOOLPKCS7 pThis)
1143{
1144 RTCrPkcs7ContentInfo_Delete(&pThis->ContentInfo);
1145 pThis->pSignedData = NULL;
1146 RTMemFree(pThis->pbBuf);
1147 pThis->pbBuf = NULL;
1148 pThis->cbBuf = 0;
1149 RTMemFree(pThis->pbNewBuf);
1150 pThis->pbNewBuf = NULL;
1151 pThis->cbNewBuf = 0;
1152}
1153
1154
1155/**
1156 * Deletes the structure.
1157 *
1158 * @param pThis The structure to initialize.
1159 */
1160static void SignToolPkcs7Exe_Delete(PSIGNTOOLPKCS7EXE pThis)
1161{
1162 if (pThis->hLdrMod != NIL_RTLDRMOD)
1163 {
1164 int rc2 = RTLdrClose(pThis->hLdrMod);
1165 if (RT_FAILURE(rc2))
1166 RTMsgError("RTLdrClose failed: %Rrc\n", rc2);
1167 pThis->hLdrMod = NIL_RTLDRMOD;
1168 }
1169 SignToolPkcs7_Delete(pThis);
1170}
1171
1172
1173/**
1174 * Decodes the PKCS #7 blob pointed to by pThis->pbBuf.
1175 *
1176 * @returns IPRT status code (error message already shown on failure).
1177 * @param pThis The PKCS\#7 signature to decode.
1178 * @param fCatalog Set if catalog file, clear if executable.
1179 */
1180static int SignToolPkcs7_Decode(PSIGNTOOLPKCS7 pThis, bool fCatalog)
1181{
1182 RTERRINFOSTATIC ErrInfo;
1183 RTASN1CURSORPRIMARY PrimaryCursor;
1184 RTAsn1CursorInitPrimary(&PrimaryCursor, pThis->pbBuf, (uint32_t)pThis->cbBuf, RTErrInfoInitStatic(&ErrInfo),
1185 &g_RTAsn1DefaultAllocator, 0, "WinCert");
1186
1187 int rc = RTCrPkcs7ContentInfo_DecodeAsn1(&PrimaryCursor.Cursor, 0, &pThis->ContentInfo, "CI");
1188 if (RT_SUCCESS(rc))
1189 {
1190 if (RTCrPkcs7ContentInfo_IsSignedData(&pThis->ContentInfo))
1191 {
1192 pThis->pSignedData = pThis->ContentInfo.u.pSignedData;
1193
1194 /*
1195 * Decode the authenticode bits.
1196 */
1197 if (!strcmp(pThis->pSignedData->ContentInfo.ContentType.szObjId, RTCRSPCINDIRECTDATACONTENT_OID))
1198 {
1199 PRTCRSPCINDIRECTDATACONTENT pIndData = pThis->pSignedData->ContentInfo.u.pIndirectDataContent;
1200 Assert(pIndData);
1201
1202 /*
1203 * Check that things add up.
1204 */
1205 rc = RTCrPkcs7SignedData_CheckSanity(pThis->pSignedData,
1206 RTCRPKCS7SIGNEDDATA_SANITY_F_AUTHENTICODE
1207 | RTCRPKCS7SIGNEDDATA_SANITY_F_ONLY_KNOWN_HASH
1208 | RTCRPKCS7SIGNEDDATA_SANITY_F_SIGNING_CERT_PRESENT,
1209 RTErrInfoInitStatic(&ErrInfo), "SD");
1210 if (RT_SUCCESS(rc))
1211 {
1212 rc = RTCrSpcIndirectDataContent_CheckSanityEx(pIndData,
1213 pThis->pSignedData,
1214 RTCRSPCINDIRECTDATACONTENT_SANITY_F_ONLY_KNOWN_HASH,
1215 RTErrInfoInitStatic(&ErrInfo));
1216 if (RT_FAILURE(rc))
1217 RTMsgError("SPC indirect data content sanity check failed for '%s': %Rrc - %s\n",
1218 pThis->pszFilename, rc, ErrInfo.szMsg);
1219 }
1220 else
1221 RTMsgError("PKCS#7 sanity check failed for '%s': %Rrc - %s\n", pThis->pszFilename, rc, ErrInfo.szMsg);
1222 }
1223 else if (!strcmp(pThis->pSignedData->ContentInfo.ContentType.szObjId, RTCR_PKCS7_DATA_OID))
1224 { /* apple code signing */ }
1225 else if (!fCatalog)
1226 RTMsgError("Unexpected the signed content in '%s': %s (expected %s)", pThis->pszFilename,
1227 pThis->pSignedData->ContentInfo.ContentType.szObjId, RTCRSPCINDIRECTDATACONTENT_OID);
1228 }
1229 else
1230 rc = RTMsgErrorRc(VERR_CR_PKCS7_NOT_SIGNED_DATA,
1231 "PKCS#7 content is inside '%s' is not 'signedData': %s\n",
1232 pThis->pszFilename, pThis->ContentInfo.ContentType.szObjId);
1233 }
1234 else
1235 RTMsgError("RTCrPkcs7ContentInfo_DecodeAsn1 failed on '%s': %Rrc - %s\n", pThis->pszFilename, rc, ErrInfo.szMsg);
1236 return rc;
1237}
1238
1239
1240/**
1241 * Reads and decodes PKCS\#7 signature from the given cat file.
1242 *
1243 * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE with error message
1244 * on failure.
1245 * @param pThis The structure to initialize.
1246 * @param pszFilename The catalog (or any other DER PKCS\#7) filename.
1247 * @param cVerbosity The verbosity.
1248 */
1249static RTEXITCODE SignToolPkcs7_InitFromFile(PSIGNTOOLPKCS7 pThis, const char *pszFilename, unsigned cVerbosity)
1250{
1251 /*
1252 * Init the return structure.
1253 */
1254 RT_ZERO(*pThis);
1255 pThis->pszFilename = pszFilename;
1256 pThis->enmType = RTSIGNTOOLFILETYPE_CAT;
1257
1258 /*
1259 * Lazy bird uses RTFileReadAll and duplicates the allocation.
1260 */
1261 void *pvFile;
1262 int rc = RTFileReadAll(pszFilename, &pvFile, &pThis->cbBuf);
1263 if (RT_SUCCESS(rc))
1264 {
1265 pThis->pbBuf = (uint8_t *)RTMemDup(pvFile, pThis->cbBuf);
1266 RTFileReadAllFree(pvFile, pThis->cbBuf);
1267 if (pThis->pbBuf)
1268 {
1269 if (cVerbosity > 2)
1270 RTPrintf("PKCS#7 signature: %u bytes\n", pThis->cbBuf);
1271
1272 /*
1273 * Decode it.
1274 */
1275 rc = SignToolPkcs7_Decode(pThis, true /*fCatalog*/);
1276 if (RT_SUCCESS(rc))
1277 return RTEXITCODE_SUCCESS;
1278 }
1279 else
1280 RTMsgError("Out of memory!");
1281 }
1282 else
1283 RTMsgError("Error reading '%s' into memory: %Rrc", pszFilename, rc);
1284
1285 SignToolPkcs7_Delete(pThis);
1286 return RTEXITCODE_FAILURE;
1287}
1288
1289
1290/**
1291 * Encodes the signature into the SIGNTOOLPKCS7::pbNewBuf and
1292 * SIGNTOOLPKCS7::cbNewBuf members.
1293 *
1294 * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE with error message
1295 * on failure.
1296 * @param pThis The signature to encode.
1297 * @param cVerbosity The verbosity.
1298 */
1299static RTEXITCODE SignToolPkcs7_Encode(PSIGNTOOLPKCS7 pThis, unsigned cVerbosity)
1300{
1301 RTERRINFOSTATIC StaticErrInfo;
1302 PRTASN1CORE pRoot = RTCrPkcs7ContentInfo_GetAsn1Core(&pThis->ContentInfo);
1303 uint32_t cbEncoded;
1304 int rc = RTAsn1EncodePrepare(pRoot, RTASN1ENCODE_F_DER, &cbEncoded, RTErrInfoInitStatic(&StaticErrInfo));
1305 if (RT_SUCCESS(rc))
1306 {
1307 if (cVerbosity >= 4)
1308 RTAsn1Dump(pRoot, 0, 0, RTStrmDumpPrintfV, g_pStdOut);
1309
1310 RTMemFree(pThis->pbNewBuf);
1311 pThis->cbNewBuf = cbEncoded;
1312 pThis->pbNewBuf = (uint8_t *)RTMemAllocZ(cbEncoded);
1313 if (pThis->pbNewBuf)
1314 {
1315 rc = RTAsn1EncodeToBuffer(pRoot, RTASN1ENCODE_F_DER, pThis->pbNewBuf, pThis->cbNewBuf,
1316 RTErrInfoInitStatic(&StaticErrInfo));
1317 if (RT_SUCCESS(rc))
1318 {
1319 if (cVerbosity > 1)
1320 RTMsgInfo("Encoded signature to %u bytes", cbEncoded);
1321 return RTEXITCODE_SUCCESS;
1322 }
1323 RTMsgError("RTAsn1EncodeToBuffer failed: %Rrc", rc);
1324
1325 RTMemFree(pThis->pbNewBuf);
1326 pThis->pbNewBuf = NULL;
1327 }
1328 else
1329 RTMsgError("Failed to allocate %u bytes!", cbEncoded);
1330 }
1331 else
1332 RTMsgError("RTAsn1EncodePrepare failed: %Rrc - %s", rc, StaticErrInfo.szMsg);
1333 return RTEXITCODE_FAILURE;
1334}
1335
1336
1337/**
1338 * Helper that makes sure the UnauthenticatedAttributes are present in the given
1339 * SignerInfo structure.
1340 *
1341 * Call this before trying to modify the array.
1342 *
1343 * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE with error already
1344 * displayed on failure.
1345 * @param pSignerInfo The SignerInfo structure in question.
1346 */
1347static RTEXITCODE SignToolPkcs7_EnsureUnauthenticatedAttributesPresent(PRTCRPKCS7SIGNERINFO pSignerInfo)
1348{
1349 if (pSignerInfo->UnauthenticatedAttributes.cItems == 0)
1350 {
1351 /* HACK ALERT! Invent ASN.1 setters/whatever for members to replace this mess. */
1352
1353 if (pSignerInfo->AuthenticatedAttributes.cItems == 0)
1354 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No authenticated or unauthenticated attributes! Sorry, no can do.");
1355
1356 Assert(pSignerInfo->UnauthenticatedAttributes.SetCore.Asn1Core.uTag == 0);
1357 int rc = RTAsn1SetCore_Init(&pSignerInfo->UnauthenticatedAttributes.SetCore,
1358 pSignerInfo->AuthenticatedAttributes.SetCore.Asn1Core.pOps);
1359 if (RT_FAILURE(rc))
1360 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTAsn1SetCore_Init failed: %Rrc", rc);
1361 pSignerInfo->UnauthenticatedAttributes.SetCore.Asn1Core.uTag = 1;
1362 pSignerInfo->UnauthenticatedAttributes.SetCore.Asn1Core.fClass = ASN1_TAGCLASS_CONTEXT | ASN1_TAGFLAG_CONSTRUCTED;
1363 RTAsn1MemInitArrayAllocation(&pSignerInfo->UnauthenticatedAttributes.Allocation,
1364 pSignerInfo->AuthenticatedAttributes.Allocation.pAllocator,
1365 sizeof(**pSignerInfo->UnauthenticatedAttributes.papItems));
1366 }
1367 return RTEXITCODE_SUCCESS;
1368}
1369
1370
1371/**
1372 * Adds the @a pSrc signature as a nested signature.
1373 *
1374 * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE with error message
1375 * on failure.
1376 * @param pThis The signature to modify.
1377 * @param pSrc The signature to add as nested.
1378 * @param cVerbosity The verbosity.
1379 * @param fPrepend Whether to prepend (true) or append (false) the
1380 * source signature to the nested attribute.
1381 */
1382static RTEXITCODE SignToolPkcs7_AddNestedSignature(PSIGNTOOLPKCS7 pThis, PSIGNTOOLPKCS7 pSrc,
1383 unsigned cVerbosity, bool fPrepend)
1384{
1385 PRTCRPKCS7SIGNERINFO pSignerInfo = pThis->pSignedData->SignerInfos.papItems[0];
1386
1387 /*
1388 * Deal with UnauthenticatedAttributes being absent before trying to append to the array.
1389 */
1390 RTEXITCODE rcExit = SignToolPkcs7_EnsureUnauthenticatedAttributesPresent(pSignerInfo);
1391 if (rcExit != RTEXITCODE_SUCCESS)
1392 return rcExit;
1393
1394 /*
1395 * Find or add an unauthenticated attribute for nested signatures.
1396 */
1397 int rc = VERR_NOT_FOUND;
1398 PRTCRPKCS7ATTRIBUTE pAttr = NULL;
1399 int32_t iPos = pSignerInfo->UnauthenticatedAttributes.cItems;
1400 while (iPos-- > 0)
1401 if (pSignerInfo->UnauthenticatedAttributes.papItems[iPos]->enmType == RTCRPKCS7ATTRIBUTETYPE_MS_NESTED_SIGNATURE)
1402 {
1403 pAttr = pSignerInfo->UnauthenticatedAttributes.papItems[iPos];
1404 rc = VINF_SUCCESS;
1405 break;
1406 }
1407 if (iPos < 0)
1408 {
1409 iPos = RTCrPkcs7Attributes_Append(&pSignerInfo->UnauthenticatedAttributes);
1410 if (iPos >= 0)
1411 {
1412 if (cVerbosity >= 3)
1413 RTMsgInfo("Adding UnauthenticatedAttribute #%u...", iPos);
1414 Assert((uint32_t)iPos < pSignerInfo->UnauthenticatedAttributes.cItems);
1415
1416 pAttr = pSignerInfo->UnauthenticatedAttributes.papItems[iPos];
1417 rc = RTAsn1ObjId_InitFromString(&pAttr->Type, RTCR_PKCS9_ID_MS_NESTED_SIGNATURE, pAttr->Allocation.pAllocator);
1418 if (RT_SUCCESS(rc))
1419 {
1420 /** @todo Generalize the Type + enmType DYN stuff and generate setters. */
1421 Assert(pAttr->enmType == RTCRPKCS7ATTRIBUTETYPE_NOT_PRESENT);
1422 Assert(pAttr->uValues.pContentInfos == NULL);
1423 pAttr->enmType = RTCRPKCS7ATTRIBUTETYPE_MS_NESTED_SIGNATURE;
1424 rc = RTAsn1MemAllocZ(&pAttr->Allocation, (void **)&pAttr->uValues.pContentInfos,
1425 sizeof(*pAttr->uValues.pContentInfos));
1426 if (RT_SUCCESS(rc))
1427 {
1428 rc = RTCrPkcs7SetOfContentInfos_Init(pAttr->uValues.pContentInfos, pAttr->Allocation.pAllocator);
1429 if (!RT_SUCCESS(rc))
1430 RTMsgError("RTCrPkcs7ContentInfos_Init failed: %Rrc", rc);
1431 }
1432 else
1433 RTMsgError("RTAsn1MemAllocZ failed: %Rrc", rc);
1434 }
1435 else
1436 RTMsgError("RTAsn1ObjId_InitFromString failed: %Rrc", rc);
1437 }
1438 else
1439 RTMsgError("RTCrPkcs7Attributes_Append failed: %Rrc", iPos);
1440 }
1441 else if (cVerbosity >= 2)
1442 RTMsgInfo("Found UnauthenticatedAttribute #%u...", iPos);
1443 if (RT_SUCCESS(rc))
1444 {
1445 /*
1446 * Append/prepend the signature.
1447 */
1448 uint32_t iActualPos = UINT32_MAX;
1449 iPos = fPrepend ? 0 : pAttr->uValues.pContentInfos->cItems;
1450 rc = RTCrPkcs7SetOfContentInfos_InsertEx(pAttr->uValues.pContentInfos, iPos, &pSrc->ContentInfo,
1451 pAttr->Allocation.pAllocator, &iActualPos);
1452 if (RT_SUCCESS(rc))
1453 {
1454 if (cVerbosity > 0)
1455 RTMsgInfo("Added nested signature (#%u)", iActualPos);
1456 if (cVerbosity >= 3)
1457 {
1458 RTMsgInfo("SingerInfo dump after change:");
1459 RTAsn1Dump(RTCrPkcs7SignerInfo_GetAsn1Core(pSignerInfo), 0, 2, RTStrmDumpPrintfV, g_pStdOut);
1460 }
1461 return RTEXITCODE_SUCCESS;
1462 }
1463
1464 RTMsgError("RTCrPkcs7ContentInfos_InsertEx failed: %Rrc", rc);
1465 }
1466 return RTEXITCODE_FAILURE;
1467}
1468
1469
1470/**
1471 * Writes the signature to the file.
1472 *
1473 * Caller must have called SignToolPkcs7_Encode() prior to this function.
1474 *
1475 * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE with error
1476 * message on failure.
1477 * @param pThis The file which to write.
1478 * @param cVerbosity The verbosity.
1479 */
1480static RTEXITCODE SignToolPkcs7_WriteSignatureToFile(PSIGNTOOLPKCS7 pThis, const char *pszFilename, unsigned cVerbosity)
1481{
1482 AssertReturn(pThis->cbNewBuf && pThis->pbNewBuf, RTEXITCODE_FAILURE);
1483
1484 /*
1485 * Open+truncate file, write new signature, close. Simple.
1486 */
1487 RTFILE hFile;
1488 int rc = RTFileOpen(&hFile, pszFilename, RTFILE_O_WRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_TRUNCATE | RTFILE_O_DENY_WRITE);
1489 if (RT_SUCCESS(rc))
1490 {
1491 rc = RTFileWrite(hFile, pThis->pbNewBuf, pThis->cbNewBuf, NULL);
1492 if (RT_SUCCESS(rc))
1493 {
1494 rc = RTFileClose(hFile);
1495 if (RT_SUCCESS(rc))
1496 {
1497 if (cVerbosity > 0)
1498 RTMsgInfo("Wrote %u bytes to %s", pThis->cbNewBuf, pszFilename);
1499 return RTEXITCODE_SUCCESS;
1500 }
1501
1502 RTMsgError("RTFileClose failed on %s: %Rrc", pszFilename, rc);
1503 }
1504 else
1505 RTMsgError("Write error on %s: %Rrc", pszFilename, rc);
1506 }
1507 else
1508 RTMsgError("Failed to open %s for writing: %Rrc", pszFilename, rc);
1509 return RTEXITCODE_FAILURE;
1510}
1511
1512
1513
1514/**
1515 * Worker for recursively searching for MS nested signatures and signer infos.
1516 *
1517 * @returns Pointer to the signer info corresponding to @a iReqSignature. NULL
1518 * if not found.
1519 * @param pSignedData The signature to search.
1520 * @param piNextSignature Pointer to the variable keeping track of the next
1521 * signature number.
1522 * @param iReqSignature The request signature number.
1523 * @param ppSignedData Where to return the signature data structure.
1524 * Optional.
1525 */
1526static PRTCRPKCS7SIGNERINFO SignToolPkcs7_FindNestedSignatureByIndexWorker(PRTCRPKCS7SIGNEDDATA pSignedData,
1527 uint32_t *piNextSignature,
1528 uint32_t iReqSignature,
1529 PRTCRPKCS7SIGNEDDATA *ppSignedData)
1530{
1531 for (uint32_t iSignerInfo = 0; iSignerInfo < pSignedData->SignerInfos.cItems; iSignerInfo++)
1532 {
1533 /* Match?*/
1534 PRTCRPKCS7SIGNERINFO pSignerInfo = pSignedData->SignerInfos.papItems[iSignerInfo];
1535 if (*piNextSignature == iReqSignature)
1536 {
1537 if (ppSignedData)
1538 *ppSignedData = pSignedData;
1539 return pSignerInfo;
1540 }
1541 *piNextSignature += 1;
1542
1543 /* Look for nested signatures. */
1544 for (uint32_t iAttrib = 0; iAttrib < pSignerInfo->UnauthenticatedAttributes.cItems; iAttrib++)
1545 if (pSignerInfo->UnauthenticatedAttributes.papItems[iAttrib]->enmType == RTCRPKCS7ATTRIBUTETYPE_MS_NESTED_SIGNATURE)
1546 {
1547 PRTCRPKCS7SETOFCONTENTINFOS pCntInfos;
1548 pCntInfos = pSignerInfo->UnauthenticatedAttributes.papItems[iAttrib]->uValues.pContentInfos;
1549 for (uint32_t iCntInfo = 0; iCntInfo < pCntInfos->cItems; iCntInfo++)
1550 {
1551 PRTCRPKCS7CONTENTINFO pCntInfo = pCntInfos->papItems[iCntInfo];
1552 if (RTCrPkcs7ContentInfo_IsSignedData(pCntInfo))
1553 {
1554 PRTCRPKCS7SIGNERINFO pRet;
1555 pRet = SignToolPkcs7_FindNestedSignatureByIndexWorker(pCntInfo->u.pSignedData, piNextSignature,
1556 iReqSignature, ppSignedData);
1557 if (pRet)
1558 return pRet;
1559 }
1560 }
1561 }
1562 }
1563 return NULL;
1564}
1565
1566
1567/**
1568 * Locates the given nested signature.
1569 *
1570 * @returns Pointer to the signer info corresponding to @a iReqSignature. NULL
1571 * if not found.
1572 * @param pThis The PKCS\#7 structure to search.
1573 * @param iReqSignature The requested signature number.
1574 * @param ppSignedData Where to return the pointer to the signed data that
1575 * the returned signer info belongs to.
1576 *
1577 * @todo Move into SPC or PKCS\#7.
1578 */
1579static PRTCRPKCS7SIGNERINFO SignToolPkcs7_FindNestedSignatureByIndex(PSIGNTOOLPKCS7 pThis, uint32_t iReqSignature,
1580 PRTCRPKCS7SIGNEDDATA *ppSignedData)
1581{
1582 uint32_t iNextSignature = 0;
1583 return SignToolPkcs7_FindNestedSignatureByIndexWorker(pThis->pSignedData, &iNextSignature, iReqSignature, ppSignedData);
1584}
1585
1586
1587/**
1588 * Count the number of signatures, nested or otherwise.
1589 *
1590 * @returns Number of signatures.
1591 * @param pThis The PKCS\#7 structure to search.
1592 *
1593 * @todo Move into SPC or PKCS\#7.
1594 */
1595static uint32_t SignToolPkcs7_CountSignatures(PSIGNTOOLPKCS7 pThis)
1596{
1597 uint32_t iNextSignature = 0;
1598 PRTCRPKCS7SIGNEDDATA pSignedData = NULL;
1599 SignToolPkcs7_FindNestedSignatureByIndexWorker(pThis->pSignedData, &iNextSignature, UINT32_MAX / 2, &pSignedData);
1600 return iNextSignature;
1601}
1602
1603
1604/**
1605 * Reads and decodes PKCS\#7 signature from the given executable, if it has one.
1606 *
1607 * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE with error message
1608 * on failure.
1609 * @param pThis The structure to initialize.
1610 * @param pszFilename The executable filename.
1611 * @param cVerbosity The verbosity.
1612 * @param enmLdrArch For FAT binaries.
1613 * @param fAllowUnsigned Whether to allow unsigned binaries.
1614 */
1615static RTEXITCODE SignToolPkcs7Exe_InitFromFile(PSIGNTOOLPKCS7EXE pThis, const char *pszFilename, unsigned cVerbosity,
1616 RTLDRARCH enmLdrArch = RTLDRARCH_WHATEVER, bool fAllowUnsigned = false)
1617{
1618 /*
1619 * Init the return structure.
1620 */
1621 RT_ZERO(*pThis);
1622 pThis->hLdrMod = NIL_RTLDRMOD;
1623 pThis->pszFilename = pszFilename;
1624 pThis->enmType = RTSIGNTOOLFILETYPE_EXE;
1625
1626 /*
1627 * Open the image and check if it's signed.
1628 */
1629 int rc = RTLdrOpen(pszFilename, RTLDR_O_FOR_VALIDATION, enmLdrArch, &pThis->hLdrMod);
1630 if (RT_SUCCESS(rc))
1631 {
1632 bool fIsSigned = false;
1633 rc = RTLdrQueryProp(pThis->hLdrMod, RTLDRPROP_IS_SIGNED, &fIsSigned, sizeof(fIsSigned));
1634 if (RT_SUCCESS(rc) && fIsSigned)
1635 {
1636 /*
1637 * Query the PKCS#7 data (assuming M$ style signing) and hand it to a worker.
1638 */
1639 size_t cbActual = 0;
1640#ifdef DEBUG
1641 size_t cbBuf = 64;
1642#else
1643 size_t cbBuf = _512K;
1644#endif
1645 void *pvBuf = RTMemAllocZ(cbBuf);
1646 if (pvBuf)
1647 {
1648 rc = RTLdrQueryPropEx(pThis->hLdrMod, RTLDRPROP_PKCS7_SIGNED_DATA, NULL /*pvBits*/, pvBuf, cbBuf, &cbActual);
1649 if (rc == VERR_BUFFER_OVERFLOW)
1650 {
1651 RTMemFree(pvBuf);
1652 cbBuf = cbActual;
1653 pvBuf = RTMemAllocZ(cbActual);
1654 if (pvBuf)
1655 rc = RTLdrQueryPropEx(pThis->hLdrMod, RTLDRPROP_PKCS7_SIGNED_DATA, NULL /*pvBits*/,
1656 pvBuf, cbBuf, &cbActual);
1657 else
1658 rc = VERR_NO_MEMORY;
1659 }
1660 }
1661 else
1662 rc = VERR_NO_MEMORY;
1663
1664 pThis->pbBuf = (uint8_t *)pvBuf;
1665 pThis->cbBuf = cbActual;
1666 if (RT_SUCCESS(rc))
1667 {
1668 if (cVerbosity > 2)
1669 RTPrintf("PKCS#7 signature: %u bytes\n", cbActual);
1670 if (cVerbosity > 3)
1671 RTPrintf("%.*Rhxd\n", cbActual, pvBuf);
1672
1673 /*
1674 * Decode it.
1675 */
1676 rc = SignToolPkcs7_Decode(pThis, false /*fCatalog*/);
1677 if (RT_SUCCESS(rc))
1678 return RTEXITCODE_SUCCESS;
1679 }
1680 else
1681 RTMsgError("RTLdrQueryPropEx/RTLDRPROP_PKCS7_SIGNED_DATA failed on '%s': %Rrc\n", pszFilename, rc);
1682 }
1683 else if (RT_SUCCESS(rc))
1684 {
1685 if (!fAllowUnsigned || cVerbosity >= 2)
1686 RTMsgInfo("'%s': not signed\n", pszFilename);
1687 if (fAllowUnsigned)
1688 return RTEXITCODE_SUCCESS;
1689 }
1690 else
1691 RTMsgError("RTLdrQueryProp/RTLDRPROP_IS_SIGNED failed on '%s': %Rrc\n", pszFilename, rc);
1692 }
1693 else
1694 RTMsgError("Error opening executable image '%s': %Rrc", pszFilename, rc);
1695
1696 SignToolPkcs7Exe_Delete(pThis);
1697 return RTEXITCODE_FAILURE;
1698}
1699
1700
1701/**
1702 * Calculates the checksum of an executable.
1703 *
1704 * @returns Success indicator (errors are reported)
1705 * @param pThis The exe file to checksum.
1706 * @param hFile The file handle.
1707 * @param puCheckSum Where to return the checksum.
1708 */
1709static bool SignToolPkcs7Exe_CalcPeCheckSum(PSIGNTOOLPKCS7EXE pThis, RTFILE hFile, uint32_t *puCheckSum)
1710{
1711#ifdef RT_OS_WINDOWS
1712 /*
1713 * Try use IMAGEHLP!MapFileAndCheckSumW first.
1714 */
1715 PRTUTF16 pwszPath;
1716 int rc = RTStrToUtf16(pThis->pszFilename, &pwszPath);
1717 if (RT_SUCCESS(rc))
1718 {
1719 decltype(MapFileAndCheckSumW) *pfnMapFileAndCheckSumW;
1720 pfnMapFileAndCheckSumW = (decltype(MapFileAndCheckSumW) *)RTLdrGetSystemSymbol("IMAGEHLP.DLL", "MapFileAndCheckSumW");
1721 if (pfnMapFileAndCheckSumW)
1722 {
1723 DWORD uOldSum = UINT32_MAX;
1724 DWORD uCheckSum = UINT32_MAX;
1725 DWORD dwRc = pfnMapFileAndCheckSumW(pwszPath, &uOldSum, &uCheckSum);
1726 if (dwRc == CHECKSUM_SUCCESS)
1727 {
1728 *puCheckSum = uCheckSum;
1729 return true;
1730 }
1731 }
1732 }
1733#endif
1734
1735 RT_NOREF(pThis, hFile, puCheckSum);
1736 RTMsgError("Implement check sum calcuation fallback!");
1737 return false;
1738}
1739
1740
1741/**
1742 * Writes the signature to the file.
1743 *
1744 * This has the side-effect of closing the hLdrMod member. So, it can only be
1745 * called once!
1746 *
1747 * Caller must have called SignToolPkcs7_Encode() prior to this function.
1748 *
1749 * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE with error
1750 * message on failure.
1751 * @param pThis The file which to write.
1752 * @param cVerbosity The verbosity.
1753 */
1754static RTEXITCODE SignToolPkcs7Exe_WriteSignatureToFile(PSIGNTOOLPKCS7EXE pThis, unsigned cVerbosity)
1755{
1756 AssertReturn(pThis->cbNewBuf && pThis->pbNewBuf, RTEXITCODE_FAILURE);
1757
1758 /*
1759 * Get the file header offset and arch before closing the destination handle.
1760 */
1761 uint32_t offNtHdrs;
1762 int rc = RTLdrQueryProp(pThis->hLdrMod, RTLDRPROP_FILE_OFF_HEADER, &offNtHdrs, sizeof(offNtHdrs));
1763 if (RT_SUCCESS(rc))
1764 {
1765 RTLDRARCH enmLdrArch = RTLdrGetArch(pThis->hLdrMod);
1766 if (enmLdrArch != RTLDRARCH_INVALID)
1767 {
1768 RTLdrClose(pThis->hLdrMod);
1769 pThis->hLdrMod = NIL_RTLDRMOD;
1770 unsigned cbNtHdrs = 0;
1771 switch (enmLdrArch)
1772 {
1773 case RTLDRARCH_AMD64:
1774 cbNtHdrs = sizeof(IMAGE_NT_HEADERS64);
1775 break;
1776 case RTLDRARCH_X86_32:
1777 cbNtHdrs = sizeof(IMAGE_NT_HEADERS32);
1778 break;
1779 default:
1780 RTMsgError("Unknown image arch: %d", enmLdrArch);
1781 }
1782 if (cbNtHdrs > 0)
1783 {
1784 if (cVerbosity > 0)
1785 RTMsgInfo("offNtHdrs=%#x cbNtHdrs=%u\n", offNtHdrs, cbNtHdrs);
1786
1787 /*
1788 * Open the executable file for writing.
1789 */
1790 RTFILE hFile;
1791 rc = RTFileOpen(&hFile, pThis->pszFilename, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1792 if (RT_SUCCESS(rc))
1793 {
1794 /* Read the file header and locate the security directory entry. */
1795 union
1796 {
1797 IMAGE_NT_HEADERS32 NtHdrs32;
1798 IMAGE_NT_HEADERS64 NtHdrs64;
1799 } uBuf;
1800 PIMAGE_DATA_DIRECTORY pSecDir = cbNtHdrs == sizeof(IMAGE_NT_HEADERS64)
1801 ? &uBuf.NtHdrs64.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY]
1802 : &uBuf.NtHdrs32.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY];
1803
1804 rc = RTFileReadAt(hFile, offNtHdrs, &uBuf, cbNtHdrs, NULL);
1805 if ( RT_SUCCESS(rc)
1806 && uBuf.NtHdrs32.Signature == IMAGE_NT_SIGNATURE)
1807 {
1808 /*
1809 * Drop any old signature by truncating the file.
1810 */
1811 if ( pSecDir->Size > 8
1812 && pSecDir->VirtualAddress > offNtHdrs + sizeof(IMAGE_NT_HEADERS32))
1813 {
1814 rc = RTFileSetSize(hFile, pSecDir->VirtualAddress);
1815 if (RT_FAILURE(rc))
1816 RTMsgError("Error truncating file to %#x bytes: %Rrc", pSecDir->VirtualAddress, rc);
1817 }
1818 else if (pSecDir->Size != 0 && pSecDir->VirtualAddress == 0)
1819 rc = RTMsgErrorRc(VERR_BAD_EXE_FORMAT, "Bad security directory entry: VA=%#x Size=%#x",
1820 pSecDir->VirtualAddress, pSecDir->Size);
1821 if (RT_SUCCESS(rc))
1822 {
1823 /*
1824 * Pad the file with zero up to a WIN_CERTIFICATE_ALIGNMENT boundary.
1825 *
1826 * Since the hash algorithm hashes everything up to the signature data,
1827 * zero padding included, the alignment we do here must match the alignment
1828 * padding that done while calculating the hash.
1829 */
1830 uint32_t const cbWinCert = RT_UOFFSETOF(WIN_CERTIFICATE, bCertificate);
1831 uint64_t offCur = 0;
1832 rc = RTFileQuerySize(hFile, &offCur);
1833 if ( RT_SUCCESS(rc)
1834 && offCur < _2G)
1835 {
1836 if (offCur != RT_ALIGN_64(offCur, WIN_CERTIFICATE_ALIGNMENT))
1837 {
1838 uint32_t const cbNeeded = (uint32_t)(RT_ALIGN_64(offCur, WIN_CERTIFICATE_ALIGNMENT) - offCur);
1839 rc = RTFileWriteAt(hFile, offCur, g_abRTZero4K, cbNeeded, NULL);
1840 if (RT_SUCCESS(rc))
1841 offCur += cbNeeded;
1842 }
1843 if (RT_SUCCESS(rc))
1844 {
1845 /*
1846 * Write the header followed by the signature data.
1847 */
1848 uint32_t const cbZeroPad = (uint32_t)(RT_ALIGN_Z(pThis->cbNewBuf, 8) - pThis->cbNewBuf);
1849 pSecDir->VirtualAddress = (uint32_t)offCur;
1850 pSecDir->Size = cbWinCert + (uint32_t)pThis->cbNewBuf + cbZeroPad;
1851 if (cVerbosity >= 2)
1852 RTMsgInfo("Writing %u (%#x) bytes of signature at %#x (%u).\n",
1853 pSecDir->Size, pSecDir->Size, pSecDir->VirtualAddress, pSecDir->VirtualAddress);
1854
1855 WIN_CERTIFICATE WinCert;
1856 WinCert.dwLength = pSecDir->Size;
1857 WinCert.wRevision = WIN_CERT_REVISION_2_0;
1858 WinCert.wCertificateType = WIN_CERT_TYPE_PKCS_SIGNED_DATA;
1859
1860 rc = RTFileWriteAt(hFile, offCur, &WinCert, cbWinCert, NULL);
1861 if (RT_SUCCESS(rc))
1862 {
1863 offCur += cbWinCert;
1864 rc = RTFileWriteAt(hFile, offCur, pThis->pbNewBuf, pThis->cbNewBuf, NULL);
1865 }
1866 if (RT_SUCCESS(rc) && cbZeroPad)
1867 {
1868 offCur += pThis->cbNewBuf;
1869 rc = RTFileWriteAt(hFile, offCur, g_abRTZero4K, cbZeroPad, NULL);
1870 }
1871 if (RT_SUCCESS(rc))
1872 {
1873 /*
1874 * Reset the checksum (sec dir updated already) and rewrite the header.
1875 */
1876 uBuf.NtHdrs32.OptionalHeader.CheckSum = 0;
1877 offCur = offNtHdrs;
1878 rc = RTFileWriteAt(hFile, offNtHdrs, &uBuf, cbNtHdrs, NULL);
1879 if (RT_SUCCESS(rc))
1880 rc = RTFileFlush(hFile);
1881 if (RT_SUCCESS(rc))
1882 {
1883 /*
1884 * Calc checksum and write out the header again.
1885 */
1886 uint32_t uCheckSum = UINT32_MAX;
1887 if (SignToolPkcs7Exe_CalcPeCheckSum(pThis, hFile, &uCheckSum))
1888 {
1889 uBuf.NtHdrs32.OptionalHeader.CheckSum = uCheckSum;
1890 rc = RTFileWriteAt(hFile, offNtHdrs, &uBuf, cbNtHdrs, NULL);
1891 if (RT_SUCCESS(rc))
1892 rc = RTFileFlush(hFile);
1893 if (RT_SUCCESS(rc))
1894 {
1895 rc = RTFileClose(hFile);
1896 if (RT_SUCCESS(rc))
1897 return RTEXITCODE_SUCCESS;
1898 RTMsgError("RTFileClose failed: %Rrc\n", rc);
1899 return RTEXITCODE_FAILURE;
1900 }
1901 }
1902 }
1903 }
1904 }
1905 if (RT_FAILURE(rc))
1906 RTMsgError("Write error at %#RX64: %Rrc", offCur, rc);
1907 }
1908 else if (RT_SUCCESS(rc))
1909 RTMsgError("File to big: %'RU64 bytes", offCur);
1910 else
1911 RTMsgError("RTFileQuerySize failed: %Rrc", rc);
1912 }
1913 }
1914 else if (RT_SUCCESS(rc))
1915 RTMsgError("Not NT executable header!");
1916 else
1917 RTMsgError("Error reading NT headers (%#x bytes) at %#x: %Rrc", cbNtHdrs, offNtHdrs, rc);
1918 RTFileClose(hFile);
1919 }
1920 else
1921 RTMsgError("Failed to open '%s' for writing: %Rrc", pThis->pszFilename, rc);
1922 }
1923 }
1924 else
1925 RTMsgError("RTLdrGetArch failed!");
1926 }
1927 else
1928 RTMsgError("RTLdrQueryProp/RTLDRPROP_FILE_OFF_HEADER failed: %Rrc", rc);
1929 return RTEXITCODE_FAILURE;
1930}
1931
1932#ifndef IPRT_SIGNTOOL_NO_SIGNING
1933
1934static PRTCRPKCS7ATTRIBUTE SignToolPkcs7_AuthAttribAppend(PRTCRPKCS7ATTRIBUTES pAuthAttribs)
1935{
1936 int32_t iPos = RTCrPkcs7Attributes_Append(pAuthAttribs);
1937 if (iPos >= 0)
1938 return pAuthAttribs->papItems[iPos];
1939 RTMsgError("RTCrPkcs7Attributes_Append failed: %Rrc", iPos);
1940 return NULL;
1941}
1942
1943
1944static RTEXITCODE SignToolPkcs7_AuthAttribsAddSigningTime(PRTCRPKCS7ATTRIBUTES pAuthAttribs, RTTIMESPEC SigningTime)
1945{
1946 /*
1947 * Signing time. For the old-style timestamps, Symantec used ASN.1 UTC TIME.
1948 * start -vv vv=ASN1_TAG_UTC_TIME
1949 * 00000187d6a65fd0/23b0: 0d 01 09 05 31 0f 17 0d-31 36 31 30 30 35 30 37 ....1...16100507
1950 * 00000187d6a65fe0/23c0: 35 30 33 30 5a 30 23 06-09 2a 86 48 86 f7 0d 01 5030Z0#..*.H....
1951 * ^^- end 2016-10-05T07:50:30.000000000Z (161005075030Z)
1952 */
1953 PRTCRPKCS7ATTRIBUTE pAttr = SignToolPkcs7_AuthAttribAppend(pAuthAttribs);
1954 if (!pAttr)
1955 return RTEXITCODE_FAILURE;
1956
1957 int rc = RTCrPkcs7Attribute_SetSigningTime(pAttr, NULL, pAuthAttribs->Allocation.pAllocator);
1958 if (RT_FAILURE(rc))
1959 return RTMsgErrorExitFailure("RTCrPkcs7Attribute_SetSigningTime failed: %Rrc", rc);
1960
1961 /* Create the timestamp. */
1962 int32_t iPos = RTAsn1SetOfTimes_Append(pAttr->uValues.pSigningTime);
1963 if (iPos < 0)
1964 return RTMsgErrorExitFailure("RTAsn1SetOfTimes_Append failed: %Rrc", iPos);
1965
1966 PRTASN1TIME pTime = pAttr->uValues.pSigningTime->papItems[iPos];
1967 rc = RTAsn1Time_SetTimeSpec(pTime, pAttr->Allocation.pAllocator, &SigningTime);
1968 if (RT_FAILURE(rc))
1969 return RTMsgErrorExitFailure("RTAsn1Time_SetTimeSpec failed: %Rrc", rc);
1970
1971 return RTEXITCODE_SUCCESS;
1972}
1973
1974
1975static RTEXITCODE SignToolPkcs7_AuthAttribsAddSpcOpusInfo(PRTCRPKCS7ATTRIBUTES pAuthAttribs, void *pvInfo)
1976{
1977 /** @todo The OpusInfo is a structure with an optional SpcString and an
1978 * optional SpcLink (url). The two attributes can be set using the /d and /du
1979 * options of MS signtool.exe, I think. We shouldn't be using them atm. */
1980
1981 PRTCRPKCS7ATTRIBUTE pAttr = SignToolPkcs7_AuthAttribAppend(pAuthAttribs);
1982 if (!pAttr)
1983 return RTEXITCODE_FAILURE;
1984
1985 int rc = RTCrPkcs7Attribute_SetMsStatementType(pAttr, NULL, pAuthAttribs->Allocation.pAllocator);
1986 if (RT_FAILURE(rc))
1987 return RTMsgErrorExitFailure("RTCrPkcs7Attribute_SetMsStatementType failed: %Rrc", rc);
1988
1989 /* Override the ID. */
1990 rc = RTAsn1ObjId_SetFromString(&pAttr->Type, RTCR_PKCS9_ID_MS_SP_OPUS_INFO, pAuthAttribs->Allocation.pAllocator);
1991 if (RT_FAILURE(rc))
1992 return RTMsgErrorExitFailure("RTAsn1ObjId_SetFromString failed: %Rrc", rc);
1993
1994 /* Add attribute value entry. */
1995 int32_t iPos = RTAsn1SetOfObjIdSeqs_Append(pAttr->uValues.pObjIdSeqs);
1996 if (iPos < 0)
1997 return RTMsgErrorExitFailure("RTAsn1SetOfObjIdSeqs_Append failed: %Rrc", iPos);
1998
1999 RT_NOREF(pvInfo); Assert(!pvInfo);
2000 return RTEXITCODE_SUCCESS;
2001}
2002
2003
2004static RTEXITCODE SignToolPkcs7_AuthAttribsAddMsStatementType(PRTCRPKCS7ATTRIBUTES pAuthAttribs, const char *pszTypeId)
2005{
2006 PRTCRPKCS7ATTRIBUTE pAttr = SignToolPkcs7_AuthAttribAppend(pAuthAttribs);
2007 if (!pAttr)
2008 return RTEXITCODE_FAILURE;
2009
2010 int rc = RTCrPkcs7Attribute_SetMsStatementType(pAttr, NULL, pAuthAttribs->Allocation.pAllocator);
2011 if (RT_FAILURE(rc))
2012 return RTMsgErrorExitFailure("RTCrPkcs7Attribute_SetMsStatementType failed: %Rrc", rc);
2013
2014 /* Add attribute value entry. */
2015 int32_t iPos = RTAsn1SetOfObjIdSeqs_Append(pAttr->uValues.pObjIdSeqs);
2016 if (iPos < 0)
2017 return RTMsgErrorExitFailure("RTAsn1SetOfObjIdSeqs_Append failed: %Rrc", iPos);
2018 PRTASN1SEQOFOBJIDS pSeqObjIds = pAttr->uValues.pObjIdSeqs->papItems[iPos];
2019
2020 /* Add a object id to the value. */
2021 RTASN1OBJID ObjIdValue;
2022 rc = RTAsn1ObjId_InitFromString(&ObjIdValue, pszTypeId, &g_RTAsn1DefaultAllocator);
2023 if (RT_FAILURE(rc))
2024 return RTMsgErrorExitFailure("RTAsn1ObjId_InitFromString/%s failed: %Rrc", pszTypeId, rc);
2025
2026 rc = RTAsn1SeqOfObjIds_InsertEx(pSeqObjIds, 0 /*iPos*/, &ObjIdValue, &g_RTAsn1DefaultAllocator, NULL);
2027 RTAsn1ObjId_Delete(&ObjIdValue);
2028 if (RT_FAILURE(rc))
2029 return RTMsgErrorExitFailure("RTAsn1SeqOfObjIds_InsertEx failed: %Rrc", rc);
2030
2031 return RTEXITCODE_SUCCESS;
2032}
2033
2034
2035static RTEXITCODE SignToolPkcs7_AuthAttribsAddContentType(PRTCRPKCS7ATTRIBUTES pAuthAttribs, const char *pszContentTypeId)
2036{
2037 PRTCRPKCS7ATTRIBUTE pAttr = SignToolPkcs7_AuthAttribAppend(pAuthAttribs);
2038 if (!pAttr)
2039 return RTEXITCODE_FAILURE;
2040
2041 int rc = RTCrPkcs7Attribute_SetContentType(pAttr, NULL, pAuthAttribs->Allocation.pAllocator);
2042 if (RT_FAILURE(rc))
2043 return RTMsgErrorExitFailure("RTCrPkcs7Attribute_SetContentType failed: %Rrc", rc);
2044
2045 /* Add a object id to the value. */
2046 RTASN1OBJID ObjIdValue;
2047 rc = RTAsn1ObjId_InitFromString(&ObjIdValue, pszContentTypeId, pAuthAttribs->Allocation.pAllocator);
2048 if (RT_FAILURE(rc))
2049 return RTMsgErrorExitFailure("RTAsn1ObjId_InitFromString/%s failed: %Rrc", pszContentTypeId, rc);
2050
2051 rc = RTAsn1SetOfObjIds_InsertEx(pAttr->uValues.pObjIds, 0 /*iPos*/, &ObjIdValue, pAuthAttribs->Allocation.pAllocator, NULL);
2052 RTAsn1ObjId_Delete(&ObjIdValue);
2053 if (RT_FAILURE(rc))
2054 return RTMsgErrorExitFailure("RTAsn1SetOfObjIds_InsertEx failed: %Rrc", rc);
2055
2056 return RTEXITCODE_SUCCESS;
2057}
2058
2059
2060static RTEXITCODE SignToolPkcs7_AddAuthAttribsForTimestamp(PRTCRPKCS7ATTRIBUTES pAuthAttribs, TIMESTAMPTYPE enmTimestampType,
2061 RTTIMESPEC SigningTime, PCRTCRX509CERTIFICATE pTimestampCert)
2062{
2063 /*
2064 * Add content type.
2065 */
2066 RTEXITCODE rcExit = SignToolPkcs7_AuthAttribsAddContentType(pAuthAttribs,
2067 enmTimestampType == kTimestampType_Old
2068 ? RTCR_PKCS7_DATA_OID : RTCRTSPTSTINFO_OID);
2069 if (rcExit != RTEXITCODE_SUCCESS)
2070 return rcExit;
2071
2072 /*
2073 * Add signing time.
2074 */
2075 rcExit = SignToolPkcs7_AuthAttribsAddSigningTime(pAuthAttribs, SigningTime);
2076 if (rcExit != RTEXITCODE_SUCCESS)
2077 return rcExit;
2078
2079 /*
2080 * More later if we want to support fTimestampTypeOld = false perhaps?
2081 */
2082 Assert(enmTimestampType == kTimestampType_Old);
2083 RT_NOREF(pTimestampCert);
2084
2085 return RTEXITCODE_SUCCESS;
2086}
2087
2088
2089static RTEXITCODE SignToolPkcs7_AddAuthAttribsForImageOrCatSignature(PRTCRPKCS7ATTRIBUTES pAuthAttribs, RTTIMESPEC SigningTime,
2090 bool fNoSigningTime, const char *pszContentTypeId)
2091{
2092 /*
2093 * Add SpcOpusInfo. No attribute values.
2094 * SEQ start -vv vv- Type ObjId
2095 * 1c60: 0e 03 02 1a 05 00 a0 70-30 10 06 0a 2b 06 01 04 .......p0...+...
2096 * 1c70: 01 82 37 02 01 0c 31 02-30 00 30 19 06 09 2a 86 ..7...1.0.0...*.
2097 * Set Of -^^ ^^- Empty Sequence.
2098 */
2099 RTEXITCODE rcExit = SignToolPkcs7_AuthAttribsAddSpcOpusInfo(pAuthAttribs, NULL /*pvInfo - none*/);
2100 if (rcExit != RTEXITCODE_SUCCESS)
2101 return rcExit;
2102
2103 /*
2104 * Add ContentType = Ms-SpcIndirectDataContext?
2105 * SEQ start -vv vv- Type ObjId
2106 * 1c70: 01 82 37 02 01 0c 31 02-30 00 30 19 06 09 2a 86 ..7...1.0.0...*.
2107 * 1c80: 48 86 f7 0d 01 09 03 31-0c 06 0a 2b 06 01 04 01 H......1...+....
2108 * 1c90: 82 37 02 01 04 ^^- ^^- ObjId
2109 * ^- Set Of
2110 */
2111 rcExit = SignToolPkcs7_AuthAttribsAddContentType(pAuthAttribs, pszContentTypeId);
2112 if (rcExit != RTEXITCODE_SUCCESS)
2113 return rcExit;
2114
2115 /*
2116 * Add Ms-SpcStatementType = Ms-SpcIndividualCodeSigning.
2117 * SEQ start -vv vv- Type ObjId
2118 * 1c90: 82 37 02 01 04 30 1c 06-0a 2b 06 01 04 01 82 37 .7...0...+.....7
2119 * 1ca0: 02 01 0b 31 0e 30 0c 06-0a 2b 06 01 04 01 82 37 ...1.0...+.....7
2120 * 1cb0: 02 01 15 ^^ ^^ ^^- ObjId
2121 * Set Of -^^ ^^- Sequence Of
2122 */
2123 rcExit = SignToolPkcs7_AuthAttribsAddMsStatementType(pAuthAttribs, RTCRSPC_STMT_TYPE_INDIVIDUAL_CODE_SIGNING);
2124 if (rcExit != RTEXITCODE_SUCCESS)
2125 return rcExit;
2126
2127 /*
2128 * Add signing time. We add this, even if signtool.exe, since OpenSSL will always do it otherwise.
2129 */
2130 if (!fNoSigningTime) /** @todo requires disabling the code in do_pkcs7_signed_attrib that adds it when absent */
2131 {
2132 rcExit = SignToolPkcs7_AuthAttribsAddSigningTime(pAuthAttribs, SigningTime);
2133 if (rcExit != RTEXITCODE_SUCCESS)
2134 return rcExit;
2135 }
2136
2137 /** @todo more? Some certificate stuff? */
2138
2139 return RTEXITCODE_SUCCESS;
2140}
2141
2142
2143static RTEXITCODE SignToolPkcs7_AppendCounterSignature(PRTCRPKCS7SIGNERINFO pSignerInfo,
2144 PCRTCRPKCS7SIGNERINFO pCounterSignerInfo, unsigned cVerbosity)
2145{
2146 /* Make sure the UnauthenticatedAttributes member is there. */
2147 RTEXITCODE rcExit = SignToolPkcs7_EnsureUnauthenticatedAttributesPresent(pSignerInfo);
2148 if (rcExit != RTEXITCODE_SUCCESS)
2149 return rcExit;
2150
2151#if 0 /* Windows won't accept multiple timestamps either way. Doing the latter as it makes more sense to me... */
2152 /* Append an entry to UnauthenticatedAttributes. */
2153 uint32_t iPos;
2154 int rc = RTCrPkcs7Attributes_InsertEx(&pSignerInfo->UnauthenticatedAttributes, 0 /*iPosition*/, NULL /*pToClone*/,
2155 &g_RTAsn1DefaultAllocator, &iPos);
2156 if (RT_FAILURE(rc))
2157 return RTMsgErrorExitFailure("RTCrPkcs7Attributes_Append failed: %Rrc", rc);
2158 Assert(iPos < pSignerInfo->UnauthenticatedAttributes.cItems); Assert(iPos == 0);
2159 PRTCRPKCS7ATTRIBUTE pAttr = pSignerInfo->UnauthenticatedAttributes.papItems[iPos];
2160
2161 if (cVerbosity >= 2)
2162 RTMsgInfo("Adding UnauthenticatedAttribute #%u...", iPos);
2163#else
2164 /* Look up the counter signature attribute, create one if needed. */
2165 int rc;
2166 uint32_t iPos = 0;
2167 PRTCRPKCS7ATTRIBUTE pAttr = NULL;
2168 for (; iPos < pSignerInfo->UnauthenticatedAttributes.cItems; iPos++)
2169 {
2170 pAttr = pSignerInfo->UnauthenticatedAttributes.papItems[iPos];
2171 if (pAttr->enmType == RTCRPKCS7ATTRIBUTETYPE_COUNTER_SIGNATURES)
2172 break;
2173 }
2174 if (iPos >= pSignerInfo->UnauthenticatedAttributes.cItems)
2175 {
2176 /* Append a new entry to UnauthenticatedAttributes. */
2177 rc = RTCrPkcs7Attributes_InsertEx(&pSignerInfo->UnauthenticatedAttributes, 0 /*iPosition*/, NULL /*pToClone*/,
2178 &g_RTAsn1DefaultAllocator, &iPos);
2179 if (RT_FAILURE(rc))
2180 return RTMsgErrorExitFailure("RTCrPkcs7Attributes_Append failed: %Rrc", rc);
2181 Assert(iPos < pSignerInfo->UnauthenticatedAttributes.cItems); Assert(iPos == 0);
2182 pAttr = pSignerInfo->UnauthenticatedAttributes.papItems[iPos];
2183
2184 /* Create the attrib and its sub-set of counter signatures. */
2185 rc = RTCrPkcs7Attribute_SetCounterSignatures(pAttr, NULL, pAttr->Allocation.pAllocator);
2186 if (RT_FAILURE(rc))
2187 return RTMsgErrorExitFailure("RTCrPkcs7Attribute_SetCounterSignatures failed: %Rrc", rc);
2188 }
2189
2190 if (cVerbosity >= 2)
2191 RTMsgInfo("Adding UnauthenticatedAttribute #%u.%u...", iPos, pAttr->uValues.pCounterSignatures->cItems);
2192
2193#endif
2194
2195 /* Insert the counter signature. */
2196 rc = RTCrPkcs7SignerInfos_InsertEx(pAttr->uValues.pCounterSignatures, pAttr->uValues.pCounterSignatures->cItems /*iPosition*/,
2197 pCounterSignerInfo, pAttr->Allocation.pAllocator, NULL);
2198 if (RT_FAILURE(rc))
2199 return RTMsgErrorExitFailure("RTCrPkcs7SignerInfos_InsertEx failed: %Rrc", rc);
2200
2201 return RTEXITCODE_SUCCESS;
2202}
2203
2204
2205static RTEXITCODE SignToolPkcs7_AppendCertificate(PRTCRPKCS7SIGNEDDATA pSignedData, PCRTCRX509CERTIFICATE pCertToAppend)
2206{
2207 if (pSignedData->Certificates.cItems == 0 && !RTCrPkcs7SetOfCerts_IsPresent(&pSignedData->Certificates))
2208 return RTMsgErrorExitFailure("PKCS#7 signature includes no certificates! Didn't expect that");
2209
2210 /* Already there? */
2211 PCRTCRX509CERTIFICATE pExisting
2212 = RTCrPkcs7SetOfCerts_FindX509ByIssuerAndSerialNumber(&pSignedData->Certificates, &pCertToAppend->TbsCertificate.Issuer,
2213 &pCertToAppend->TbsCertificate.SerialNumber);
2214 if (!pExisting || RTCrX509Certificate_Compare(pExisting, pCertToAppend) != 0)
2215 {
2216 /* Prepend a RTCRPKCS7CERT entry. */
2217 uint32_t iPos;
2218 int rc = RTCrPkcs7SetOfCerts_InsertEx(&pSignedData->Certificates, 0 /*iPosition*/, NULL /*pToClone*/,
2219 &g_RTAsn1DefaultAllocator, &iPos);
2220 if (RT_FAILURE(rc))
2221 return RTMsgErrorExitFailure("RTCrPkcs7SetOfCerts_Append failed: %Rrc", rc);
2222 PRTCRPKCS7CERT pCertEntry = pSignedData->Certificates.papItems[iPos];
2223
2224 /* Set (clone) the certificate. */
2225 rc = RTCrPkcs7Cert_SetX509Cert(pCertEntry, pCertToAppend, pCertEntry->Allocation.pAllocator);
2226 if (RT_FAILURE(rc))
2227 return RTMsgErrorExitFailure("RTCrPkcs7Cert_X509Cert failed: %Rrc", rc);
2228 }
2229 return RTEXITCODE_SUCCESS;
2230}
2231
2232#ifdef RT_OS_WINDOWS
2233
2234static PCRTUTF16 GetBCryptNameFromCrDigest(RTCRDIGEST hDigest)
2235{
2236 switch (RTCrDigestGetType(hDigest))
2237 {
2238 case RTDIGESTTYPE_MD2: return BCRYPT_MD2_ALGORITHM;
2239 case RTDIGESTTYPE_MD4: return BCRYPT_MD4_ALGORITHM;
2240 case RTDIGESTTYPE_SHA1: return BCRYPT_SHA1_ALGORITHM;
2241 case RTDIGESTTYPE_SHA256: return BCRYPT_SHA256_ALGORITHM;
2242 case RTDIGESTTYPE_SHA384: return BCRYPT_SHA384_ALGORITHM;
2243 case RTDIGESTTYPE_SHA512: return BCRYPT_SHA512_ALGORITHM;
2244 default:
2245 RTMsgError("No BCrypt translation for %s/%d!", RTCrDigestGetAlgorithmOid(hDigest), RTCrDigestGetType(hDigest));
2246 return L"No BCrypt translation";
2247 }
2248}
2249
2250static RTEXITCODE
2251SignToolPkcs7_Pkcs7SignStuffAgainWithReal(const char *pszWhat, SignToolKeyPair *pCertKeyPair, unsigned cVerbosity,
2252 PRTCRPKCS7CONTENTINFO pContentInfo, void **ppvSigned, size_t *pcbSigned)
2253
2254{
2255 RT_NOREF(cVerbosity);
2256
2257 /*
2258 * First remove the fake certificate from the PKCS7 structure and insert the real one.
2259 */
2260 PRTCRPKCS7SIGNEDDATA pSignedData = pContentInfo->u.pSignedData;
2261 unsigned iCert = pSignedData->Certificates.cItems;
2262 unsigned cErased = 0;
2263 while (iCert-- > 0)
2264 {
2265 PCRTCRPKCS7CERT pCert = pSignedData->Certificates.papItems[iCert];
2266 if ( pCert->enmChoice == RTCRPKCS7CERTCHOICE_X509
2267 && RTCrX509Certificate_MatchIssuerAndSerialNumber(pCert->u.pX509Cert,
2268 &pCertKeyPair->pCertificate->TbsCertificate.Issuer,
2269 &pCertKeyPair->pCertificate->TbsCertificate.SerialNumber))
2270 {
2271 RTCrPkcs7SetOfCerts_Erase(&pSignedData->Certificates, iCert);
2272 cErased++;
2273 }
2274 }
2275 if (cErased == 0)
2276 return RTMsgErrorExitFailure("(%s) Failed to find temporary signing certificate in PKCS#7 from OpenSSL: %u certs",
2277 pszWhat, pSignedData->Certificates.cItems);
2278
2279 /* Then insert the real signing certificate. */
2280 PCRTCRX509CERTIFICATE const pRealCertificate = pCertKeyPair->getRealCertificate();
2281 RTEXITCODE rcExit = SignToolPkcs7_AppendCertificate(pSignedData, pRealCertificate);
2282 if (rcExit != RTEXITCODE_SUCCESS)
2283 return rcExit;
2284
2285 /*
2286 * Modify the signer info to reflect the real certificate.
2287 */
2288 PRTCRPKCS7SIGNERINFO pSignerInfo = pSignedData->SignerInfos.papItems[0];
2289 RTCrX509Name_Delete(&pSignerInfo->IssuerAndSerialNumber.Name);
2290 int rc = RTCrX509Name_Clone(&pSignerInfo->IssuerAndSerialNumber.Name,
2291 &pRealCertificate->TbsCertificate.Issuer, &g_RTAsn1DefaultAllocator);
2292 if (RT_FAILURE(rc))
2293 return RTMsgErrorExitFailure("(%s) RTCrX509Name_Clone failed: %Rrc", pszWhat, rc);
2294
2295 RTAsn1Integer_Delete(&pSignerInfo->IssuerAndSerialNumber.SerialNumber);
2296 rc = RTAsn1Integer_Clone(&pSignerInfo->IssuerAndSerialNumber.SerialNumber,
2297 &pRealCertificate->TbsCertificate.SerialNumber, &g_RTAsn1DefaultAllocator);
2298 if (RT_FAILURE(rc))
2299 return RTMsgErrorExitFailure("(%s) RTAsn1Integer_Clone failed: %Rrc", pszWhat, rc);
2300
2301 /* There shouldn't be anything in the authenticated attributes that
2302 we need to modify... */
2303
2304 /*
2305 * Now a create a new signature using the real key. Since we haven't modified
2306 * the authenticated attributes, we can just hash them as-is.
2307 */
2308 /* Create the hash to sign. */
2309 RTCRDIGEST hDigest;
2310 rc = RTCrDigestCreateByObjId(&hDigest, &pSignerInfo->DigestAlgorithm.Algorithm);
2311 if (RT_FAILURE(rc))
2312 return RTMsgErrorExitFailure("(%s) RTCrDigestCreateByObjId failed on '%s': %Rrc",
2313 pszWhat, pSignerInfo->DigestAlgorithm.Algorithm.szObjId, rc);
2314
2315 rcExit = RTEXITCODE_FAILURE;
2316 RTERRINFOSTATIC ErrInfo;
2317 rc = RTCrPkcs7Attributes_HashAttributes(&pSignerInfo->AuthenticatedAttributes, hDigest, RTErrInfoInitStatic(&ErrInfo));
2318 if (RT_SUCCESS(rc))
2319 {
2320 BCRYPT_PKCS1_PADDING_INFO PaddingInfo = { GetBCryptNameFromCrDigest(hDigest) };
2321 DWORD cbSignature = 0;
2322 SECURITY_STATUS rcNCrypt = NCryptSignHash(pCertKeyPair->hNCryptPrivateKey, &PaddingInfo,
2323 (PBYTE)RTCrDigestGetHash(hDigest), RTCrDigestGetHashSize(hDigest),
2324 NULL, 0, &cbSignature, NCRYPT_SILENT_FLAG | BCRYPT_PAD_PKCS1);
2325 if (rcNCrypt == ERROR_SUCCESS)
2326 {
2327 if (cVerbosity)
2328 RTMsgInfo("PaddingInfo: '%ls' cb=%#x, was %#zx\n",
2329 PaddingInfo.pszAlgId, cbSignature, pSignerInfo->EncryptedDigest.Asn1Core.cb);
2330
2331 rc = RTAsn1OctetString_AllocContent(&pSignerInfo->EncryptedDigest, NULL /*pvSrc*/, cbSignature,
2332 &g_RTAsn1DefaultAllocator);
2333 if (RT_SUCCESS(rc))
2334 {
2335 Assert(pSignerInfo->EncryptedDigest.Asn1Core.uData.pv);
2336 rcNCrypt = NCryptSignHash(pCertKeyPair->hNCryptPrivateKey, &PaddingInfo,
2337 (PBYTE)RTCrDigestGetHash(hDigest), RTCrDigestGetHashSize(hDigest),
2338 (PBYTE)pSignerInfo->EncryptedDigest.Asn1Core.uData.pv, cbSignature, &cbSignature,
2339 /*NCRYPT_SILENT_FLAG |*/ BCRYPT_PAD_PKCS1);
2340 if (rcNCrypt == ERROR_SUCCESS)
2341 {
2342 /*
2343 * Now we need to re-encode the whole thing and decode it again.
2344 */
2345 PRTASN1CORE pRoot = RTCrPkcs7ContentInfo_GetAsn1Core(pContentInfo);
2346 uint32_t cbRealSigned;
2347 rc = RTAsn1EncodePrepare(pRoot, RTASN1ENCODE_F_DER, &cbRealSigned, RTErrInfoInitStatic(&ErrInfo));
2348 if (RT_SUCCESS(rc))
2349 {
2350 void *pvRealSigned = RTMemAllocZ(cbRealSigned);
2351 if (pvRealSigned)
2352 {
2353 rc = RTAsn1EncodeToBuffer(pRoot, RTASN1ENCODE_F_DER, pvRealSigned, cbRealSigned,
2354 RTErrInfoInitStatic(&ErrInfo));
2355 if (RT_SUCCESS(rc))
2356 {
2357 /* Decode it */
2358 RTCrPkcs7ContentInfo_Delete(pContentInfo);
2359
2360 RTASN1CURSORPRIMARY PrimaryCursor;
2361 RTAsn1CursorInitPrimary(&PrimaryCursor, pvRealSigned, cbRealSigned, RTErrInfoInitStatic(&ErrInfo),
2362 &g_RTAsn1DefaultAllocator, 0, pszWhat);
2363 rc = RTCrPkcs7ContentInfo_DecodeAsn1(&PrimaryCursor.Cursor, 0, pContentInfo, "CI");
2364 if (RT_SUCCESS(rc))
2365 {
2366 Assert(RTCrPkcs7ContentInfo_IsSignedData(pContentInfo));
2367
2368 /* Almost done! Just replace output buffer. */
2369 RTMemFree(*ppvSigned);
2370 *ppvSigned = pvRealSigned;
2371 *pcbSigned = cbRealSigned;
2372 pvRealSigned = NULL;
2373 rcExit = RTEXITCODE_SUCCESS;
2374 }
2375 else
2376 RTMsgError("(%s) RTCrPkcs7ContentInfo_DecodeAsn1 failed: %Rrc%#RTeim",
2377 pszWhat, rc, &ErrInfo.Core);
2378 }
2379 else
2380 RTMsgError("(%s) RTAsn1EncodeToBuffer failed: %Rrc%#RTeim", pszWhat, rc, &ErrInfo.Core);
2381
2382 RTMemFree(pvRealSigned);
2383 }
2384 else
2385 RTMsgError("(%s) Failed to allocate %u bytes!", pszWhat, cbRealSigned);
2386 }
2387 else
2388 RTMsgError("(%s) RTAsn1EncodePrepare failed: %Rrc%#RTeim", pszWhat, rc, &ErrInfo.Core);
2389 }
2390 else
2391 RTMsgError("(%s) NCryptSignHash/2 failed: %Rwc %#x (%u)", pszWhat, rcNCrypt, rcNCrypt, rcNCrypt);
2392 }
2393 else
2394 RTMsgError("(%s) RTAsn1OctetString_AllocContent(,,%#x) failed: %Rrc", pszWhat, cbSignature, rc);
2395 }
2396 else
2397 RTMsgError("(%s) NCryptSignHash/1 failed: %Rwc %#x (%u)", pszWhat, rcNCrypt, rcNCrypt, rcNCrypt);
2398 }
2399 else
2400 RTMsgError("(%s) RTCrPkcs7Attributes_HashAttributes failed: %Rrc%#RTeim", pszWhat, rc, &ErrInfo.Core);
2401 RTCrDigestRelease(hDigest);
2402 return rcExit;
2403}
2404
2405#endif /* RT_OS_WINDOWS */
2406
2407static RTEXITCODE SignToolPkcs7_Pkcs7SignStuffInner(const char *pszWhat, const void *pvToDataToSign, size_t cbToDataToSign,
2408 PCRTCRPKCS7ATTRIBUTES pAuthAttribs, RTCRSTORE hAdditionalCerts,
2409 uint32_t fExtraFlags, RTDIGESTTYPE enmDigestType,
2410 SignToolKeyPair *pCertKeyPair, unsigned cVerbosity,
2411 void **ppvSigned, size_t *pcbSigned, PRTCRPKCS7CONTENTINFO pContentInfo,
2412 PRTCRPKCS7SIGNEDDATA *ppSignedData)
2413{
2414 *ppvSigned = NULL;
2415 if (pcbSigned)
2416 *pcbSigned = 0;
2417 if (ppSignedData)
2418 *ppSignedData = NULL;
2419
2420 /* Figure out how large the signature will be. */
2421 uint32_t const fSignFlags = RTCRPKCS7SIGN_SD_F_USE_V1 | RTCRPKCS7SIGN_SD_F_NO_SMIME_CAP | fExtraFlags;
2422 size_t cbSigned = 1024;
2423 RTERRINFOSTATIC ErrInfo;
2424 int rc = RTCrPkcs7SimpleSignSignedData(fSignFlags, pCertKeyPair->pCertificate, pCertKeyPair->hPrivateKey,
2425 pvToDataToSign, cbToDataToSign,enmDigestType, hAdditionalCerts, pAuthAttribs,
2426 NULL, &cbSigned, RTErrInfoInitStatic(&ErrInfo));
2427 if (rc != VERR_BUFFER_OVERFLOW)
2428 return RTMsgErrorExitFailure("(%s) RTCrPkcs7SimpleSignSignedData failed: %Rrc%#RTeim", pszWhat, rc, &ErrInfo.Core);
2429
2430 /* Allocate memory for it and do the actual signing. */
2431 void *pvSigned = RTMemAllocZ(cbSigned);
2432 if (!pvSigned)
2433 return RTMsgErrorExitFailure("(%s) Failed to allocate %#zx bytes for %s signature", pszWhat, cbSigned, pszWhat);
2434 rc = RTCrPkcs7SimpleSignSignedData(fSignFlags, pCertKeyPair->pCertificate, pCertKeyPair->hPrivateKey,
2435 pvToDataToSign, cbToDataToSign, enmDigestType, hAdditionalCerts, pAuthAttribs,
2436 pvSigned, &cbSigned, RTErrInfoInitStatic(&ErrInfo));
2437 if (RT_SUCCESS(rc))
2438 {
2439 if (cVerbosity > 2)
2440 RTMsgInfo("%s signature: %#zx bytes\n%.*Rhxd\n", pszWhat, cbSigned, cbSigned, pvSigned);
2441
2442 /*
2443 * Decode the signature and check that it is SignedData.
2444 */
2445 RTASN1CURSORPRIMARY PrimaryCursor;
2446 RTAsn1CursorInitPrimary(&PrimaryCursor, pvSigned, (uint32_t)cbSigned, RTErrInfoInitStatic(&ErrInfo),
2447 &g_RTAsn1DefaultAllocator, 0, pszWhat);
2448 rc = RTCrPkcs7ContentInfo_DecodeAsn1(&PrimaryCursor.Cursor, 0, pContentInfo, "CI");
2449 if (RT_SUCCESS(rc))
2450 {
2451 if (RTCrPkcs7ContentInfo_IsSignedData(pContentInfo))
2452 {
2453#ifdef RT_OS_WINDOWS
2454 /*
2455 * If we're using a fake key+cert, we now have to re-do the signing using the real
2456 * key+cert and the windows crypto API. This kludge is necessary because we can't
2457 * typically get that the encoded private key, so it isn't possible to feed it to
2458 * openssl.
2459 */
2460 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
2461 if (pCertKeyPair->pCertificateReal)
2462 rcExit = SignToolPkcs7_Pkcs7SignStuffAgainWithReal(pszWhat, pCertKeyPair, cVerbosity, pContentInfo,
2463 &pvSigned, &cbSigned);
2464 if (rcExit == RTEXITCODE_SUCCESS)
2465#endif
2466 {
2467 /*
2468 * Set returns and maybe display the result before returning.
2469 */
2470 *ppvSigned = pvSigned;
2471 if (pcbSigned)
2472 *pcbSigned = cbSigned;
2473 if (ppSignedData)
2474 *ppSignedData = pContentInfo->u.pSignedData;
2475
2476 if (cVerbosity)
2477 {
2478 SHOWEXEPKCS7 ShowExe;
2479 RT_ZERO(ShowExe);
2480 ShowExe.cVerbosity = cVerbosity;
2481 HandleShowExeWorkerPkcs7Display(&ShowExe, pContentInfo->u.pSignedData, 0, pContentInfo);
2482 }
2483 return RTEXITCODE_SUCCESS;
2484 }
2485 }
2486
2487 RTMsgError("(%s) RTCrPkcs7SimpleSignSignedData did not create SignedData: %s",
2488 pszWhat, pContentInfo->ContentType.szObjId);
2489 }
2490 else
2491 RTMsgError("(%s) RTCrPkcs7ContentInfo_DecodeAsn1 failed: %Rrc%#RTeim", pszWhat, rc, &ErrInfo.Core);
2492 RTCrPkcs7ContentInfo_Delete(pContentInfo);
2493 }
2494 RTMemFree(pvSigned);
2495 return RTEXITCODE_FAILURE;
2496}
2497
2498
2499static RTEXITCODE SignToolPkcs7_Pkcs7SignStuff(const char *pszWhat, const void *pvToDataToSign, size_t cbToDataToSign,
2500 PCRTCRPKCS7ATTRIBUTES pAuthAttribs, RTCRSTORE hAdditionalCerts,
2501 uint32_t fExtraFlags, RTDIGESTTYPE enmDigestType, SignToolKeyPair *pCertKeyPair,
2502 unsigned cVerbosity, void **ppvSigned, size_t *pcbSigned,
2503 PRTCRPKCS7CONTENTINFO pContentInfo, PRTCRPKCS7SIGNEDDATA *ppSignedData)
2504{
2505 /*
2506 * Gather all additional certificates before doing the actual work.
2507 */
2508 RTCRSTORE hAllAdditionalCerts = pCertKeyPair->assembleAllAdditionalCertificates(hAdditionalCerts);
2509 if (hAllAdditionalCerts == NIL_RTCRSTORE)
2510 return RTEXITCODE_FAILURE;
2511 RTEXITCODE rcExit = SignToolPkcs7_Pkcs7SignStuffInner(pszWhat, pvToDataToSign, cbToDataToSign, pAuthAttribs,
2512 hAllAdditionalCerts, fExtraFlags, enmDigestType, pCertKeyPair,
2513 cVerbosity, ppvSigned, pcbSigned, pContentInfo, ppSignedData);
2514 RTCrStoreRelease(hAllAdditionalCerts);
2515 return rcExit;
2516}
2517
2518
2519static RTEXITCODE SignToolPkcs7_AddTimestampSignatureEx(PRTCRPKCS7SIGNERINFO pSignerInfo, PRTCRPKCS7SIGNEDDATA pSignedData,
2520 unsigned cVerbosity, bool fReplaceExisting,
2521 RTTIMESPEC SigningTime, SignToolTimestampOpts *pTimestampOpts)
2522{
2523 AssertReturn(!pTimestampOpts->isNewType(), RTMsgErrorExitFailure("New style signatures not supported yet"));
2524
2525 /*
2526 * Create a set of attributes we need to include in the AuthenticatedAttributes
2527 * of the timestamp signature.
2528 */
2529 RTCRPKCS7ATTRIBUTES AuthAttribs;
2530 int rc = RTCrPkcs7Attributes_Init(&AuthAttribs, &g_RTAsn1DefaultAllocator);
2531 if (RT_FAILURE(rc))
2532 return RTMsgErrorExitFailure("RTCrPkcs7SetOfAttributes_Init failed: %Rrc", rc);
2533
2534 RTEXITCODE rcExit = SignToolPkcs7_AddAuthAttribsForTimestamp(&AuthAttribs, pTimestampOpts->m_enmType, SigningTime,
2535 pTimestampOpts->getRealCertificate());
2536 if (rcExit == RTEXITCODE_SUCCESS)
2537 {
2538 /*
2539 * Now create a PKCS#7 signature of the encrypted signature from the selected signer info.
2540 */
2541 void *pvSigned = NULL;
2542 PRTCRPKCS7SIGNEDDATA pTsSignedData = NULL;
2543 RTCRPKCS7CONTENTINFO TsContentInfo;
2544 rcExit = SignToolPkcs7_Pkcs7SignStuffInner("timestamp", pSignerInfo->EncryptedDigest.Asn1Core.uData.pv,
2545 pSignerInfo->EncryptedDigest.Asn1Core.cb, &AuthAttribs,
2546 NIL_RTCRSTORE /*hAdditionalCerts*/, RTCRPKCS7SIGN_SD_F_DEATCHED,
2547 RTDIGESTTYPE_SHA1, pTimestampOpts, cVerbosity,
2548 &pvSigned, NULL /*pcbSigned*/, &TsContentInfo, &pTsSignedData);
2549 if (rcExit == RTEXITCODE_SUCCESS)
2550 {
2551
2552 /*
2553 * If we're replacing existing timestamp signatures, remove old ones now.
2554 */
2555 if ( fReplaceExisting
2556 && RTCrPkcs7Attributes_IsPresent(&pSignerInfo->UnauthenticatedAttributes))
2557 {
2558 uint32_t iItem = pSignerInfo->UnauthenticatedAttributes.cItems;
2559 while (iItem-- > 0)
2560 {
2561 PRTCRPKCS7ATTRIBUTE pAttr = pSignerInfo->UnauthenticatedAttributes.papItems[iItem];
2562 if (pAttr->enmType == RTCRPKCS7ATTRIBUTETYPE_COUNTER_SIGNATURES) /* ASSUMES all counter sigs are timstamps */
2563 {
2564 if (cVerbosity > 1)
2565 RTMsgInfo("Removing counter signature in attribute #%u\n", iItem);
2566 rc = RTCrPkcs7Attributes_Erase(&pSignerInfo->UnauthenticatedAttributes, iItem);
2567 if (RT_FAILURE(rc))
2568 rcExit = RTMsgErrorExitFailure("RTCrPkcs7Attributes_Erase failed on #%u: %Rrc", iItem, rc);
2569 }
2570 }
2571 }
2572
2573 /*
2574 * Add the new one.
2575 */
2576 if (rcExit == RTEXITCODE_SUCCESS)
2577 rcExit = SignToolPkcs7_AppendCounterSignature(pSignerInfo, pTsSignedData->SignerInfos.papItems[0], cVerbosity);
2578
2579 /*
2580 * Make sure the signing certificate is included.
2581 */
2582 if (rcExit == RTEXITCODE_SUCCESS)
2583 {
2584 rcExit = SignToolPkcs7_AppendCertificate(pSignedData, pTimestampOpts->getRealCertificate());
2585
2586 PCRTCRCERTCTX pInterCaCtx = NULL;
2587 while ((pInterCaCtx = pTimestampOpts->findNextIntermediateCert(pInterCaCtx)) != NULL)
2588 if (rcExit == RTEXITCODE_SUCCESS)
2589 rcExit = SignToolPkcs7_AppendCertificate(pSignedData, pInterCaCtx->pCert);
2590 }
2591
2592 /*
2593 * Clean up.
2594 */
2595 RTCrPkcs7ContentInfo_Delete(&TsContentInfo);
2596 RTMemFree(pvSigned);
2597 }
2598 }
2599 RTCrPkcs7Attributes_Delete(&AuthAttribs);
2600 return rcExit;
2601}
2602
2603
2604static RTEXITCODE SignToolPkcs7_AddTimestampSignature(SIGNTOOLPKCS7EXE *pThis, unsigned cVerbosity, unsigned iSignature,
2605 bool fReplaceExisting, RTTIMESPEC SigningTime,
2606 SignToolTimestampOpts *pTimestampOpts)
2607{
2608 /*
2609 * Locate the signature specified by iSignature and add a timestamp to it.
2610 */
2611 PRTCRPKCS7SIGNEDDATA pSignedData = NULL;
2612 PRTCRPKCS7SIGNERINFO pSignerInfo = SignToolPkcs7_FindNestedSignatureByIndex(pThis, iSignature, &pSignedData);
2613 if (!pSignerInfo)
2614 return RTMsgErrorExitFailure("No signature #%u in %s", iSignature, pThis->pszFilename);
2615
2616 return SignToolPkcs7_AddTimestampSignatureEx(pSignerInfo, pSignedData, cVerbosity, fReplaceExisting,
2617 SigningTime, pTimestampOpts);
2618}
2619
2620
2621typedef enum SIGNDATATWEAK
2622{
2623 kSignDataTweak_NoTweak = 1,
2624 kSignDataTweak_RootIsParent
2625} SIGNDATATWEAK;
2626
2627static RTEXITCODE SignToolPkcs7_SignData(SIGNTOOLPKCS7 *pThis, PRTASN1CORE pToSignRoot, SIGNDATATWEAK enmTweak,
2628 const char *pszContentTypeId, unsigned cVerbosity, uint32_t fExtraFlags,
2629 RTDIGESTTYPE enmSigType, bool fReplaceExisting, bool fNoSigningTime,
2630 SignToolKeyPair *pSigningCertKey, RTCRSTORE hAddCerts,
2631 RTTIMESPEC SigningTime, size_t cTimestampOpts, SignToolTimestampOpts *paTimestampOpts)
2632{
2633 /*
2634 * Encode it.
2635 */
2636 RTERRINFOSTATIC ErrInfo;
2637 uint32_t cbEncoded = 0;
2638 int rc = RTAsn1EncodePrepare(pToSignRoot, RTASN1ENCODE_F_DER, &cbEncoded, RTErrInfoInitStatic(&ErrInfo));
2639 if (RT_FAILURE(rc))
2640 return RTMsgErrorExitFailure("RTAsn1EncodePrepare failed: %Rrc%RTeim", rc, &ErrInfo.Core);
2641
2642 if (cVerbosity >= 4)
2643 RTAsn1Dump(pToSignRoot, 0, 0, RTStrmDumpPrintfV, g_pStdOut);
2644
2645 uint8_t *pbEncoded = (uint8_t *)RTMemTmpAllocZ(cbEncoded );
2646 if (!pbEncoded)
2647 return RTMsgErrorExitFailure("Failed to allocate %#z bytes for encoding data we're signing (%s)",
2648 cbEncoded, pszContentTypeId);
2649
2650 RTEXITCODE rcExit = RTEXITCODE_FAILURE;
2651 rc = RTAsn1EncodeToBuffer(pToSignRoot, RTASN1ENCODE_F_DER, pbEncoded, cbEncoded, RTErrInfoInitStatic(&ErrInfo));
2652 if (RT_SUCCESS(rc))
2653 {
2654 size_t const cbToSign = cbEncoded - (enmTweak == kSignDataTweak_RootIsParent ? pToSignRoot->cbHdr : 0);
2655 void const *pvToSign = pbEncoded + (enmTweak == kSignDataTweak_RootIsParent ? pToSignRoot->cbHdr : 0);
2656
2657 /*
2658 * Create additional authenticated attributes.
2659 */
2660 RTCRPKCS7ATTRIBUTES AuthAttribs;
2661 rc = RTCrPkcs7Attributes_Init(&AuthAttribs, &g_RTAsn1DefaultAllocator);
2662 if (RT_SUCCESS(rc))
2663 {
2664 rcExit = SignToolPkcs7_AddAuthAttribsForImageOrCatSignature(&AuthAttribs, SigningTime, fNoSigningTime,
2665 pszContentTypeId);
2666 if (rcExit == RTEXITCODE_SUCCESS)
2667 {
2668 /*
2669 * Ditch the old signature if so desired.
2670 * (It is okay to do this in the CAT case too, as we've already
2671 * encoded the data and won't touch pToSignRoot any more.)
2672 */
2673 pToSignRoot = NULL; /* (may become invalid if replacing) */
2674 if (fReplaceExisting && pThis->pSignedData)
2675 {
2676 RTCrPkcs7ContentInfo_Delete(&pThis->ContentInfo);
2677 pThis->pSignedData = NULL;
2678 RTMemFree(pThis->pbBuf);
2679 pThis->pbBuf = NULL;
2680 pThis->cbBuf = 0;
2681 }
2682
2683 /*
2684 * Do the actual signing.
2685 */
2686 SIGNTOOLPKCS7 Src = { RTSIGNTOOLFILETYPE_DETECT, NULL, 0, NULL };
2687 PSIGNTOOLPKCS7 pSigDst = !pThis->pSignedData ? pThis : &Src;
2688 rcExit = SignToolPkcs7_Pkcs7SignStuff("image", pvToSign, cbToSign, &AuthAttribs, hAddCerts,
2689 fExtraFlags | RTCRPKCS7SIGN_SD_F_NO_DATA_ENCAP, enmSigType /** @todo ?? */,
2690 pSigningCertKey, cVerbosity,
2691 (void **)&pSigDst->pbBuf, &pSigDst->cbBuf,
2692 &pSigDst->ContentInfo, &pSigDst->pSignedData);
2693 if (rcExit == RTEXITCODE_SUCCESS)
2694 {
2695 /*
2696 * Add the requested timestamp signatures if requested.
2697 */
2698 for (size_t i = 0; rcExit == RTEXITCODE_SUCCESS &&i < cTimestampOpts; i++)
2699 if (paTimestampOpts[i].isComplete())
2700 rcExit = SignToolPkcs7_AddTimestampSignatureEx(pSigDst->pSignedData->SignerInfos.papItems[0],
2701 pSigDst->pSignedData,
2702 cVerbosity, false /*fReplaceExisting*/,
2703 SigningTime, &paTimestampOpts[i]);
2704
2705 /*
2706 * Append the signature to the existing one, if that's what we're doing.
2707 */
2708 if (rcExit == RTEXITCODE_SUCCESS && pSigDst == &Src)
2709 rcExit = SignToolPkcs7_AddNestedSignature(pThis, &Src, cVerbosity, true /*fPrepend*/); /** @todo prepend/append option */
2710
2711 /* cleanup */
2712 if (pSigDst == &Src)
2713 SignToolPkcs7_Delete(&Src);
2714 }
2715
2716 }
2717 RTCrPkcs7Attributes_Delete(&AuthAttribs);
2718 }
2719 else
2720 RTMsgError("RTCrPkcs7SetOfAttributes_Init failed: %Rrc", rc);
2721 }
2722 else
2723 RTMsgError("RTAsn1EncodeToBuffer failed: %Rrc", rc);
2724 RTMemTmpFree(pbEncoded);
2725 return rcExit;
2726}
2727
2728
2729static RTEXITCODE SignToolPkcs7_SpcCompleteWithoutPageHashes(RTCRSPCINDIRECTDATACONTENT *pSpcIndData)
2730{
2731 PCRTASN1ALLOCATORVTABLE const pAllocator = &g_RTAsn1DefaultAllocator;
2732 PRTCRSPCPEIMAGEDATA const pPeImage = pSpcIndData->Data.uValue.pPeImage;
2733 Assert(pPeImage);
2734
2735 /*
2736 * Set it to File with an empty name.
2737 * RTCRSPCPEIMAGEDATA::Flags -vv
2738 * RTCRSPCPEIMAGEDATA::SeqCore -vv T0 -vv vv- pT2/CtxTag2
2739 * 0040: 04 01 82 37 02 01 0f 30-09 03 01 00 a0 04 a2 02 ...7...0........
2740 * 0050: 80 00 30 21 30 09 06 05-2b 0e 03 02 1a 05 00 04 ..0!0...+.......
2741 * ^^- pUcs2 / empty string
2742 */
2743
2744 /* Create an empty BMP string. */
2745 RTASN1STRING EmptyStr;
2746 int rc = RTAsn1BmpString_Init(&EmptyStr, pAllocator);
2747 if (RT_FAILURE(rc))
2748 return RTMsgErrorExitFailure("RTAsn1BmpString_Init/Ucs2 failed: %Rrc", rc);
2749
2750 /* Create an SPC string and use the above empty string with the Ucs2 setter. */
2751 RTEXITCODE rcExit = RTEXITCODE_FAILURE;
2752 RTCRSPCSTRING SpcString;
2753 rc = RTCrSpcString_Init(&SpcString, pAllocator);
2754 if (RT_SUCCESS(rc))
2755 {
2756 rc = RTCrSpcString_SetUcs2(&SpcString, &EmptyStr, pAllocator);
2757 if (RT_SUCCESS(rc))
2758 {
2759 /* Create a temporary SpcLink with the empty SpcString. */
2760 RTCRSPCLINK SpcLink;
2761 rc = RTCrSpcLink_Init(&SpcLink, pAllocator);
2762 if (RT_SUCCESS(rc))
2763 {
2764 /* Use the setter on the SpcLink object to copy the SpcString to it. */
2765 rc = RTCrSpcLink_SetFile(&SpcLink, &SpcString, pAllocator);
2766 if (RT_SUCCESS(rc))
2767 {
2768 /* Use the setter to copy SpcLink to the PeImage structure. */
2769 rc = RTCrSpcPeImageData_SetFile(pPeImage, &SpcLink, pAllocator);
2770 if (RT_SUCCESS(rc))
2771 rcExit = RTEXITCODE_SUCCESS;
2772 else
2773 RTMsgError("RTCrSpcPeImageData_SetFile failed: %Rrc", rc);
2774 }
2775 else
2776 RTMsgError("RTCrSpcLink_SetFile failed: %Rrc", rc);
2777 RTCrSpcLink_Delete(&SpcLink);
2778 }
2779 else
2780 RTMsgError("RTCrSpcLink_Init failed: %Rrc", rc);
2781 }
2782 else
2783 RTMsgError("RTCrSpcString_SetUcs2 failed: %Rrc", rc);
2784 RTCrSpcString_Delete(&SpcString);
2785 }
2786 else
2787 RTMsgError("RTCrSpcString_Init failed: %Rrc", rc);
2788 RTAsn1BmpString_Delete(&EmptyStr);
2789 return rcExit;
2790}
2791
2792
2793static RTEXITCODE SignToolPkcs7_SpcAddImagePageHashes(SIGNTOOLPKCS7EXE *pThis, RTCRSPCINDIRECTDATACONTENT *pSpcIndData,
2794 RTDIGESTTYPE enmSigType)
2795{
2796 PCRTASN1ALLOCATORVTABLE const pAllocator = &g_RTAsn1DefaultAllocator;
2797 PRTCRSPCPEIMAGEDATA const pPeImage = pSpcIndData->Data.uValue.pPeImage;
2798 Assert(pPeImage);
2799
2800 /*
2801 * The hashes are stored in the 'Moniker' attribute.
2802 */
2803 /* Create a temporary SpcLink with a default moniker. */
2804 RTCRSPCLINK SpcLink;
2805 int rc = RTCrSpcLink_Init(&SpcLink, pAllocator);
2806 if (RT_FAILURE(rc))
2807 return RTMsgErrorExitFailure("RTCrSpcLink_Init failed: %Rrc", rc);
2808 rc = RTCrSpcLink_SetMoniker(&SpcLink, NULL, pAllocator);
2809 if (RT_SUCCESS(rc))
2810 {
2811 /* Use the setter to copy SpcLink to the PeImage structure. */
2812 rc = RTCrSpcPeImageData_SetFile(pPeImage, &SpcLink, pAllocator);
2813 if (RT_FAILURE(rc))
2814 RTMsgError("RTCrSpcLink_SetFile failed: %Rrc", rc);
2815 }
2816 else
2817 RTMsgError("RTCrSpcLink_SetMoniker failed: %Rrc", rc);
2818 RTCrSpcLink_Delete(&SpcLink);
2819 if (RT_FAILURE(rc))
2820 return RTEXITCODE_FAILURE;
2821
2822 /*
2823 * Now go to work on the moniker. It doesn't have any autogenerated
2824 * setters, so we must do stuff manually.
2825 */
2826 PRTCRSPCSERIALIZEDOBJECT pMoniker = pPeImage->T0.File.u.pMoniker;
2827 RTUUID Uuid;
2828 rc = RTUuidFromStr(&Uuid, RTCRSPCSERIALIZEDOBJECT_UUID_STR);
2829 if (RT_FAILURE(rc))
2830 return RTMsgErrorExitFailure("RTUuidFromStr failed: %Rrc", rc);
2831
2832 rc = RTAsn1OctetString_AllocContent(&pMoniker->Uuid, &Uuid, sizeof(Uuid), pAllocator);
2833 if (RT_FAILURE(rc))
2834 return RTMsgErrorExitFailure("RTAsn1String_InitWithValue/UUID failed: %Rrc", rc);
2835
2836 /* Create a new set of attributes and associate this with the SerializedData member. */
2837 PRTCRSPCSERIALIZEDOBJECTATTRIBUTES pSpcAttribs;
2838 rc = RTAsn1MemAllocZ(&pMoniker->SerializedData.EncapsulatedAllocation,
2839 (void **)&pSpcAttribs, sizeof(*pSpcAttribs));
2840 if (RT_FAILURE(rc))
2841 return RTMsgErrorExitFailure("RTAsn1MemAllocZ/pSpcAttribs failed: %Rrc", rc);
2842 pMoniker->SerializedData.pEncapsulated = RTCrSpcSerializedObjectAttributes_GetAsn1Core(pSpcAttribs);
2843 pMoniker->enmType = RTCRSPCSERIALIZEDOBJECTTYPE_ATTRIBUTES;
2844 pMoniker->u.pData = pSpcAttribs;
2845
2846 rc = RTCrSpcSerializedObjectAttributes_Init(pSpcAttribs, pAllocator);
2847 if (RT_FAILURE(rc))
2848 return RTMsgErrorExitFailure("RTCrSpcSerializedObjectAttributes_Init failed: %Rrc", rc);
2849
2850 /*
2851 * Add a single attribute to the set that we'll use for page hashes.
2852 */
2853 int32_t iPos = RTCrSpcSerializedObjectAttributes_Append(pSpcAttribs);
2854 if (iPos < 0)
2855 return RTMsgErrorExitFailure("RTCrSpcSerializedObjectAttributes_Append failed: %Rrc", iPos);
2856 PRTCRSPCSERIALIZEDOBJECTATTRIBUTE pSpcObjAttr = pSpcAttribs->papItems[iPos];
2857
2858 if (enmSigType == RTDIGESTTYPE_SHA1)
2859 rc = RTCrSpcSerializedObjectAttribute_SetV1Hashes(pSpcObjAttr, NULL, pAllocator);
2860 else if (enmSigType == RTDIGESTTYPE_SHA256)
2861 rc = RTCrSpcSerializedObjectAttribute_SetV2Hashes(pSpcObjAttr, NULL, pAllocator);
2862 else
2863 rc = VERR_CR_DIGEST_NOT_SUPPORTED;
2864 if (RT_FAILURE(rc))
2865 return RTMsgErrorExitFailure("RTCrSpcSerializedObjectAttribute_SetV1Hashes/SetV2Hashes failed: %Rrc", rc);
2866 PRTCRSPCSERIALIZEDPAGEHASHES pSpcPageHashes = pSpcObjAttr->u.pPageHashes;
2867 Assert(pSpcPageHashes);
2868
2869 /*
2870 * Now ask the loader for the number of pages in the page hash table
2871 * and calculate its size.
2872 */
2873 uint32_t cPages = 0;
2874 rc = RTLdrQueryPropEx(pThis->hLdrMod, RTLDRPROP_HASHABLE_PAGES, NULL, &cPages, sizeof(cPages), NULL);
2875 if (RT_FAILURE(rc))
2876 return RTMsgErrorExitFailure("RTLdrQueryPropEx/RTLDRPROP_HASHABLE_PAGES failed: %Rrc", rc);
2877
2878 uint32_t const cbHash = RTCrDigestTypeToHashSize(enmSigType);
2879 AssertReturn(cbHash > 0, RTMsgErrorExitFailure("Invalid value: enmSigType=%d", enmSigType));
2880 uint32_t const cbTable = (sizeof(uint32_t) + cbHash) * cPages;
2881
2882 /*
2883 * Allocate memory in the octect string.
2884 */
2885 rc = RTAsn1ContentAllocZ(&pSpcPageHashes->RawData.Asn1Core, cbTable, pAllocator);
2886 if (RT_FAILURE(rc))
2887 return RTMsgErrorExitFailure("RTAsn1ContentAllocZ failed to allocate %#x bytes for page hashes: %Rrc", cbTable, rc);
2888 pSpcPageHashes->pData = (PCRTCRSPCPEIMAGEPAGEHASHES)pSpcPageHashes->RawData.Asn1Core.uData.pu8;
2889
2890 RTLDRPROP enmLdrProp;
2891 switch (enmSigType)
2892 {
2893 case RTDIGESTTYPE_SHA1: enmLdrProp = RTLDRPROP_SHA1_PAGE_HASHES; break;
2894 case RTDIGESTTYPE_SHA256: enmLdrProp = RTLDRPROP_SHA256_PAGE_HASHES; break;
2895 default: AssertFailedReturn(RTMsgErrorExitFailure("Invalid value: enmSigType=%d", enmSigType));
2896
2897 }
2898 rc = RTLdrQueryPropEx(pThis->hLdrMod, enmLdrProp, NULL, (void *)pSpcPageHashes->RawData.Asn1Core.uData.pv, cbTable, NULL);
2899 if (RT_FAILURE(rc))
2900 return RTMsgErrorExitFailure("RTLdrQueryPropEx/RTLDRPROP_SHA?_PAGE_HASHES/%#x failed: %Rrc", cbTable, rc);
2901
2902 return RTEXITCODE_SUCCESS;
2903}
2904
2905
2906static RTEXITCODE SignToolPkcs7_SpcAddImageHash(SIGNTOOLPKCS7EXE *pThis, RTCRSPCINDIRECTDATACONTENT *pSpcIndData,
2907 RTDIGESTTYPE enmSigType)
2908{
2909 uint32_t const cbHash = RTCrDigestTypeToHashSize(enmSigType);
2910 const char * const pszAlgId = RTCrDigestTypeToAlgorithmOid(enmSigType);
2911
2912 /*
2913 * Ask the loader for the hash.
2914 */
2915 uint8_t abHash[RTSHA512_HASH_SIZE];
2916 int rc = RTLdrHashImage(pThis->hLdrMod, enmSigType, abHash, sizeof(abHash));
2917 if (RT_FAILURE(rc))
2918 return RTMsgErrorExitFailure("RTLdrHashImage/%s failed: %Rrc", RTCrDigestTypeToName(enmSigType), rc);
2919
2920 /*
2921 * Set it.
2922 */
2923 /** @todo no setter, this should be okay, though... */
2924 rc = RTAsn1ObjId_InitFromString(&pSpcIndData->DigestInfo.DigestAlgorithm.Algorithm, pszAlgId, &g_RTAsn1DefaultAllocator);
2925 if (RT_FAILURE(rc))
2926 return RTMsgErrorExitFailure("RTAsn1ObjId_InitFromString/%s failed: %Rrc", pszAlgId, rc);
2927 RTAsn1DynType_SetToNull(&pSpcIndData->DigestInfo.DigestAlgorithm.Parameters); /* ASSUMES RSA or similar */
2928
2929 rc = RTAsn1ContentDup(&pSpcIndData->DigestInfo.Digest.Asn1Core, abHash, cbHash, &g_RTAsn1DefaultAllocator);
2930 if (RT_FAILURE(rc))
2931 return RTMsgErrorExitFailure("RTAsn1ContentDup/%#x failed: %Rrc", cbHash, rc);
2932
2933 return RTEXITCODE_SUCCESS;
2934}
2935
2936
2937static RTEXITCODE SignToolPkcs7_AddOrReplaceSignature(SIGNTOOLPKCS7EXE *pThis, unsigned cVerbosity, RTDIGESTTYPE enmSigType,
2938 bool fReplaceExisting, bool fHashPages, bool fNoSigningTime,
2939 SignToolKeyPair *pSigningCertKey, RTCRSTORE hAddCerts,
2940 RTTIMESPEC SigningTime,
2941 size_t cTimestampOpts, SignToolTimestampOpts *paTimestampOpts)
2942{
2943 /*
2944 * We must construct the data to be packed into the PKCS#7 signature
2945 * and signed.
2946 */
2947 PCRTASN1ALLOCATORVTABLE const pAllocator = &g_RTAsn1DefaultAllocator;
2948 RTCRSPCINDIRECTDATACONTENT SpcIndData;
2949 int rc = RTCrSpcIndirectDataContent_Init(&SpcIndData, pAllocator);
2950 if (RT_FAILURE(rc))
2951 return RTMsgErrorExitFailure("RTCrSpcIndirectDataContent_Init failed: %Rrc", rc);
2952
2953 /* Set the data to PE image. */
2954 /** @todo Generalize the Type + enmType DYN stuff and generate setters. */
2955 Assert(SpcIndData.Data.enmType == RTCRSPCAAOVTYPE_NOT_PRESENT);
2956 Assert(SpcIndData.Data.uValue.pPeImage == NULL);
2957 RTEXITCODE rcExit;
2958 rc = RTAsn1ObjId_SetFromString(&SpcIndData.Data.Type, RTCRSPCPEIMAGEDATA_OID, pAllocator);
2959 if (RT_SUCCESS(rc))
2960 {
2961 SpcIndData.Data.enmType = RTCRSPCAAOVTYPE_PE_IMAGE_DATA;
2962 rc = RTAsn1MemAllocZ(&SpcIndData.Data.Allocation, (void **)&SpcIndData.Data.uValue.pPeImage,
2963 sizeof(*SpcIndData.Data.uValue.pPeImage));
2964 if (RT_SUCCESS(rc))
2965 {
2966 rc = RTCrSpcPeImageData_Init(SpcIndData.Data.uValue.pPeImage, pAllocator);
2967 if (RT_SUCCESS(rc))
2968 {
2969 /* Old (SHA1) signatures has a Flags member, it's zero bits, though. */
2970 if (enmSigType == RTDIGESTTYPE_SHA1)
2971 {
2972 uint8_t bFlags = 0;
2973 RTASN1BITSTRING Flags;
2974 rc = RTAsn1BitString_InitWithData(&Flags, &bFlags, 0, pAllocator);
2975 if (RT_SUCCESS(rc))
2976 {
2977 rc = RTCrSpcPeImageData_SetFlags(SpcIndData.Data.uValue.pPeImage, &Flags, pAllocator);
2978 RTAsn1BitString_Delete(&Flags);
2979 if (RT_FAILURE(rc))
2980 rcExit = RTMsgErrorExitFailure("RTCrSpcPeImageData_SetFlags failed: %Rrc", rc);
2981 }
2982 else
2983 rcExit = RTMsgErrorExitFailure("RTAsn1BitString_InitWithData failed: %Rrc", rc);
2984 }
2985
2986 /*
2987 * Add the hashes.
2988 */
2989 rcExit = SignToolPkcs7_SpcAddImageHash(pThis, &SpcIndData, enmSigType);
2990 if (rcExit == RTEXITCODE_SUCCESS)
2991 {
2992 if (fHashPages)
2993 rcExit = SignToolPkcs7_SpcAddImagePageHashes(pThis, &SpcIndData, enmSigType);
2994 else
2995 rcExit = SignToolPkcs7_SpcCompleteWithoutPageHashes(&SpcIndData);
2996
2997 /*
2998 * Encode and sign the SPC data, timestamp it, and line it up for adding to the executable.
2999 */
3000 if (rcExit == RTEXITCODE_SUCCESS)
3001 rcExit = SignToolPkcs7_SignData(pThis, RTCrSpcIndirectDataContent_GetAsn1Core(&SpcIndData),
3002 kSignDataTweak_NoTweak, RTCRSPCINDIRECTDATACONTENT_OID, cVerbosity, 0,
3003 enmSigType, fReplaceExisting, fNoSigningTime, pSigningCertKey, hAddCerts,
3004 SigningTime, cTimestampOpts, paTimestampOpts);
3005 }
3006 }
3007 else
3008 rcExit = RTMsgErrorExitFailure("RTCrPkcs7SignerInfos_Init failed: %Rrc", rc);
3009 }
3010 else
3011 rcExit = RTMsgErrorExitFailure("RTAsn1MemAllocZ failed for RTCRSPCPEIMAGEDATA: %Rrc", rc);
3012 }
3013 else
3014 rcExit = RTMsgErrorExitFailure("RTAsn1ObjId_SetWithString/SpcPeImageData failed: %Rrc", rc);
3015
3016 RTCrSpcIndirectDataContent_Delete(&SpcIndData);
3017 return rcExit;
3018}
3019
3020
3021static RTEXITCODE SignToolPkcs7_AddOrReplaceCatSignature(SIGNTOOLPKCS7 *pThis, unsigned cVerbosity, RTDIGESTTYPE enmSigType,
3022 bool fReplaceExisting, bool fNoSigningTime,
3023 SignToolKeyPair *pSigningCertKey, RTCRSTORE hAddCerts,
3024 RTTIMESPEC SigningTime,
3025 size_t cTimestampOpts, SignToolTimestampOpts *paTimestampOpts)
3026{
3027 AssertReturn(pThis->pSignedData, RTMsgErrorExitFailure("pSignedData is NULL!"));
3028
3029 /*
3030 * Figure out what to sign first.
3031 */
3032 uint32_t fExtraFlags = 0;
3033 PRTASN1CORE pToSign = &pThis->pSignedData->ContentInfo.Content.Asn1Core;
3034 const char *pszType = pThis->pSignedData->ContentInfo.ContentType.szObjId;
3035
3036 if (!fReplaceExisting && pThis->pSignedData->SignerInfos.cItems == 0)
3037 fReplaceExisting = true;
3038 if (!fReplaceExisting)
3039 {
3040 pszType = RTCR_PKCS7_DATA_OID;
3041 fExtraFlags |= RTCRPKCS7SIGN_SD_F_DEATCHED;
3042 }
3043
3044 /*
3045 * Do the signing.
3046 */
3047 RTEXITCODE rcExit = SignToolPkcs7_SignData(pThis, pToSign, kSignDataTweak_RootIsParent,
3048 pszType, cVerbosity, fExtraFlags, enmSigType, fReplaceExisting,
3049 fNoSigningTime, pSigningCertKey, hAddCerts,
3050 SigningTime, cTimestampOpts, paTimestampOpts);
3051
3052 /* probably need to clean up stuff related to nested signatures here later... */
3053 return rcExit;
3054}
3055
3056#endif /* !IPRT_SIGNTOOL_NO_SIGNING */
3057
3058
3059/*********************************************************************************************************************************
3060* Option handlers shared by 'sign-exe', 'sign-cat', 'add-timestamp-exe-signature' and others. *
3061*********************************************************************************************************************************/
3062#ifndef IPRT_SIGNTOOL_NO_SIGNING
3063
3064static RTEXITCODE HandleOptAddCert(PRTCRSTORE phStore, const char *pszFile)
3065{
3066 if (*phStore == NIL_RTCRSTORE)
3067 {
3068 int rc = RTCrStoreCreateInMem(phStore, 2);
3069 if (RT_FAILURE(rc))
3070 return RTMsgErrorExitFailure("RTCrStoreCreateInMem(,2) failed: %Rrc", rc);
3071 }
3072 RTERRINFOSTATIC ErrInfo;
3073 int rc = RTCrStoreCertAddFromFile(*phStore, RTCRCERTCTX_F_ADD_IF_NOT_FOUND, pszFile, RTErrInfoInitStatic(&ErrInfo));
3074 if (RT_FAILURE(rc))
3075 return RTMsgErrorExitFailure("Error reading certificate from '%s': %Rrc%#RTeim", pszFile, rc, &ErrInfo.Core);
3076 return RTEXITCODE_SUCCESS;
3077}
3078
3079static RTEXITCODE HandleOptSignatureType(RTDIGESTTYPE *penmSigType, const char *pszType)
3080{
3081 if ( RTStrICmpAscii(pszType, "sha1") == 0
3082 || RTStrICmpAscii(pszType, "sha-1") == 0)
3083 *penmSigType = RTDIGESTTYPE_SHA1;
3084 else if ( RTStrICmpAscii(pszType, "sha256") == 0
3085 || RTStrICmpAscii(pszType, "sha-256") == 0)
3086 *penmSigType = RTDIGESTTYPE_SHA256;
3087 else
3088 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown signature type: %s (expected sha1 or sha256)", pszType);
3089 return RTEXITCODE_SUCCESS;
3090}
3091
3092
3093static RTEXITCODE HandleOptTimestampType(SignToolTimestampOpts *pTimestampOpts, const char *pszType)
3094{
3095 if (strcmp(pszType, "old") == 0)
3096 pTimestampOpts->m_enmType = kTimestampType_Old;
3097 else if (strcmp(pszType, "new") == 0)
3098 pTimestampOpts->m_enmType = kTimestampType_New;
3099 else
3100 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown timestamp type: %s", pszType);
3101 return RTEXITCODE_SUCCESS;
3102}
3103
3104static RTEXITCODE HandleOptTimestampOverride(PRTTIMESPEC pSigningTime, const char *pszPartialTs)
3105{
3106 /*
3107 * First try use it as-is.
3108 */
3109 if (RTTimeSpecFromString(pSigningTime, pszPartialTs) != NULL)
3110 return RTEXITCODE_SUCCESS;
3111
3112 /* Check the input against a pattern, making sure we've got something that
3113 makes sense before trying to merge. */
3114 size_t const cchPartialTs = strlen(pszPartialTs);
3115 static char s_szPattern[] = "0000-00-00T00:00:";
3116 if (cchPartialTs > sizeof(s_szPattern) - 1) /* It is not a partial timestamp if we've got the seconds component. */
3117 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid timestamp: %s", pszPartialTs);
3118
3119 for (size_t off = 0; off < cchPartialTs; off++)
3120 switch (s_szPattern[off])
3121 {
3122 case '0':
3123 if (!RT_C_IS_DIGIT(pszPartialTs[off]))
3124 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid timestamp, expected digit at position %u: %s",
3125 off + 1, pszPartialTs);
3126 break;
3127 case '-':
3128 case ':':
3129 if (pszPartialTs[off] != s_szPattern[off])
3130 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid timestamp, expected '%c' at position %u: %s",
3131 s_szPattern[off], off + 1, pszPartialTs);
3132 break;
3133 case 'T':
3134 if ( pszPartialTs[off] != 'T'
3135 && pszPartialTs[off] != 't'
3136 && pszPartialTs[off] != ' ')
3137 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid timestamp, expected 'T' or space at position %u: %s",
3138 off + 1, pszPartialTs);
3139 break;
3140 default:
3141 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Internal error");
3142 }
3143
3144 if (RT_C_IS_DIGIT(s_szPattern[cchPartialTs]) && RT_C_IS_DIGIT(s_szPattern[cchPartialTs - 1]))
3145 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Incomplete timstamp component: %s", pszPartialTs);
3146
3147 /*
3148 * Take the current time and merge in the components from pszPartialTs.
3149 */
3150 char szSigningTime[RTTIME_STR_LEN];
3151 RTTIMESPEC Now;
3152 RTTimeSpecToString(RTTimeNow(&Now), szSigningTime, sizeof(szSigningTime));
3153 memcpy(szSigningTime, pszPartialTs, cchPartialTs);
3154 szSigningTime[4+1+2+1+2] = 'T';
3155
3156 /* Fix 29th for non-leap override: */
3157 if (memcmp(&szSigningTime[5], RT_STR_TUPLE("02-29")) == 0)
3158 {
3159 if (!RTTimeIsLeapYear(RTStrToUInt32(szSigningTime)))
3160 szSigningTime[9] = '8';
3161 }
3162 if (RTTimeSpecFromString(pSigningTime, szSigningTime) == NULL)
3163 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid timestamp: %s (%s)", pszPartialTs, szSigningTime);
3164
3165 return RTEXITCODE_SUCCESS;
3166}
3167
3168static RTEXITCODE HandleOptFileType(RTSIGNTOOLFILETYPE *penmFileType, const char *pszType)
3169{
3170 if (strcmp(pszType, "detect") == 0 || strcmp(pszType, "auto") == 0)
3171 *penmFileType = RTSIGNTOOLFILETYPE_DETECT;
3172 else if (strcmp(pszType, "exe") == 0)
3173 *penmFileType = RTSIGNTOOLFILETYPE_EXE;
3174 else if (strcmp(pszType, "cat") == 0)
3175 *penmFileType = RTSIGNTOOLFILETYPE_CAT;
3176 else
3177 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown forced file type: %s", pszType);
3178 return RTEXITCODE_SUCCESS;
3179}
3180
3181#endif /* !IPRT_SIGNTOOL_NO_SIGNING */
3182
3183/**
3184 * Detects the type of files @a pszFile is (by reading from it).
3185 *
3186 * @returns The file type, or RTSIGNTOOLFILETYPE_UNKNOWN (error displayed).
3187 * @param enmForceFileType Usually set to RTSIGNTOOLFILETYPE_DETECT, but if
3188 * not we'll return this without probing the file.
3189 * @param pszFile The name of the file to detect the type of.
3190 */
3191static RTSIGNTOOLFILETYPE DetectFileType(RTSIGNTOOLFILETYPE enmForceFileType, const char *pszFile)
3192{
3193 /*
3194 * Forced?
3195 */
3196 if (enmForceFileType != RTSIGNTOOLFILETYPE_DETECT)
3197 return enmForceFileType;
3198
3199 /*
3200 * Read the start of the file.
3201 */
3202 RTFILE hFile = NIL_RTFILE;
3203 int rc = RTFileOpen(&hFile, pszFile, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
3204 if (RT_FAILURE(rc))
3205 {
3206 RTMsgError("Error opening '%s' for reading: %Rrc", pszFile, rc);
3207 return RTSIGNTOOLFILETYPE_UNKNOWN;
3208 }
3209
3210 union
3211 {
3212 uint8_t ab[256];
3213 uint16_t au16[256/2];
3214 uint32_t au32[256/4];
3215 } uBuf;
3216 RT_ZERO(uBuf);
3217
3218 size_t cbRead = 0;
3219 rc = RTFileRead(hFile, &uBuf, sizeof(uBuf), &cbRead);
3220 if (RT_FAILURE(rc))
3221 RTMsgError("Error reading from '%s': %Rrc", pszFile, rc);
3222
3223 uint64_t cbFile;
3224 int rcSize = RTFileQuerySize(hFile, &cbFile);
3225 if (RT_FAILURE(rcSize))
3226 RTMsgError("Error querying size of '%s': %Rrc", pszFile, rc);
3227
3228 RTFileClose(hFile);
3229 if (RT_FAILURE(rc) || RT_FAILURE(rcSize))
3230 return RTSIGNTOOLFILETYPE_UNKNOWN;
3231
3232 /*
3233 * Try guess the kind of file.
3234 */
3235 /* All the executable magics we know: */
3236 if ( uBuf.au16[0] == RT_H2LE_U16_C(IMAGE_DOS_SIGNATURE)
3237 || uBuf.au16[0] == RT_H2LE_U16_C(IMAGE_NE_SIGNATURE)
3238 || uBuf.au16[0] == RT_H2LE_U16_C(IMAGE_LX_SIGNATURE)
3239 || uBuf.au16[0] == RT_H2LE_U16_C(IMAGE_LE_SIGNATURE)
3240 || uBuf.au32[0] == RT_H2LE_U32_C(IMAGE_NT_SIGNATURE)
3241 || uBuf.au32[0] == RT_H2LE_U32_C(IMAGE_ELF_SIGNATURE)
3242 || uBuf.au32[0] == IMAGE_FAT_SIGNATURE
3243 || uBuf.au32[0] == IMAGE_FAT_SIGNATURE_OE
3244 || uBuf.au32[0] == IMAGE_MACHO32_SIGNATURE
3245 || uBuf.au32[0] == IMAGE_MACHO32_SIGNATURE_OE
3246 || uBuf.au32[0] == IMAGE_MACHO64_SIGNATURE
3247 || uBuf.au32[0] == IMAGE_MACHO64_SIGNATURE_OE)
3248 return RTSIGNTOOLFILETYPE_EXE;
3249
3250 /*
3251 * Catalog files are PKCS#7 SignedData and starts with a ContentInfo, i.e.:
3252 * SEQUENCE {
3253 * contentType OBJECT IDENTIFIER,
3254 * content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL
3255 * }
3256 *
3257 * We ASSUME that it's DER encoded and doesn't use an indefinite length form
3258 * at the start and that contentType is signedData (1.2.840.113549.1.7.2).
3259 *
3260 * Example of a 10353 (0x2871) byte long file:
3261 * vv-------- contentType -------vv
3262 * 00000000 30 82 28 6D 06 09 2A 86 48 86 F7 0D 01 07 02 A0
3263 * 00000010 82 28 5E 30 82 28 5A 02 01 01 31 0B 30 09 06 05
3264 */
3265 if ( uBuf.ab[0] == (ASN1_TAG_SEQUENCE | ASN1_TAGFLAG_CONSTRUCTED)
3266 && uBuf.ab[1] != 0x80 /* not indefinite form */
3267 && uBuf.ab[1] > 0x30)
3268 {
3269 size_t off = 1;
3270 uint32_t cbRec = uBuf.ab[1];
3271 if (cbRec & 0x80)
3272 {
3273 cbRec &= 0x7f;
3274 off += cbRec;
3275 switch (cbRec)
3276 {
3277 case 1: cbRec = uBuf.ab[2]; break;
3278 case 2: cbRec = RT_MAKE_U16( uBuf.ab[3], uBuf.ab[2]); break;
3279 case 3: cbRec = RT_MAKE_U32_FROM_U8(uBuf.ab[4], uBuf.ab[3], uBuf.ab[2], 0); break;
3280 case 4: cbRec = RT_MAKE_U32_FROM_U8(uBuf.ab[5], uBuf.ab[4], uBuf.ab[3], uBuf.ab[2]); break;
3281 default: cbRec = UINT32_MAX; break;
3282 }
3283 }
3284 if (off <= 5)
3285 {
3286 off++;
3287 if (off + cbRec == cbFile)
3288 {
3289 /* If the contentType is signedData we're going to treat it as a catalog file,
3290 we don't currently much care about the signed content of a cat file. */
3291 static const uint8_t s_abSignedDataOid[] =
3292 { ASN1_TAG_OID, 9 /*length*/, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02 };
3293 if (memcmp(&uBuf.ab[off], s_abSignedDataOid, sizeof(s_abSignedDataOid)) == 0)
3294 return RTSIGNTOOLFILETYPE_CAT;
3295 }
3296 }
3297 }
3298
3299 RTMsgError("Unable to detect type of '%s'", pszFile);
3300 return RTSIGNTOOLFILETYPE_UNKNOWN;
3301}
3302
3303
3304/*********************************************************************************************************************************
3305* The 'extract-exe-signer-cert' command. *
3306*********************************************************************************************************************************/
3307
3308static RTEXITCODE HelpExtractExeSignerCert(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
3309{
3310 RT_NOREF_PV(enmLevel);
3311 RTStrmWrappedPrintf(pStrm, RTSTRMWRAPPED_F_HANGING_INDENT,
3312 "extract-exe-signer-cert [--as-c-array=name|--ber|--cer|--der] [--signature-index|-i <num>] [--input|--exe|-e] <exe> [--output|-o] <outfile.cer/h>\n");
3313 return RTEXITCODE_SUCCESS;
3314}
3315
3316static RTEXITCODE WriteCertToFile(PCRTCRX509CERTIFICATE pCert, const char *pszFilename, bool fForce)
3317{
3318 RTEXITCODE rcExit = RTEXITCODE_FAILURE;
3319 RTFILE hFile;
3320 int rc = RTFileOpen(&hFile, pszFilename,
3321 RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | (fForce ? RTFILE_O_CREATE_REPLACE : RTFILE_O_CREATE));
3322 if (RT_SUCCESS(rc))
3323 {
3324 uint32_t cbCert = pCert->SeqCore.Asn1Core.cbHdr + pCert->SeqCore.Asn1Core.cb;
3325 rc = RTFileWrite(hFile, pCert->SeqCore.Asn1Core.uData.pu8 - pCert->SeqCore.Asn1Core.cbHdr,
3326 cbCert, NULL);
3327 if (RT_SUCCESS(rc))
3328 {
3329 rc = RTFileClose(hFile);
3330 if (RT_SUCCESS(rc))
3331 {
3332 hFile = NIL_RTFILE;
3333 rcExit = RTEXITCODE_SUCCESS;
3334 RTMsgInfo("Successfully wrote %u bytes to '%s'", cbCert, pszFilename);
3335 }
3336 else
3337 RTMsgError("RTFileClose failed: %Rrc", rc);
3338 }
3339 else
3340 RTMsgError("RTFileWrite failed: %Rrc", rc);
3341 RTFileClose(hFile);
3342 }
3343 else
3344 RTMsgError("Error opening '%s' for writing: %Rrc", pszFilename, rc);
3345 return rcExit;
3346}
3347
3348
3349static void PrintCertAsCArray(PCRTCRX509CERTIFICATE pCert, uint32_t iSignature, const char *pszBaseNm, PRTSTREAM pStrm)
3350{
3351 uint32_t const cbCert = pCert->SeqCore.Asn1Core.cbHdr + pCert->SeqCore.Asn1Core.cb;
3352 uint8_t const * const pbCert = pCert->SeqCore.Asn1Core.uData.pu8 - pCert->SeqCore.Asn1Core.cbHdr;
3353 if (iSignature == UINT32_MAX)
3354 RTStrmPrintf(pStrm,
3355 "uint32_t const g_cb%s = %u;\n"
3356 "uint8_t const g_ab%s[%u] =\n"
3357 "{",
3358 pszBaseNm, cbCert, pszBaseNm, cbCert);
3359 else
3360 RTStrmPrintf(pStrm, "static uint8_t const g_ab%sCert%u[%u] =\n{", pszBaseNm, iSignature, cbCert);
3361 for (uint32_t off = 0; off < cbCert; off++)
3362 {
3363 if (off % 16 == 0)
3364 RTStrmPrintf(pStrm, "\n ");
3365 RTStrmPrintf(pStrm, " %#04x,", pbCert[off]);
3366 }
3367 RTStrmPrintf(pStrm, "\n};\n\n");
3368}
3369
3370
3371static void PrintCertTableAsC(uint32_t cSignatures, const char *pszBaseNm, PRTSTREAM pStrm)
3372{
3373 RTStrmPrintf(pStrm,
3374 "uint32_t const g_c%s = %u;\n"
3375 "struct { uint8_t const *pbCert; size_t cbCert; } const g_a%s[%u] =\n"
3376 "{\n"
3377 , pszBaseNm, cSignatures, pszBaseNm, cSignatures ? cSignatures : 1);
3378 for (uint32_t iSignature = 0; iSignature < cSignatures; iSignature++)
3379 RTStrmPrintf(pStrm, " { g_ab%sCert%u, sizeof(g_ab%sCert%u) },\n",
3380 pszBaseNm, iSignature, pszBaseNm, iSignature);
3381 if (!cSignatures)
3382 RTStrmPrintf(pStrm, " { NULL, 0 } /* dummy */ \n");
3383 RTStrmPrintf(pStrm, "};\n");
3384}
3385
3386
3387static RTEXITCODE WriteCertToFileAsC(PCRTCRX509CERTIFICATE pCert, const char *pszFilename, bool fForce, const char *pszBaseNm)
3388{
3389 RTEXITCODE rcExit;
3390 PRTSTREAM pStrm = NULL;
3391 int rc = RTStrmOpen(pszFilename, fForce ? "wt+" : "wtx", &pStrm);
3392 if (RT_SUCCESS(rc))
3393 {
3394 PrintCertAsCArray(pCert, UINT32_MAX, pszBaseNm, pStrm);
3395 rc = RTStrmClose(pStrm);
3396 if (RT_SUCCESS(rc))
3397 rcExit = RTEXITCODE_SUCCESS;
3398 else
3399 rcExit = RTMsgErrorExitFailure("Error writing/closing '%s': %Rrc", pszFilename, rc);
3400 }
3401 else
3402 rcExit = RTMsgErrorExitFailure("Failed to open '%s' for writing: %Rrc", pszFilename, rc);
3403 return rcExit;
3404}
3405
3406
3407
3408static RTEXITCODE HandleExtractExeSignerCert(int cArgs, char **papszArgs)
3409{
3410 /*
3411 * Parse arguments.
3412 */
3413 static const RTGETOPTDEF s_aOptions[] =
3414 {
3415 { "--ber", 'b', RTGETOPT_REQ_NOTHING },
3416 { "--cer", 'c', RTGETOPT_REQ_NOTHING },
3417 { "--der", 'd', RTGETOPT_REQ_NOTHING },
3418 { "--exe", 'e', RTGETOPT_REQ_STRING },
3419 { "--input", 'e', RTGETOPT_REQ_STRING },
3420 { "--output", 'o', RTGETOPT_REQ_STRING },
3421 { "--signature-index", 'i', RTGETOPT_REQ_UINT32 },
3422 { "--force", 'f', RTGETOPT_REQ_NOTHING },
3423 { "--as-c-array", 'C', RTGETOPT_REQ_STRING },
3424 };
3425
3426 const char *pszExe = NULL;
3427 const char *pszOut = NULL;
3428 RTLDRARCH enmLdrArch = RTLDRARCH_WHATEVER;
3429 unsigned cVerbosity = 0;
3430 uint32_t fCursorFlags = RTASN1CURSOR_FLAGS_DER;
3431 uint32_t iSignature = 0;
3432 const char *pszCArrayNm = NULL;
3433 bool fForce = false;
3434
3435 RTGETOPTSTATE GetState;
3436 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
3437 AssertRCReturn(rc, RTEXITCODE_FAILURE);
3438 RTGETOPTUNION ValueUnion;
3439 int ch;
3440 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
3441 {
3442 switch (ch)
3443 {
3444 case 'e': pszExe = ValueUnion.psz; break;
3445 case 'o': pszOut = ValueUnion.psz; break;
3446 case 'b': fCursorFlags = 0; break;
3447 case 'c': fCursorFlags = RTASN1CURSOR_FLAGS_CER; break;
3448 case 'd': fCursorFlags = RTASN1CURSOR_FLAGS_DER; break;
3449 case 'f': fForce = true; break;
3450 case 'i': iSignature = ValueUnion.u32; break;
3451 case 'V': return HandleVersion(cArgs, papszArgs);
3452 case 'h': return HelpExtractExeSignerCert(g_pStdOut, RTSIGNTOOLHELP_FULL);
3453
3454 case 'C':
3455 pszCArrayNm = *ValueUnion.psz ? ValueUnion.psz : NULL;
3456 if (pszCArrayNm)
3457 iSignature = UINT32_MAX;
3458 break;
3459
3460 case VINF_GETOPT_NOT_OPTION:
3461 if (!pszExe)
3462 pszExe = ValueUnion.psz;
3463 else if (!pszOut)
3464 pszOut = ValueUnion.psz;
3465 else
3466 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Too many file arguments: %s", ValueUnion.psz);
3467 break;
3468
3469 default:
3470 return RTGetOptPrintError(ch, &ValueUnion);
3471 }
3472 }
3473 if (!pszExe)
3474 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No executable given.");
3475 if (!pszOut)
3476 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No output file given.");
3477 if (!fForce && RTPathExists(pszOut))
3478 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The output file '%s' exists.", pszOut);
3479
3480 /*
3481 * Do it.
3482 */
3483 /* Read & decode the PKCS#7 signature. */
3484 SIGNTOOLPKCS7EXE This;
3485 RTEXITCODE rcExit = SignToolPkcs7Exe_InitFromFile(&This, pszExe, cVerbosity, enmLdrArch);
3486 if (rcExit == RTEXITCODE_SUCCESS)
3487 {
3488 if (pszCArrayNm == NULL || iSignature != UINT32_MAX)
3489 {
3490 /* Find the signing certificate (ASSUMING that the certificate used is shipped in the set of certificates). */
3491 PRTCRPKCS7SIGNEDDATA pSignedData;
3492 PCRTCRPKCS7SIGNERINFO pSignerInfo = SignToolPkcs7_FindNestedSignatureByIndex(&This, iSignature, &pSignedData);
3493 rcExit = RTEXITCODE_FAILURE;
3494 if (pSignerInfo)
3495 {
3496 PCRTCRPKCS7ISSUERANDSERIALNUMBER pISN = &pSignedData->SignerInfos.papItems[0]->IssuerAndSerialNumber;
3497 PCRTCRX509CERTIFICATE pCert;
3498 pCert = RTCrPkcs7SetOfCerts_FindX509ByIssuerAndSerialNumber(&pSignedData->Certificates,
3499 &pISN->Name, &pISN->SerialNumber);
3500 if (pCert)
3501 {
3502 /*
3503 * Write it out.
3504 */
3505 if (pszCArrayNm == NULL)
3506 rcExit = WriteCertToFile(pCert, pszOut, fForce);
3507 else
3508 rcExit = WriteCertToFileAsC(pCert, pszOut, fForce, pszCArrayNm);
3509 }
3510 else
3511 RTMsgError("Certificate not found.");
3512 }
3513 else
3514 RTMsgError("Could not locate signature #%u!", iSignature);
3515 }
3516 else
3517 {
3518 uint32_t const cSignatures = SignToolPkcs7_CountSignatures(&This);
3519 if (cSignatures)
3520 {
3521 PRTSTREAM pStrm = NULL;
3522 rc = RTStrmOpen(pszOut, fForce ? "wt+" : "wtx", &pStrm);
3523 if (RT_SUCCESS(rc))
3524 {
3525 for (iSignature = 0; iSignature < cSignatures; iSignature++)
3526 {
3527 PRTCRPKCS7SIGNEDDATA pSignedData;
3528 PCRTCRPKCS7SIGNERINFO pSignerInfo = SignToolPkcs7_FindNestedSignatureByIndex(&This, iSignature, &pSignedData);
3529 if (!pSignerInfo)
3530 {
3531 rcExit = RTMsgErrorExitFailure("Could not locate signature #%u out of %u!", iSignature, cSignatures);
3532 break;
3533 }
3534 PCRTCRPKCS7ISSUERANDSERIALNUMBER pISN = &pSignedData->SignerInfos.papItems[0]->IssuerAndSerialNumber;
3535 PCRTCRX509CERTIFICATE pCert;
3536 pCert = RTCrPkcs7SetOfCerts_FindX509ByIssuerAndSerialNumber(&pSignedData->Certificates,
3537 &pISN->Name, &pISN->SerialNumber);
3538 if (pCert)
3539 PrintCertAsCArray(pCert, iSignature, pszCArrayNm, pStrm);
3540 else
3541 {
3542 rcExit = RTMsgErrorExitFailure("Could not locate certificate for signature #%u (out of %u)!",
3543 iSignature, cSignatures);
3544 break;
3545 }
3546 }
3547
3548 PrintCertTableAsC(iSignature, pszCArrayNm, pStrm);
3549
3550 rc = RTStrmClose(pStrm);
3551 if (RT_FAILURE(rc))
3552 rcExit = RTMsgErrorExitFailure("Error writing/closing '%s': %Rrc", pszOut, rc);
3553 }
3554 else
3555 rcExit = RTMsgErrorExitFailure("Failed to open '%s' for writing: %Rrc", pszOut, rc);
3556 }
3557 else
3558 rcExit = RTMsgErrorExitFailure("No signatures found!");
3559 }
3560
3561 /* Delete the signature data. */
3562 SignToolPkcs7Exe_Delete(&This);
3563 }
3564 return rcExit;
3565}
3566
3567
3568/*********************************************************************************************************************************
3569* The 'extract-signer-root' & 'extract-timestamp-root' commands. *
3570*********************************************************************************************************************************/
3571class BaseExtractState
3572{
3573public:
3574 const char *pszFile;
3575 const char *pszOut;
3576 RTLDRARCH enmLdrArch;
3577 unsigned cVerbosity;
3578 uint32_t iSignature;
3579 bool fForce;
3580 /** Timestamp or main signature. */
3581 bool const fTimestamp;
3582 const char *pszBaseNm;
3583
3584 BaseExtractState(bool a_fTimestamp)
3585 : pszFile(NULL)
3586 , pszOut(NULL)
3587 , enmLdrArch(RTLDRARCH_WHATEVER)
3588 , cVerbosity(0)
3589 , iSignature(0)
3590 , fForce(false)
3591 , fTimestamp(a_fTimestamp)
3592 , pszBaseNm(NULL)
3593 {
3594 }
3595};
3596
3597class RootExtractState : public BaseExtractState
3598{
3599public:
3600 CryptoStore RootStore;
3601 CryptoStore AdditionalStore;
3602
3603 RootExtractState(bool a_fTimestamp)
3604 : BaseExtractState(a_fTimestamp)
3605 , RootStore()
3606 , AdditionalStore()
3607 { }
3608
3609 /**
3610 * Creates the two stores, filling the root one with trusted CAs and
3611 * certificates found on the system or in the user's account.
3612 */
3613 bool init(void)
3614 {
3615 int rc = RTCrStoreCreateInMem(&this->RootStore.m_hStore, 0);
3616 if (RT_SUCCESS(rc))
3617 {
3618 rc = RTCrStoreCreateInMem(&this->AdditionalStore.m_hStore, 0);
3619 if (RT_SUCCESS(rc))
3620 return true;
3621 }
3622 RTMsgError("RTCrStoreCreateInMem failed: %Rrc", rc);
3623 return false;
3624 }
3625};
3626
3627
3628/**
3629 * Locates the target signature and certificate collection.
3630 */
3631static PRTCRPKCS7SIGNERINFO BaseExtractFindSignerInfo(SIGNTOOLPKCS7 *pThis, BaseExtractState *pState, bool fOptional,
3632 PRTCRPKCS7SIGNEDDATA *ppSignedData, PCRTCRPKCS7SETOFCERTS *ppCerts)
3633{
3634 *ppSignedData = NULL;
3635 *ppCerts = NULL;
3636
3637 /*
3638 * Locate the target signature.
3639 */
3640 PRTCRPKCS7SIGNEDDATA pSignedData = NULL;
3641 PRTCRPKCS7SIGNERINFO pSignerInfo = SignToolPkcs7_FindNestedSignatureByIndex(pThis, pState->iSignature, &pSignedData);
3642 if (pSignerInfo)
3643 {
3644 /*
3645 * If the target is the timestamp we have to locate the relevant
3646 * timestamp signature and adjust the return values.
3647 */
3648 if (pState->fTimestamp)
3649 {
3650 for (uint32_t iItem = 0; iItem < pSignerInfo->UnauthenticatedAttributes.cItems; iItem++)
3651 {
3652 PCRTCRPKCS7ATTRIBUTE pAttr = pSignerInfo->UnauthenticatedAttributes.papItems[iItem];
3653 if (pAttr->enmType == RTCRPKCS7ATTRIBUTETYPE_COUNTER_SIGNATURES)
3654 {
3655 /* ASSUME that all counter signatures are timestamping. */
3656 if (pAttr->uValues.pCounterSignatures->cItems > 0)
3657 {
3658 *ppSignedData = pSignedData;
3659 *ppCerts = &pSignedData->Certificates;
3660 return pAttr->uValues.pCounterSignatures->papItems[0];
3661 }
3662 RTMsgWarning("Timestamp signature attribute is empty!");
3663 }
3664 else if (pAttr->enmType == RTCRPKCS7ATTRIBUTETYPE_MS_TIMESTAMP)
3665 {
3666 /* ASSUME that all valid timestamp signatures for now, pick the first. */
3667 if (pAttr->uValues.pContentInfos->cItems > 0)
3668 {
3669 PCRTCRPKCS7CONTENTINFO pContentInfo = pAttr->uValues.pContentInfos->papItems[0];
3670 if (RTAsn1ObjId_CompareWithString(&pContentInfo->ContentType, RTCR_PKCS7_SIGNED_DATA_OID) == 0)
3671 {
3672 pSignedData = pContentInfo->u.pSignedData;
3673 if (RTAsn1ObjId_CompareWithString(&pSignedData->ContentInfo.ContentType, RTCRTSPTSTINFO_OID) == 0)
3674 {
3675 if (pSignedData->SignerInfos.cItems > 0)
3676 {
3677 *ppSignedData = pSignedData;
3678 *ppCerts = &pSignedData->Certificates;
3679 return pSignedData->SignerInfos.papItems[0];
3680 }
3681 RTMsgWarning("Timestamp signature has no signers!");
3682 }
3683 else
3684 RTMsgWarning("Timestamp signature contains wrong content (%s)!",
3685 pSignedData->ContentInfo.ContentType.szObjId);
3686 }
3687 else
3688 RTMsgWarning("Timestamp signature is not SignedData but %s!", pContentInfo->ContentType.szObjId);
3689 }
3690 else
3691 RTMsgWarning("Timestamp signature attribute is empty!");
3692 }
3693 }
3694 if (!fOptional)
3695 RTMsgError("Cound not find a timestamp signature associated with signature #%u!", pState->iSignature);
3696 pSignerInfo = NULL;
3697 }
3698 else
3699 {
3700 *ppSignedData = pSignedData;
3701 *ppCerts = &pSignedData->Certificates;
3702 }
3703 }
3704 else if (!fOptional)
3705 RTMsgError("Could not locate signature #%u!", pState->iSignature);
3706 return pSignerInfo;
3707}
3708
3709
3710/** @callback_method_impl{FNRTDUMPPRINTFV} */
3711static DECLCALLBACK(void) DumpToStdOutPrintfV(void *pvUser, const char *pszFormat, va_list va)
3712{
3713 RT_NOREF(pvUser);
3714 RTPrintfV(pszFormat, va);
3715}
3716
3717
3718static RTEXITCODE RootExtractWorker3(SIGNTOOLPKCS7 *pThis, RootExtractState *pState, PRTSTREAM pStrm, uint32_t *pidxCert,
3719 PRTERRINFOSTATIC pStaticErrInfo)
3720{
3721 /*
3722 * Locate the target signature.
3723 */
3724 bool const fOptional = pidxCert != NULL && pStrm != NULL && pState->fTimestamp /*fOptional*/;
3725 PRTCRPKCS7SIGNEDDATA pSignedData;
3726 PCRTCRPKCS7SETOFCERTS pCerts;
3727 PCRTCRPKCS7SIGNERINFO pSignerInfo = BaseExtractFindSignerInfo(pThis, pState, fOptional, &pSignedData, &pCerts);
3728 if (!pSignerInfo)
3729 {
3730 if (!fOptional)
3731 return RTMsgErrorExitFailure("Could not locate signature #%u!", pState->iSignature);
3732 return RTEXITCODE_SUCCESS;
3733 }
3734
3735
3736 /* The next bit is modelled on first half of rtCrPkcs7VerifySignerInfo. */
3737
3738 /*
3739 * Locate the signing certificate.
3740 */
3741 PCRTCRCERTCTX pSignerCertCtx = RTCrStoreCertByIssuerAndSerialNo(pState->RootStore.m_hStore,
3742 &pSignerInfo->IssuerAndSerialNumber.Name,
3743 &pSignerInfo->IssuerAndSerialNumber.SerialNumber);
3744 if (!pSignerCertCtx)
3745 pSignerCertCtx = RTCrStoreCertByIssuerAndSerialNo(pState->AdditionalStore.m_hStore,
3746 &pSignerInfo->IssuerAndSerialNumber.Name,
3747 &pSignerInfo->IssuerAndSerialNumber.SerialNumber);
3748
3749 PCRTCRX509CERTIFICATE pSignerCert;
3750 if (pSignerCertCtx)
3751 pSignerCert = pSignerCertCtx->pCert;
3752 else
3753 {
3754 pSignerCert = RTCrPkcs7SetOfCerts_FindX509ByIssuerAndSerialNumber(pCerts,
3755 &pSignerInfo->IssuerAndSerialNumber.Name,
3756 &pSignerInfo->IssuerAndSerialNumber.SerialNumber);
3757 if (!pSignerCert)
3758 return RTMsgErrorExitFailure("Certificate not found: serial=%.*Rhxs",
3759 pSignerInfo->IssuerAndSerialNumber.SerialNumber.Asn1Core.cb,
3760 pSignerInfo->IssuerAndSerialNumber.SerialNumber.Asn1Core.uData.pv);
3761 }
3762
3763 /*
3764 * Now we build paths so we can get to the root certificate.
3765 */
3766 RTCRX509CERTPATHS hCertPaths;
3767 int rc = RTCrX509CertPathsCreate(&hCertPaths, pSignerCert);
3768 if (RT_FAILURE(rc))
3769 return RTMsgErrorExitFailure("RTCrX509CertPathsCreate failed: %Rrc", rc);
3770
3771 /* Configure: */
3772 RTEXITCODE rcExit = RTEXITCODE_FAILURE;
3773 rc = RTCrX509CertPathsSetTrustedStore(hCertPaths, pState->RootStore.m_hStore);
3774 if (RT_SUCCESS(rc))
3775 {
3776 rc = RTCrX509CertPathsSetUntrustedStore(hCertPaths, pState->AdditionalStore.m_hStore);
3777 if (RT_SUCCESS(rc))
3778 {
3779 rc = RTCrX509CertPathsSetUntrustedSet(hCertPaths, pCerts);
3780 if (RT_SUCCESS(rc))
3781 {
3782 /* We don't technically need this, I think. */
3783 rc = RTCrX509CertPathsSetTrustAnchorChecks(hCertPaths, true /*fEnable*/);
3784 if (RT_SUCCESS(rc))
3785 {
3786 /* Seems we might need this for the sha-1 certs and such. */
3787 RTCrX509CertPathsSetValidTimeSpec(hCertPaths, NULL);
3788
3789 /* Build the paths: */
3790 rc = RTCrX509CertPathsBuild(hCertPaths, RTErrInfoInitStatic(pStaticErrInfo));
3791 if (RT_SUCCESS(rc))
3792 {
3793 uint32_t const cPaths = RTCrX509CertPathsGetPathCount(hCertPaths);
3794
3795 /* Validate the paths: */
3796 uint32_t cValidPaths = 0;
3797 rc = RTCrX509CertPathsValidateAll(hCertPaths, &cValidPaths, RTErrInfoInitStatic(pStaticErrInfo));
3798 if (RT_SUCCESS(rc))
3799 {
3800 if (pState->cVerbosity > 0)
3801 RTMsgInfo("%u of %u paths are valid", cValidPaths, cPaths);
3802 if (pState->cVerbosity > 1)
3803 RTCrX509CertPathsDumpAll(hCertPaths, pState->cVerbosity, DumpToStdOutPrintfV, NULL);
3804
3805 /*
3806 * Now, pick the first valid path with a real certificate at the end.
3807 */
3808 for (uint32_t iPath = 0; iPath < cPaths; iPath++)
3809 {
3810 PCRTCRX509CERTIFICATE pRootCert = NULL;
3811 PCRTCRX509NAME pSubject = NULL;
3812 bool fTrusted = false;
3813 int rcVerify = -1;
3814 rc = RTCrX509CertPathsQueryPathInfo(hCertPaths, iPath, &fTrusted, NULL /*pcNodes*/,
3815 &pSubject, NULL, &pRootCert, NULL /*ppCertCtx*/, &rcVerify);
3816 if (RT_SUCCESS(rc))
3817 {
3818 if (fTrusted && RT_SUCCESS(rcVerify) && pRootCert)
3819 {
3820 /*
3821 * Now copy out the certificate.
3822 */
3823 if (!pState->pszBaseNm)
3824 rcExit = WriteCertToFile(pRootCert, pState->pszOut, pState->fForce);
3825 else if (!pStrm)
3826 rcExit = WriteCertToFileAsC(pRootCert, pState->pszOut, pState->fForce,
3827 pState->pszBaseNm);
3828 else
3829 {
3830 PrintCertAsCArray(pRootCert, *pidxCert, pState->pszBaseNm, pStrm);
3831 *pidxCert += 1;
3832 rcExit = RTEXITCODE_SUCCESS;
3833 }
3834 break;
3835 }
3836 }
3837 else
3838 {
3839 RTMsgError("RTCrX509CertPathsQueryPathInfo failed: %Rrc", rc);
3840 break;
3841 }
3842 }
3843 }
3844 else
3845 {
3846 RTMsgError("RTCrX509CertPathsValidateAll failed: %Rrc%#RTeim", rc, &pStaticErrInfo->Core);
3847 RTCrX509CertPathsDumpAll(hCertPaths, pState->cVerbosity, DumpToStdOutPrintfV, NULL);
3848 }
3849 }
3850 else
3851 RTMsgError("RTCrX509CertPathsBuild failed: %Rrc%#RTeim", rc, &pStaticErrInfo->Core);
3852 }
3853 else
3854 RTMsgError("RTCrX509CertPathsSetTrustAnchorChecks failed: %Rrc", rc);
3855 }
3856 else
3857 RTMsgError("RTCrX509CertPathsSetUntrustedSet failed: %Rrc", rc);
3858 }
3859 else
3860 RTMsgError("RTCrX509CertPathsSetUntrustedStore failed: %Rrc", rc);
3861 }
3862 else
3863 RTMsgError("RTCrX509CertPathsSetTrustedStore failed: %Rrc", rc);
3864
3865 uint32_t cRefs = RTCrX509CertPathsRelease(hCertPaths);
3866 Assert(cRefs == 0); RT_NOREF(cRefs);
3867
3868 return rcExit;
3869}
3870
3871
3872static RTEXITCODE RootExtractWorker2(SIGNTOOLPKCS7 *pThis, RootExtractState *pState, PRTERRINFOSTATIC pStaticErrInfo)
3873{
3874 if (!pState->pszBaseNm || pState->iSignature != UINT32_MAX)
3875 return RootExtractWorker3(pThis, pState, NULL, NULL, pStaticErrInfo);
3876
3877 /*
3878 * Dump all these certificates.
3879 */
3880 uint32_t const cSignatures = SignToolPkcs7_CountSignatures(pThis);
3881 if (!cSignatures)
3882 return RTMsgErrorExitFailure("No signatures found!");
3883
3884 PRTSTREAM pStrm = NULL;
3885 int rc = RTStrmOpen(pState->pszOut, pState->fForce ? "wt+" : "wtx", &pStrm);
3886 if (RT_FAILURE(rc))
3887 return RTMsgErrorExitFailure("Failed to open '%s' for writing: %Rrc", pState->pszOut, rc);
3888
3889 uint32_t idxCert = 0; /* tracking separately, as we ignore missing timestamp chains. */
3890 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
3891 for (uint32_t iSignature = 0; iSignature < cSignatures && rcExit == RTEXITCODE_SUCCESS; iSignature++)
3892 {
3893 pState->iSignature = iSignature;
3894 rcExit = RootExtractWorker3(pThis, pState, pStrm, &idxCert, pStaticErrInfo);
3895 }
3896
3897 PrintCertTableAsC(idxCert, pState->pszBaseNm, pStrm);
3898
3899 rc = RTStrmClose(pStrm);
3900 if (RT_FAILURE(rc))
3901 rcExit = RTMsgErrorExitFailure("Error writing/closing '%s': %Rrc", pState->pszOut, rc);
3902 return rcExit;
3903}
3904
3905
3906static RTEXITCODE RootExtractWorker(RootExtractState *pState, PRTERRINFOSTATIC pStaticErrInfo)
3907{
3908 /*
3909 * Check that all we need is there and whether the output file exists.
3910 */
3911 if (!pState->pszFile)
3912 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No executable given.");
3913 if (!pState->pszOut)
3914 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No output file given.");
3915 if (!pState->fForce && RTPathExists(pState->pszOut))
3916 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The output file '%s' exists.", pState->pszOut);
3917
3918 /*
3919 * Detect the type of file we're dealing with, do type specific setup and
3920 * call common worker to do the rest.
3921 */
3922 RTEXITCODE rcExit;
3923 RTSIGNTOOLFILETYPE enmFileType = DetectFileType(RTSIGNTOOLFILETYPE_DETECT, pState->pszFile);
3924 if (enmFileType == RTSIGNTOOLFILETYPE_EXE)
3925 {
3926 SIGNTOOLPKCS7EXE Exe;
3927 rcExit = SignToolPkcs7Exe_InitFromFile(&Exe, pState->pszFile, pState->cVerbosity, pState->enmLdrArch);
3928 if (rcExit == RTEXITCODE_SUCCESS)
3929 {
3930 rcExit = RootExtractWorker2(&Exe, pState, pStaticErrInfo);
3931 SignToolPkcs7Exe_Delete(&Exe);
3932 }
3933 }
3934 else if (enmFileType == RTSIGNTOOLFILETYPE_CAT)
3935 {
3936 SIGNTOOLPKCS7 Cat;
3937 rcExit = SignToolPkcs7_InitFromFile(&Cat, pState->pszFile, pState->cVerbosity);
3938 if (rcExit == RTEXITCODE_SUCCESS)
3939 {
3940 rcExit = RootExtractWorker2(&Cat, pState, pStaticErrInfo);
3941 SignToolPkcs7_Delete(&Cat);
3942 }
3943 }
3944 else
3945 rcExit = RTEXITCODE_FAILURE;
3946 return rcExit;
3947}
3948
3949
3950static RTEXITCODE HelpExtractRootCommon(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel, bool fTimestamp)
3951{
3952 RT_NOREF_PV(enmLevel);
3953 RTStrmWrappedPrintf(pStrm, RTSTRMWRAPPED_F_HANGING_INDENT,
3954 "extract-%s-root [-v|--verbose] [-q|--quiet] [--as-c-array <name>] [--signature-index|-i <num>] "
3955 "[--root <root-cert.der>] [--self-signed-roots-from-system] [--additional <supp-cert.der>] "
3956 "[--intermediate-certs-from-system] [--input] <signed-file> "
3957 "[-f|--force] [--output|-o] <outfile.cer/h>\n",
3958 fTimestamp ? "timestamp" : "signer");
3959 if (enmLevel == RTSIGNTOOLHELP_FULL)
3960 {
3961 RTStrmWrappedPrintf(pStrm, 0,
3962 "\n"
3963 "Extracts the root certificate of the %sgiven "
3964 "signature. If there are more than one valid certificate path, the first one with "
3965 "a full certificate will be picked.\n",
3966 fTimestamp ? "first timestamp associated with the " : "");
3967 RTStrmWrappedPrintf(pStrm, 0,
3968 "\n"
3969 "Options:\n"
3970 " -v, --verbose, -q, --quite\n"
3971 " Controls the noise level. The '-v' options are accumlative while '-q' is absolute.\n"
3972 " Default: -q\n"
3973 " -C <name>, --as-c-array <name>\n"
3974 " Output a C header file containing the roots of all signatures (or a selected one if"
3975 "--signature-index is used again after this option.\n"
3976 " Default: Output one binary certificate.\n"
3977 " -i <num>, --signature-index <num>\n"
3978 " Zero-based index of the signature to extract the root for.\n"
3979 " Default: -i 0\n"
3980 " -r <root-cert.file>, --root <root-cert.file>\n"
3981 " Use the certificate(s) in the specified file as a trusted root(s). "
3982 "The file format can be PEM or DER.\n"
3983 " -R, --self-signed-roots-from-system\n"
3984 " Use all self-signed trusted root certificates found on the system and associated with the "
3985 "current user as trusted roots. This is limited to self-signed certificates, so that we get "
3986 "a full chain even if a non-end-entity certificate is present in any of those system stores for "
3987 "some reason.\n"
3988 " -a <supp-cert.file>, --additional <supp-cert.file>\n"
3989 " Use the certificate(s) in the specified file as a untrusted intermediate certificates. "
3990 "The file format can be PEM or DER.\n"
3991 " -A, --intermediate-certs-from-system\n"
3992 " Use all certificates found on the system and associated with the current user as intermediate "
3993 "certification authorities.\n"
3994 " --input <signed-file>\n"
3995 " Signed executable or security cabinet file to examine. The '--input' option bit is optional "
3996 "and there to allow more flexible parameter ordering.\n"
3997 " -f, --force\n"
3998 " Overwrite existing output file. The default is not to overwriting any existing file.\n"
3999 " -o <outfile.cer> --output <outfile.cer>\n"
4000 " The name of the output file. Again the '-o|--output' bit is optional and only for flexibility.\n"
4001 );
4002 }
4003 return RTEXITCODE_SUCCESS;
4004}
4005
4006
4007static RTEXITCODE HandleExtractRootCommon(int cArgs, char **papszArgs, bool fTimestamp)
4008{
4009 /*
4010 * Parse arguments.
4011 */
4012 static const RTGETOPTDEF s_aOptions[] =
4013 {
4014 { "--root", 'r', RTGETOPT_REQ_STRING },
4015 { "--self-signed-roots-from-system", 'R', RTGETOPT_REQ_NOTHING },
4016 { "--additional", 'a', RTGETOPT_REQ_STRING },
4017 { "--intermediate-certs-from-system",'A', RTGETOPT_REQ_NOTHING },
4018 { "--add", 'a', RTGETOPT_REQ_STRING },
4019 { "--input", 'I', RTGETOPT_REQ_STRING },
4020 { "--output", 'o', RTGETOPT_REQ_STRING },
4021 { "--signature-index", 'i', RTGETOPT_REQ_UINT32 },
4022 { "--force", 'f', RTGETOPT_REQ_NOTHING },
4023 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
4024 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
4025 { "--as-c-array", 'C', RTGETOPT_REQ_STRING },
4026 };
4027 RTERRINFOSTATIC StaticErrInfo;
4028 RootExtractState State(fTimestamp);
4029 if (!State.init())
4030 return RTEXITCODE_FAILURE;
4031 RTGETOPTSTATE GetState;
4032 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
4033 AssertRCReturn(rc, RTEXITCODE_FAILURE);
4034 RTGETOPTUNION ValueUnion;
4035 int ch;
4036 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
4037 {
4038 switch (ch)
4039 {
4040 case 'a':
4041 if (!State.AdditionalStore.addFromFile(ValueUnion.psz, &StaticErrInfo))
4042 return RTEXITCODE_FAILURE;
4043 break;
4044
4045 case 'A':
4046 if (!State.AdditionalStore.addIntermediateCertsFromSystem(&StaticErrInfo))
4047 return RTEXITCODE_FAILURE;
4048 break;
4049
4050 case 'r':
4051 if (!State.RootStore.addFromFile(ValueUnion.psz, &StaticErrInfo))
4052 return RTEXITCODE_FAILURE;
4053 break;
4054
4055 case 'R':
4056 if (!State.RootStore.addSelfSignedRootsFromSystem(&StaticErrInfo))
4057 return RTEXITCODE_FAILURE;
4058 break;
4059
4060 case 'I': State.pszFile = ValueUnion.psz; break;
4061 case 'o': State.pszOut = ValueUnion.psz; break;
4062 case 'f': State.fForce = true; break;
4063 case 'i': State.iSignature = ValueUnion.u32; break;
4064 case 'v': State.cVerbosity++; break;
4065 case 'q': State.cVerbosity = 0; break;
4066 case 'V': return HandleVersion(cArgs, papszArgs);
4067 case 'h': return HelpExtractRootCommon(g_pStdOut, RTSIGNTOOLHELP_FULL, fTimestamp);
4068
4069 case 'C':
4070 State.pszBaseNm = *ValueUnion.psz ? ValueUnion.psz : NULL;
4071 if (State.pszBaseNm)
4072 State.iSignature = UINT32_MAX;
4073 break;
4074
4075 case VINF_GETOPT_NOT_OPTION:
4076 if (!State.pszFile)
4077 State.pszFile = ValueUnion.psz;
4078 else if (!State.pszOut)
4079 State.pszOut = ValueUnion.psz;
4080 else
4081 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Too many file arguments: %s", ValueUnion.psz);
4082 break;
4083
4084 default:
4085 return RTGetOptPrintError(ch, &ValueUnion);
4086 }
4087 }
4088 return RootExtractWorker(&State, &StaticErrInfo);
4089}
4090
4091
4092static RTEXITCODE HelpExtractSignerRoot(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
4093{
4094 return HelpExtractRootCommon(pStrm, enmLevel, false /*fTimestamp*/);
4095}
4096
4097
4098static RTEXITCODE HandleExtractSignerRoot(int cArgs, char **papszArgs)
4099{
4100 return HandleExtractRootCommon(cArgs, papszArgs, false /*fTimestamp*/ );
4101}
4102
4103
4104static RTEXITCODE HelpExtractTimestampRoot(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
4105{
4106 return HelpExtractRootCommon(pStrm, enmLevel, true /*fTimestamp*/);
4107}
4108
4109
4110static RTEXITCODE HandleExtractTimestampRoot(int cArgs, char **papszArgs)
4111{
4112 return HandleExtractRootCommon(cArgs, papszArgs, true /*fTimestamp*/ );
4113}
4114
4115
4116/*********************************************************************************************************************************
4117* The 'extract-exe-signature' command. *
4118*********************************************************************************************************************************/
4119
4120static RTEXITCODE HelpExtractExeSignature(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
4121{
4122 RT_NOREF_PV(enmLevel);
4123 RTStrmWrappedPrintf(pStrm, RTSTRMWRAPPED_F_HANGING_INDENT,
4124 "extract-exe-signerature [--input|--exe|-e] <exe> [--output|-o] <outfile.pkcs7>\n");
4125 return RTEXITCODE_SUCCESS;
4126}
4127
4128static RTEXITCODE HandleExtractExeSignature(int cArgs, char **papszArgs)
4129{
4130 /*
4131 * Parse arguments.
4132 */
4133 static const RTGETOPTDEF s_aOptions[] =
4134 {
4135 { "--exe", 'e', RTGETOPT_REQ_STRING },
4136 { "--input", 'e', RTGETOPT_REQ_STRING },
4137 { "--output", 'o', RTGETOPT_REQ_STRING },
4138 { "--force", 'f', RTGETOPT_REQ_NOTHING },
4139 };
4140
4141 const char *pszExe = NULL;
4142 const char *pszOut = NULL;
4143 RTLDRARCH enmLdrArch = RTLDRARCH_WHATEVER;
4144 unsigned cVerbosity = 0;
4145 bool fForce = false;
4146
4147 RTGETOPTSTATE GetState;
4148 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
4149 AssertRCReturn(rc, RTEXITCODE_FAILURE);
4150 RTGETOPTUNION ValueUnion;
4151 int ch;
4152 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
4153 {
4154 switch (ch)
4155 {
4156 case 'e': pszExe = ValueUnion.psz; break;
4157 case 'o': pszOut = ValueUnion.psz; break;
4158 case 'f': fForce = true; break;
4159 case 'V': return HandleVersion(cArgs, papszArgs);
4160 case 'h': return HelpExtractExeSignerCert(g_pStdOut, RTSIGNTOOLHELP_FULL);
4161
4162 case VINF_GETOPT_NOT_OPTION:
4163 if (!pszExe)
4164 pszExe = ValueUnion.psz;
4165 else if (!pszOut)
4166 pszOut = ValueUnion.psz;
4167 else
4168 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Too many file arguments: %s", ValueUnion.psz);
4169 break;
4170
4171 default:
4172 return RTGetOptPrintError(ch, &ValueUnion);
4173 }
4174 }
4175 if (!pszExe)
4176 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No executable given.");
4177 if (!pszOut)
4178 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No output file given.");
4179 if (!fForce && RTPathExists(pszOut))
4180 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The output file '%s' exists.", pszOut);
4181
4182 /*
4183 * Do it.
4184 */
4185 /* Read & decode the PKCS#7 signature. */
4186 SIGNTOOLPKCS7EXE This;
4187 RTEXITCODE rcExit = SignToolPkcs7Exe_InitFromFile(&This, pszExe, cVerbosity, enmLdrArch);
4188 if (rcExit == RTEXITCODE_SUCCESS)
4189 {
4190 /*
4191 * Write out the PKCS#7 signature.
4192 */
4193 RTFILE hFile;
4194 rc = RTFileOpen(&hFile, pszOut,
4195 RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | (fForce ? RTFILE_O_CREATE_REPLACE : RTFILE_O_CREATE));
4196 if (RT_SUCCESS(rc))
4197 {
4198 rc = RTFileWrite(hFile, This.pbBuf, This.cbBuf, NULL);
4199 if (RT_SUCCESS(rc))
4200 {
4201 rc = RTFileClose(hFile);
4202 if (RT_SUCCESS(rc))
4203 {
4204 hFile = NIL_RTFILE;
4205 RTMsgInfo("Successfully wrote %u bytes to '%s'", This.cbBuf, pszOut);
4206 rcExit = RTEXITCODE_SUCCESS;
4207 }
4208 else
4209 RTMsgError("RTFileClose failed: %Rrc", rc);
4210 }
4211 else
4212 RTMsgError("RTFileWrite failed: %Rrc", rc);
4213 RTFileClose(hFile);
4214 }
4215 else
4216 RTMsgError("Error opening '%s' for writing: %Rrc", pszOut, rc);
4217
4218 /* Delete the signature data. */
4219 SignToolPkcs7Exe_Delete(&This);
4220 }
4221 return rcExit;
4222}
4223
4224
4225/*********************************************************************************************************************************
4226* The 'add-nested-exe-signature' command. *
4227*********************************************************************************************************************************/
4228
4229static RTEXITCODE HelpAddNestedExeSignature(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
4230{
4231 RT_NOREF_PV(enmLevel);
4232 RTStrmWrappedPrintf(pStrm, RTSTRMWRAPPED_F_HANGING_INDENT,
4233 "add-nested-exe-signature [-v|--verbose] [-d|--debug] [-p|--prepend] <destination-exe> <source-exe>\n");
4234 if (enmLevel == RTSIGNTOOLHELP_FULL)
4235 RTStrmWrappedPrintf(pStrm, 0,
4236 "\n"
4237 "The --debug option allows the source-exe to be omitted in order to test the "
4238 "encoding and PE file modification.\n"
4239 "\n"
4240 "The --prepend option puts the nested signature first rather than appending it "
4241 "to the end of of the nested signature set. Windows reads nested signatures in "
4242 "reverse order, so --prepend will logically putting it last.\n");
4243 return RTEXITCODE_SUCCESS;
4244}
4245
4246
4247static RTEXITCODE HandleAddNestedExeSignature(int cArgs, char **papszArgs)
4248{
4249 /*
4250 * Parse arguments.
4251 */
4252 static const RTGETOPTDEF s_aOptions[] =
4253 {
4254 { "--prepend", 'p', RTGETOPT_REQ_NOTHING },
4255 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
4256 { "--debug", 'd', RTGETOPT_REQ_NOTHING },
4257 };
4258
4259 const char *pszDst = NULL;
4260 const char *pszSrc = NULL;
4261 unsigned cVerbosity = 0;
4262 bool fDebug = false;
4263 bool fPrepend = false;
4264
4265 RTGETOPTSTATE GetState;
4266 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
4267 AssertRCReturn(rc, RTEXITCODE_FAILURE);
4268 RTGETOPTUNION ValueUnion;
4269 int ch;
4270 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
4271 {
4272 switch (ch)
4273 {
4274 case 'v': cVerbosity++; break;
4275 case 'd': fDebug = pszSrc == NULL; break;
4276 case 'p': fPrepend = true; break;
4277 case 'V': return HandleVersion(cArgs, papszArgs);
4278 case 'h': return HelpAddNestedExeSignature(g_pStdOut, RTSIGNTOOLHELP_FULL);
4279
4280 case VINF_GETOPT_NOT_OPTION:
4281 if (!pszDst)
4282 pszDst = ValueUnion.psz;
4283 else if (!pszSrc)
4284 {
4285 pszSrc = ValueUnion.psz;
4286 fDebug = false;
4287 }
4288 else
4289 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Too many file arguments: %s", ValueUnion.psz);
4290 break;
4291
4292 default:
4293 return RTGetOptPrintError(ch, &ValueUnion);
4294 }
4295 }
4296 if (!pszDst)
4297 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No destination executable given.");
4298 if (!pszSrc && !fDebug)
4299 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No source executable file given.");
4300
4301 /*
4302 * Do it.
4303 */
4304 /* Read & decode the source PKCS#7 signature. */
4305 SIGNTOOLPKCS7EXE Src;
4306 RTEXITCODE rcExit = pszSrc ? SignToolPkcs7Exe_InitFromFile(&Src, pszSrc, cVerbosity) : RTEXITCODE_SUCCESS;
4307 if (rcExit == RTEXITCODE_SUCCESS)
4308 {
4309 /* Ditto for the destination PKCS#7 signature. */
4310 SIGNTOOLPKCS7EXE Dst;
4311 rcExit = SignToolPkcs7Exe_InitFromFile(&Dst, pszDst, cVerbosity);
4312 if (rcExit == RTEXITCODE_SUCCESS)
4313 {
4314 /* Do the signature manipulation. */
4315 if (pszSrc)
4316 rcExit = SignToolPkcs7_AddNestedSignature(&Dst, &Src, cVerbosity, fPrepend);
4317 if (rcExit == RTEXITCODE_SUCCESS)
4318 rcExit = SignToolPkcs7_Encode(&Dst, cVerbosity);
4319
4320 /* Update the destination executable file. */
4321 if (rcExit == RTEXITCODE_SUCCESS)
4322 rcExit = SignToolPkcs7Exe_WriteSignatureToFile(&Dst, cVerbosity);
4323
4324 SignToolPkcs7Exe_Delete(&Dst);
4325 }
4326 if (pszSrc)
4327 SignToolPkcs7Exe_Delete(&Src);
4328 }
4329
4330 return rcExit;
4331}
4332
4333
4334/*********************************************************************************************************************************
4335* The 'add-nested-cat-signature' command. *
4336*********************************************************************************************************************************/
4337
4338static RTEXITCODE HelpAddNestedCatSignature(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
4339{
4340 RT_NOREF_PV(enmLevel);
4341 RTStrmWrappedPrintf(pStrm, RTSTRMWRAPPED_F_HANGING_INDENT,
4342 "add-nested-cat-signature [-v|--verbose] [-d|--debug] [-p|--prepend] <destination-cat> <source-cat>\n");
4343 if (enmLevel == RTSIGNTOOLHELP_FULL)
4344 RTStrmWrappedPrintf(pStrm, 0,
4345 "\n"
4346 "The --debug option allows the source-cat to be omitted in order to test the "
4347 "ASN.1 re-encoding of the destination catalog file.\n"
4348 "\n"
4349 "The --prepend option puts the nested signature first rather than appending it "
4350 "to the end of of the nested signature set. Windows reads nested signatures in "
4351 "reverse order, so --prepend will logically putting it last.\n");
4352 return RTEXITCODE_SUCCESS;
4353}
4354
4355
4356static RTEXITCODE HandleAddNestedCatSignature(int cArgs, char **papszArgs)
4357{
4358 /*
4359 * Parse arguments.
4360 */
4361 static const RTGETOPTDEF s_aOptions[] =
4362 {
4363 { "--prepend", 'p', RTGETOPT_REQ_NOTHING },
4364 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
4365 { "--debug", 'd', RTGETOPT_REQ_NOTHING },
4366 };
4367
4368 const char *pszDst = NULL;
4369 const char *pszSrc = NULL;
4370 unsigned cVerbosity = 0;
4371 bool fDebug = false;
4372 bool fPrepend = false;
4373
4374 RTGETOPTSTATE GetState;
4375 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
4376 AssertRCReturn(rc, RTEXITCODE_FAILURE);
4377 RTGETOPTUNION ValueUnion;
4378 int ch;
4379 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
4380 {
4381 switch (ch)
4382 {
4383 case 'v': cVerbosity++; break;
4384 case 'd': fDebug = pszSrc == NULL; break;
4385 case 'p': fPrepend = true; break;
4386 case 'V': return HandleVersion(cArgs, papszArgs);
4387 case 'h': return HelpAddNestedCatSignature(g_pStdOut, RTSIGNTOOLHELP_FULL);
4388
4389 case VINF_GETOPT_NOT_OPTION:
4390 if (!pszDst)
4391 pszDst = ValueUnion.psz;
4392 else if (!pszSrc)
4393 {
4394 pszSrc = ValueUnion.psz;
4395 fDebug = false;
4396 }
4397 else
4398 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Too many file arguments: %s", ValueUnion.psz);
4399 break;
4400
4401 default:
4402 return RTGetOptPrintError(ch, &ValueUnion);
4403 }
4404 }
4405 if (!pszDst)
4406 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No destination catalog file given.");
4407 if (!pszSrc && !fDebug)
4408 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No source catalog file given.");
4409
4410 /*
4411 * Do it.
4412 */
4413 /* Read & decode the source PKCS#7 signature. */
4414 SIGNTOOLPKCS7 Src;
4415 RTEXITCODE rcExit = pszSrc ? SignToolPkcs7_InitFromFile(&Src, pszSrc, cVerbosity) : RTEXITCODE_SUCCESS;
4416 if (rcExit == RTEXITCODE_SUCCESS)
4417 {
4418 /* Ditto for the destination PKCS#7 signature. */
4419 SIGNTOOLPKCS7EXE Dst;
4420 rcExit = SignToolPkcs7_InitFromFile(&Dst, pszDst, cVerbosity);
4421 if (rcExit == RTEXITCODE_SUCCESS)
4422 {
4423 /* Do the signature manipulation. */
4424 if (pszSrc)
4425 rcExit = SignToolPkcs7_AddNestedSignature(&Dst, &Src, cVerbosity, fPrepend);
4426 if (rcExit == RTEXITCODE_SUCCESS)
4427 rcExit = SignToolPkcs7_Encode(&Dst, cVerbosity);
4428
4429 /* Update the destination executable file. */
4430 if (rcExit == RTEXITCODE_SUCCESS)
4431 rcExit = SignToolPkcs7_WriteSignatureToFile(&Dst, pszDst, cVerbosity);
4432
4433 SignToolPkcs7_Delete(&Dst);
4434 }
4435 if (pszSrc)
4436 SignToolPkcs7_Delete(&Src);
4437 }
4438
4439 return rcExit;
4440}
4441
4442
4443/*********************************************************************************************************************************
4444* The 'add-timestamp-exe-signature' command. *
4445*********************************************************************************************************************************/
4446#ifndef IPRT_SIGNTOOL_NO_SIGNING
4447
4448static RTEXITCODE HelpAddTimestampExeSignature(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
4449{
4450 RT_NOREF_PV(enmLevel);
4451
4452 RTStrmWrappedPrintf(pStrm, RTSTRMWRAPPED_F_HANGING_INDENT,
4453 "add-timestamp-exe-signature [-v|--verbose] [--signature-index|-i <num>] "
4454 OPT_CERT_KEY_SYNOPSIS("--timestamp-", "")
4455 "[--timestamp-type old|new] "
4456 "[--timestamp-override <partial-isots>] "
4457 "[--replace-existing|-r] "
4458 "<exe>\n");
4459 if (enmLevel == RTSIGNTOOLHELP_FULL)
4460 RTStrmWrappedPrintf(pStrm, 0,
4461 "This is mainly to test timestamp code.\n"
4462 "\n"
4463 "The --timestamp-override option can take a partial or full ISO timestamp. It is merged "
4464 "with the current time if partial.\n"
4465 "\n");
4466 return RTEXITCODE_SUCCESS;
4467}
4468
4469static RTEXITCODE HandleAddTimestampExeSignature(int cArgs, char **papszArgs)
4470{
4471 /*
4472 * Parse arguments.
4473 */
4474 static const RTGETOPTDEF s_aOptions[] =
4475 {
4476 { "--signature-index", 'i', RTGETOPT_REQ_UINT32 },
4477 OPT_CERT_KEY_GETOPTDEF_ENTRIES("--timestamp-", "", 1000),
4478 { "--timestamp-type", OPT_TIMESTAMP_TYPE, RTGETOPT_REQ_STRING },
4479 { "--timestamp-override", OPT_TIMESTAMP_OVERRIDE, RTGETOPT_REQ_STRING },
4480 { "--replace-existing", 'r', RTGETOPT_REQ_NOTHING },
4481 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
4482 };
4483
4484 unsigned cVerbosity = 0;
4485 unsigned iSignature = 0;
4486 bool fReplaceExisting = false;
4487 SignToolTimestampOpts TimestampOpts("timestamp");
4488 RTTIMESPEC SigningTime;
4489 RTTimeNow(&SigningTime);
4490
4491 RTGETOPTSTATE GetState;
4492 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
4493 AssertRCReturn(rc, RTEXITCODE_FAILURE);
4494
4495 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
4496 RTGETOPTUNION ValueUnion;
4497 int ch;
4498 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
4499 {
4500 RTEXITCODE rcExit2 = RTEXITCODE_SUCCESS;
4501 switch (ch)
4502 {
4503 OPT_CERT_KEY_SWITCH_CASES(TimestampOpts, 1000, ch, ValueUnion, rcExit2);
4504 case 'i': iSignature = ValueUnion.u32; break;
4505 case OPT_TIMESTAMP_TYPE: rcExit2 = HandleOptTimestampType(&TimestampOpts, ValueUnion.psz); break;
4506 case OPT_TIMESTAMP_OVERRIDE: rcExit2 = HandleOptTimestampOverride(&SigningTime, ValueUnion.psz); break;
4507 case 'r': fReplaceExisting = true; break;
4508 case 'v': cVerbosity++; break;
4509 case 'V': return HandleVersion(cArgs, papszArgs);
4510 case 'h': return HelpAddTimestampExeSignature(g_pStdOut, RTSIGNTOOLHELP_FULL);
4511
4512 case VINF_GETOPT_NOT_OPTION:
4513 /* Do final certificate and key option processing (first file only). */
4514 rcExit2 = TimestampOpts.finalizeOptions(cVerbosity);
4515 if (rcExit2 == RTEXITCODE_SUCCESS)
4516 {
4517 /* Do the work: */
4518 SIGNTOOLPKCS7EXE Exe;
4519 rcExit2 = SignToolPkcs7Exe_InitFromFile(&Exe, ValueUnion.psz, cVerbosity);
4520 if (rcExit2 == RTEXITCODE_SUCCESS)
4521 {
4522 rcExit2 = SignToolPkcs7_AddTimestampSignature(&Exe, cVerbosity, iSignature, fReplaceExisting,
4523 SigningTime, &TimestampOpts);
4524 if (rcExit2 == RTEXITCODE_SUCCESS)
4525 rcExit2 = SignToolPkcs7_Encode(&Exe, cVerbosity);
4526 if (rcExit2 == RTEXITCODE_SUCCESS)
4527 rcExit2 = SignToolPkcs7Exe_WriteSignatureToFile(&Exe, cVerbosity);
4528 SignToolPkcs7Exe_Delete(&Exe);
4529 }
4530 if (rcExit2 != RTEXITCODE_SUCCESS && rcExit == RTEXITCODE_SUCCESS)
4531 rcExit = rcExit2;
4532 rcExit2 = RTEXITCODE_SUCCESS;
4533 }
4534 break;
4535
4536 default:
4537 return RTGetOptPrintError(ch, &ValueUnion);
4538 }
4539
4540 if (rcExit2 != RTEXITCODE_SUCCESS)
4541 {
4542 rcExit = rcExit2;
4543 break;
4544 }
4545 }
4546 return rcExit;
4547}
4548
4549#endif /*!IPRT_SIGNTOOL_NO_SIGNING */
4550
4551
4552/*********************************************************************************************************************************
4553* The 'sign-exe' command. *
4554*********************************************************************************************************************************/
4555#ifndef IPRT_SIGNTOOL_NO_SIGNING
4556
4557static RTEXITCODE HelpSign(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
4558{
4559 RT_NOREF_PV(enmLevel);
4560
4561 RTStrmWrappedPrintf(pStrm, RTSTRMWRAPPED_F_HANGING_INDENT,
4562 "sign [-v|--verbose] "
4563 "[--file-type exe|cat] "
4564 "[--type|/fd sha1|sha256] "
4565 "[--hash-pages|/ph] "
4566 "[--no-hash-pages|/nph] "
4567 "[--append/as] "
4568 "[--no-signing-time] "
4569 "[--add-cert <file>] "
4570 "[--timestamp-type old|new] "
4571 "[--timestamp-override <partial-isots>] "
4572 "[--verbose|/debug|-v] "
4573 OPT_CERT_KEY_SYNOPSIS("--", "")
4574 OPT_CERT_KEY_SYNOPSIS("--timestamp-", "")
4575 //OPT_CERT_KEY_SYNOPSIS("--timestamp-", "-2") - doesn't work, windows only uses one. Check again with new-style signatures
4576 "<exe>\n");
4577 if (enmLevel == RTSIGNTOOLHELP_FULL)
4578 RTStrmWrappedPrintf(pStrm, 0,
4579 "\n"
4580 "Create a new code signature for an executable or catalog.\n"
4581 "\n"
4582 "Options:\n"
4583 " --append, /as\n"
4584 " Append the signature if one already exists. The default is to replace any existing signature.\n"
4585 " --type sha1|sha256, /fd sha1|sha256\n"
4586 " Signature type, SHA-1 or SHA-256.\n"
4587 " --hash-pages, /ph, --no-page-hashes, /nph\n"
4588 " Enables or disables page hashing. Ignored for catalog files. Default: --no-page-hashes\n"
4589 " --add-cert <file>, /ac <file>\n"
4590 " Adds (first) certificate from the file to the signature. Both PEM and DER (binary) encodings "
4591 "are accepted. Repeat to add more certiifcates.\n"
4592 " --timestamp-override <partial-iso-timestamp>\n"
4593 " This specifies the signing time as a ISO timestamp. Partial timestamps are merged with the "
4594 "current time. This is applied to any timestamp signature as well as the signingTime attribute of "
4595 "main signature. Higher resolution than seconds is not supported. Default: Current time.\n"
4596 " --no-signing-time\n"
4597 " Don't set the signing time on the main signature, only on the timestamp one. Unfortunately, "
4598 "this doesn't work without modifying OpenSSL a little.\n"
4599 " --timestamp-type old|new\n"
4600 " Selects the timstamp type. 'old' is the old style /t <url> stuff from signtool.exe. "
4601 "'new' means a RTC-3161 timstamp - currently not implemented. Default: old\n"
4602 //" --timestamp-type-2 old|new\n"
4603 //" Same as --timestamp-type but for the 2nd timstamp signature.\n"
4604 "\n"
4605 //"Certificate and Key Options (--timestamp-cert-name[-2] etc for timestamps):\n"
4606 "Certificate and Key Options (--timestamp-cert-name etc for timestamps):\n"
4607 " --cert-subject <partial name>, /n <partial name>\n"
4608 " Locate the main signature signing certificate and key, unless anything else is given, "
4609 "by the given name substring. Overrides any previous --cert-sha1 and --cert-file options.\n"
4610 " --cert-sha1 <hex bytes>, /sha1 <hex bytes>\n"
4611 " Locate the main signature signing certificate and key, unless anything else is given, "
4612 "by the given thumbprint. The hex bytes can be space separated, colon separated, just "
4613 "bunched together, or a mix of these. This overrids any previous --cert-name and --cert-file "
4614 "options.\n"
4615 " --cert-store <name>, /s <store>\n"
4616 " Certificate store to search when using --cert-name or --cert-sha1. Default: MY\n"
4617 " --cert-machine-store, /sm\n"
4618 " Use the machine store rather the ones of the current user.\n"
4619 " --cert-file <file>, /f <file>\n"
4620 " Load the certificate and key, unless anything else is given, from given file. Both PEM and "
4621 "DER (binary) encodings are supported. Keys file can be RSA or PKCS#12 formatted.\n"
4622 " --key-file <file>\n"
4623 " Load the private key from the given file. Support RSA and PKCS#12 formatted files.\n"
4624 " --key-password <password>, /p <password>\n"
4625 " Password to use to decrypt a PKCS#12 password file.\n"
4626 " --key-password-file <file>|stdin\n"
4627 " Load password to decrypt the password file from the given file or from stdin.\n"
4628 " --key-name <name>, /kc <name>\n"
4629 " The private key container name. Not implemented.\n"
4630 " --key-provider <name>, /csp <name>\n"
4631 " The name of the crypto provider where the private key conatiner specified via --key-name "
4632 "can be found.\n"
4633 );
4634
4635 return RTEXITCODE_SUCCESS;
4636}
4637
4638
4639static RTEXITCODE HandleSign(int cArgs, char **papszArgs)
4640{
4641 /*
4642 * Parse arguments.
4643 */
4644 static const RTGETOPTDEF s_aOptions[] =
4645 {
4646 { "--append", 'A', RTGETOPT_REQ_NOTHING },
4647 { "/as", 'A', RTGETOPT_REQ_NOTHING },
4648 { "/a", OPT_IGNORED, RTGETOPT_REQ_NOTHING }, /* select best cert automatically */
4649 { "--type", 't', RTGETOPT_REQ_STRING },
4650 { "/fd", 't', RTGETOPT_REQ_STRING },
4651 { "--hash-pages", OPT_HASH_PAGES, RTGETOPT_REQ_NOTHING },
4652 { "/ph", OPT_HASH_PAGES, RTGETOPT_REQ_NOTHING },
4653 { "--no-hash-pages", OPT_NO_HASH_PAGES, RTGETOPT_REQ_NOTHING },
4654 { "/nph", OPT_NO_HASH_PAGES, RTGETOPT_REQ_NOTHING },
4655 { "--add-cert", OPT_ADD_CERT, RTGETOPT_REQ_STRING },
4656 { "/ac", OPT_ADD_CERT, RTGETOPT_REQ_STRING },
4657 { "--description", 'd', RTGETOPT_REQ_STRING },
4658 { "--desc", 'd', RTGETOPT_REQ_STRING },
4659 { "/d", 'd', RTGETOPT_REQ_STRING },
4660 { "--description-url", 'D', RTGETOPT_REQ_STRING },
4661 { "--desc-url", 'D', RTGETOPT_REQ_STRING },
4662 { "/du", 'D', RTGETOPT_REQ_STRING },
4663 { "--no-signing-time", OPT_NO_SIGNING_TIME, RTGETOPT_REQ_NOTHING },
4664 OPT_CERT_KEY_GETOPTDEF_ENTRIES("--", "", 1000),
4665 OPT_CERT_KEY_GETOPTDEF_COMPAT_ENTRIES( 1000),
4666 OPT_CERT_KEY_GETOPTDEF_ENTRIES("--timestamp-", "", 1020),
4667 //OPT_CERT_KEY_GETOPTDEF_ENTRIES("--timestamp-", "-1", 1020),
4668 //OPT_CERT_KEY_GETOPTDEF_ENTRIES("--timestamp-", "-2", 1040), - disabled as windows cannot make use of it. Try again when
4669 // new-style timestamp signatures has been implemented. Otherwise, just add two primary signatures with the two
4670 // different timestamps certificates / hashes / whatever.
4671 { "--timestamp-type", OPT_TIMESTAMP_TYPE, RTGETOPT_REQ_STRING },
4672 { "--timestamp-type-1", OPT_TIMESTAMP_TYPE, RTGETOPT_REQ_STRING },
4673 { "--timestamp-type-2", OPT_TIMESTAMP_TYPE_2, RTGETOPT_REQ_STRING },
4674 { "--timestamp-override", OPT_TIMESTAMP_OVERRIDE, RTGETOPT_REQ_STRING },
4675 { "--file-type", OPT_FILE_TYPE, RTGETOPT_REQ_STRING },
4676 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
4677 { "/v", 'v', RTGETOPT_REQ_NOTHING },
4678 { "/debug", 'v', RTGETOPT_REQ_NOTHING },
4679 };
4680
4681 unsigned cVerbosity = 0;
4682 RTDIGESTTYPE enmSigType = RTDIGESTTYPE_SHA1;
4683 bool fReplaceExisting = true;
4684 bool fHashPages = false;
4685 bool fNoSigningTime = false;
4686 RTSIGNTOOLFILETYPE enmForceFileType = RTSIGNTOOLFILETYPE_DETECT;
4687 SignToolKeyPair SigningCertKey("signing", true);
4688 CryptoStore AddCerts;
4689 const char *pszDescription = NULL; /** @todo implement putting descriptions into the OpusInfo stuff. */
4690 const char *pszDescriptionUrl = NULL;
4691 SignToolTimestampOpts aTimestampOpts[2] = { SignToolTimestampOpts("timestamp"), SignToolTimestampOpts("timestamp#2") };
4692 RTTIMESPEC SigningTime;
4693 RTTimeNow(&SigningTime);
4694
4695 RTGETOPTSTATE GetState;
4696 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
4697 AssertRCReturn(rc, RTEXITCODE_FAILURE);
4698
4699 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
4700 RTGETOPTUNION ValueUnion;
4701 int ch;
4702 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
4703 {
4704 RTEXITCODE rcExit2 = RTEXITCODE_SUCCESS;
4705 switch (ch)
4706 {
4707 OPT_CERT_KEY_SWITCH_CASES(SigningCertKey, 1000, ch, ValueUnion, rcExit2);
4708 OPT_CERT_KEY_SWITCH_CASES(aTimestampOpts[0], 1020, ch, ValueUnion, rcExit2);
4709 OPT_CERT_KEY_SWITCH_CASES(aTimestampOpts[1], 1040, ch, ValueUnion, rcExit2);
4710 case 't': rcExit2 = HandleOptSignatureType(&enmSigType, ValueUnion.psz); break;
4711 case 'A': fReplaceExisting = false; break;
4712 case 'd': pszDescription = ValueUnion.psz; break;
4713 case 'D': pszDescriptionUrl = ValueUnion.psz; break;
4714 case OPT_HASH_PAGES: fHashPages = true; break;
4715 case OPT_NO_HASH_PAGES: fHashPages = false; break;
4716 case OPT_NO_SIGNING_TIME: fNoSigningTime = true; break;
4717 case OPT_ADD_CERT: rcExit2 = HandleOptAddCert(&AddCerts.m_hStore, ValueUnion.psz); break;
4718 case OPT_TIMESTAMP_TYPE: rcExit2 = HandleOptTimestampType(&aTimestampOpts[0], ValueUnion.psz); break;
4719 case OPT_TIMESTAMP_TYPE_2: rcExit2 = HandleOptTimestampType(&aTimestampOpts[1], ValueUnion.psz); break;
4720 case OPT_TIMESTAMP_OVERRIDE: rcExit2 = HandleOptTimestampOverride(&SigningTime, ValueUnion.psz); break;
4721 case OPT_FILE_TYPE: rcExit2 = HandleOptFileType(&enmForceFileType, ValueUnion.psz); break;
4722 case OPT_IGNORED: break;
4723 case 'v': cVerbosity++; break;
4724 case 'V': return HandleVersion(cArgs, papszArgs);
4725 case 'h': return HelpSign(g_pStdOut, RTSIGNTOOLHELP_FULL);
4726
4727 case VINF_GETOPT_NOT_OPTION:
4728 /*
4729 * Do final certificate and key option processing (first file only).
4730 */
4731 rcExit2 = SigningCertKey.finalizeOptions(cVerbosity);
4732 for (unsigned i = 0; rcExit2 == RTEXITCODE_SUCCESS && i < RT_ELEMENTS(aTimestampOpts); i++)
4733 rcExit2 = aTimestampOpts[i].finalizeOptions(cVerbosity);
4734 if (rcExit2 == RTEXITCODE_SUCCESS)
4735 {
4736 /*
4737 * Detect file type.
4738 */
4739 RTSIGNTOOLFILETYPE enmFileType = DetectFileType(enmForceFileType, ValueUnion.psz);
4740 if (enmFileType == RTSIGNTOOLFILETYPE_EXE)
4741 {
4742 /*
4743 * Sign executable image.
4744 */
4745 SIGNTOOLPKCS7EXE Exe;
4746 rcExit2 = SignToolPkcs7Exe_InitFromFile(&Exe, ValueUnion.psz, cVerbosity,
4747 RTLDRARCH_WHATEVER, true /*fAllowUnsigned*/);
4748 if (rcExit2 == RTEXITCODE_SUCCESS)
4749 {
4750 rcExit2 = SignToolPkcs7_AddOrReplaceSignature(&Exe, cVerbosity, enmSigType, fReplaceExisting,
4751 fHashPages, fNoSigningTime, &SigningCertKey,
4752 AddCerts.m_hStore, SigningTime,
4753 RT_ELEMENTS(aTimestampOpts), aTimestampOpts);
4754 if (rcExit2 == RTEXITCODE_SUCCESS)
4755 rcExit2 = SignToolPkcs7_Encode(&Exe, cVerbosity);
4756 if (rcExit2 == RTEXITCODE_SUCCESS)
4757 rcExit2 = SignToolPkcs7Exe_WriteSignatureToFile(&Exe, cVerbosity);
4758 SignToolPkcs7Exe_Delete(&Exe);
4759 }
4760 }
4761 else if (enmFileType == RTSIGNTOOLFILETYPE_CAT)
4762 {
4763 /*
4764 * Sign catalog file.
4765 */
4766 SIGNTOOLPKCS7 Cat;
4767 rcExit2 = SignToolPkcs7_InitFromFile(&Cat, ValueUnion.psz, cVerbosity);
4768 if (rcExit2 == RTEXITCODE_SUCCESS)
4769 {
4770 rcExit2 = SignToolPkcs7_AddOrReplaceCatSignature(&Cat, cVerbosity, enmSigType, fReplaceExisting,
4771 fNoSigningTime, &SigningCertKey,
4772 AddCerts.m_hStore, SigningTime,
4773 RT_ELEMENTS(aTimestampOpts), aTimestampOpts);
4774 if (rcExit2 == RTEXITCODE_SUCCESS)
4775 rcExit2 = SignToolPkcs7_Encode(&Cat, cVerbosity);
4776 if (rcExit2 == RTEXITCODE_SUCCESS)
4777 rcExit2 = SignToolPkcs7_WriteSignatureToFile(&Cat, ValueUnion.psz, cVerbosity);
4778 SignToolPkcs7_Delete(&Cat);
4779 }
4780 }
4781 else
4782 rcExit2 = RTEXITCODE_FAILURE;
4783 if (rcExit2 != RTEXITCODE_SUCCESS && rcExit == RTEXITCODE_SUCCESS)
4784 rcExit = rcExit2;
4785 rcExit2 = RTEXITCODE_SUCCESS;
4786 }
4787 break;
4788
4789 default:
4790 return RTGetOptPrintError(ch, &ValueUnion);
4791 }
4792 if (rcExit2 != RTEXITCODE_SUCCESS)
4793 {
4794 rcExit = rcExit2;
4795 break;
4796 }
4797 }
4798
4799 return rcExit;
4800}
4801
4802#endif /*!IPRT_SIGNTOOL_NO_SIGNING */
4803
4804
4805/*********************************************************************************************************************************
4806* The 'verify-exe' command. *
4807*********************************************************************************************************************************/
4808#ifndef IPRT_IN_BUILD_TOOL
4809
4810static RTEXITCODE HelpVerifyExe(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
4811{
4812 RT_NOREF_PV(enmLevel);
4813 RTStrmWrappedPrintf(pStrm, RTSTRMWRAPPED_F_HANGING_INDENT,
4814 "verify-exe [--verbose|--quiet] [--kernel] [--root <root-cert.der>] [--self-signed-roots-from-system] "
4815 "[--additional <supp-cert.der>] [--type <win|osx>] <exe1> [exe2 [..]]\n");
4816 return RTEXITCODE_SUCCESS;
4817}
4818
4819typedef struct VERIFYEXESTATE
4820{
4821 CryptoStore RootStore;
4822 CryptoStore KernelRootStore;
4823 CryptoStore AdditionalStore;
4824 bool fKernel;
4825 int cVerbose;
4826 enum { kSignType_Windows, kSignType_OSX } enmSignType;
4827 RTLDRARCH enmLdrArch;
4828 uint32_t cBad;
4829 uint32_t cOkay;
4830 const char *pszFilename;
4831 RTTIMESPEC ValidationTime;
4832
4833 VERIFYEXESTATE()
4834 : fKernel(false)
4835 , cVerbose(0)
4836 , enmSignType(kSignType_Windows)
4837 , enmLdrArch(RTLDRARCH_WHATEVER)
4838 , cBad(0)
4839 , cOkay(0)
4840 , pszFilename(NULL)
4841 {
4842 RTTimeSpecSetSeconds(&ValidationTime, 0);
4843 }
4844} VERIFYEXESTATE;
4845
4846# ifdef VBOX
4847/** Certificate store load set.
4848 * Declared outside HandleVerifyExe because of braindead gcc visibility crap. */
4849struct STSTORESET
4850{
4851 RTCRSTORE hStore;
4852 PCSUPTAENTRY paTAs;
4853 unsigned cTAs;
4854};
4855# endif
4856
4857/**
4858 * @callback_method_impl{FNRTCRPKCS7VERIFYCERTCALLBACK,
4859 * Standard code signing. Use this for Microsoft SPC.}
4860 */
4861static DECLCALLBACK(int) VerifyExecCertVerifyCallback(PCRTCRX509CERTIFICATE pCert, RTCRX509CERTPATHS hCertPaths, uint32_t fFlags,
4862 void *pvUser, PRTERRINFO pErrInfo)
4863{
4864 VERIFYEXESTATE *pState = (VERIFYEXESTATE *)pvUser;
4865 uint32_t cPaths = RTCrX509CertPathsGetPathCount(hCertPaths);
4866
4867 /*
4868 * Dump all the paths.
4869 */
4870 if (pState->cVerbose > 0)
4871 {
4872 RTPrintf(fFlags & RTCRPKCS7VCC_F_TIMESTAMP ? "Timestamp Path%s:\n" : "Signature Path%s:\n",
4873 cPaths == 1 ? "" : "s");
4874 for (uint32_t iPath = 0; iPath < cPaths; iPath++)
4875 {
4876 //if (iPath != 0)
4877 // RTPrintf("---\n");
4878 RTCrX509CertPathsDumpOne(hCertPaths, iPath, pState->cVerbose, RTStrmDumpPrintfV, g_pStdOut);
4879 *pErrInfo->pszMsg = '\0';
4880 }
4881 //RTPrintf(fFlags & RTCRPKCS7VCC_F_TIMESTAMP ? "--- end timestamp ---\n" : "--- end signature ---\n");
4882 }
4883
4884 /*
4885 * Test signing certificates normally doesn't have all the necessary
4886 * features required below. So, treat them as special cases.
4887 */
4888 if ( hCertPaths == NIL_RTCRX509CERTPATHS
4889 && RTCrX509Name_Compare(&pCert->TbsCertificate.Issuer, &pCert->TbsCertificate.Subject) == 0)
4890 {
4891 RTMsgInfo("Test signed.\n");
4892 return VINF_SUCCESS;
4893 }
4894
4895 if (hCertPaths == NIL_RTCRX509CERTPATHS)
4896 RTMsgInfo("Signed by trusted certificate.\n");
4897
4898 /*
4899 * Standard code signing capabilites required.
4900 */
4901 int rc = RTCrPkcs7VerifyCertCallbackCodeSigning(pCert, hCertPaths, fFlags, NULL, pErrInfo);
4902 if ( RT_SUCCESS(rc)
4903 && (fFlags & RTCRPKCS7VCC_F_SIGNED_DATA))
4904 {
4905 /*
4906 * If windows kernel signing, a valid certificate path must be anchored
4907 * by the microsoft kernel signing root certificate. The only
4908 * alternative is test signing.
4909 */
4910 if ( pState->fKernel
4911 && hCertPaths != NIL_RTCRX509CERTPATHS
4912 && pState->enmSignType == VERIFYEXESTATE::kSignType_Windows)
4913 {
4914 uint32_t cFound = 0;
4915 uint32_t cValid = 0;
4916 for (uint32_t iPath = 0; iPath < cPaths; iPath++)
4917 {
4918 bool fTrusted;
4919 PCRTCRX509NAME pSubject;
4920 PCRTCRX509SUBJECTPUBLICKEYINFO pPublicKeyInfo;
4921 int rcVerify;
4922 rc = RTCrX509CertPathsQueryPathInfo(hCertPaths, iPath, &fTrusted, NULL /*pcNodes*/, &pSubject, &pPublicKeyInfo,
4923 NULL, NULL /*pCertCtx*/, &rcVerify);
4924 AssertRCBreak(rc);
4925
4926 if (RT_SUCCESS(rcVerify))
4927 {
4928 Assert(fTrusted);
4929 cValid++;
4930
4931 /* Search the kernel signing root store for a matching anchor. */
4932 RTCRSTORECERTSEARCH Search;
4933 rc = RTCrStoreCertFindBySubjectOrAltSubjectByRfc5280(pState->KernelRootStore.m_hStore, pSubject, &Search);
4934 AssertRCBreak(rc);
4935 PCRTCRCERTCTX pCertCtx;
4936 while ((pCertCtx = RTCrStoreCertSearchNext(pState->KernelRootStore.m_hStore, &Search)) != NULL)
4937 {
4938 PCRTCRX509SUBJECTPUBLICKEYINFO pPubKeyInfo;
4939 if (pCertCtx->pCert)
4940 pPubKeyInfo = &pCertCtx->pCert->TbsCertificate.SubjectPublicKeyInfo;
4941 else if (pCertCtx->pTaInfo)
4942 pPubKeyInfo = &pCertCtx->pTaInfo->PubKey;
4943 else
4944 pPubKeyInfo = NULL;
4945 if (RTCrX509SubjectPublicKeyInfo_Compare(pPubKeyInfo, pPublicKeyInfo) == 0)
4946 cFound++;
4947 RTCrCertCtxRelease(pCertCtx);
4948 }
4949
4950 int rc2 = RTCrStoreCertSearchDestroy(pState->KernelRootStore.m_hStore, &Search); AssertRC(rc2);
4951 }
4952 }
4953 if (RT_SUCCESS(rc) && cFound == 0)
4954 rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE, "Not valid kernel code signature.");
4955 if (RT_SUCCESS(rc) && cValid != 2)
4956 RTMsgWarning("%u valid paths, expected 2", cValid);
4957 }
4958 /*
4959 * For Mac OS X signing, check for special developer ID attributes.
4960 */
4961 else if (pState->enmSignType == VERIFYEXESTATE::kSignType_OSX)
4962 {
4963 uint32_t cDevIdApp = 0;
4964 uint32_t cDevIdKext = 0;
4965 uint32_t cDevIdMacDev = 0;
4966 for (uint32_t i = 0; i < pCert->TbsCertificate.T3.Extensions.cItems; i++)
4967 {
4968 PCRTCRX509EXTENSION pExt = pCert->TbsCertificate.T3.Extensions.papItems[i];
4969 if (RTAsn1ObjId_CompareWithString(&pExt->ExtnId, RTCR_APPLE_CS_DEVID_APPLICATION_OID) == 0)
4970 {
4971 cDevIdApp++;
4972 if (!pExt->Critical.fValue)
4973 rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
4974 "Dev ID Application certificate extension is not flagged critical");
4975 }
4976 else if (RTAsn1ObjId_CompareWithString(&pExt->ExtnId, RTCR_APPLE_CS_DEVID_KEXT_OID) == 0)
4977 {
4978 cDevIdKext++;
4979 if (!pExt->Critical.fValue)
4980 rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
4981 "Dev ID kext certificate extension is not flagged critical");
4982 }
4983 else if (RTAsn1ObjId_CompareWithString(&pExt->ExtnId, RTCR_APPLE_CS_DEVID_MAC_SW_DEV_OID) == 0)
4984 {
4985 cDevIdMacDev++;
4986 if (!pExt->Critical.fValue)
4987 rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
4988 "Dev ID Mac SW dev certificate extension is not flagged critical");
4989 }
4990 }
4991 if (cDevIdApp == 0)
4992 {
4993 if (cDevIdMacDev == 0)
4994 rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
4995 "Certificate is missing the 'Dev ID Application' extension");
4996 else
4997 RTMsgWarning("Mac SW dev certificate used to sign code.");
4998 }
4999 if (cDevIdKext == 0 && pState->fKernel)
5000 {
5001 if (cDevIdMacDev == 0)
5002 rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
5003 "Certificate is missing the 'Dev ID kext' extension");
5004 else
5005 RTMsgWarning("Mac SW dev certificate used to sign kernel code.");
5006 }
5007 }
5008 }
5009
5010 return rc;
5011}
5012
5013/** @callback_method_impl{FNRTLDRVALIDATESIGNEDDATA} */
5014static DECLCALLBACK(int) VerifyExeCallback(RTLDRMOD hLdrMod, PCRTLDRSIGNATUREINFO pInfo, PRTERRINFO pErrInfo, void *pvUser)
5015{
5016 VERIFYEXESTATE *pState = (VERIFYEXESTATE *)pvUser;
5017 RT_NOREF_PV(hLdrMod);
5018
5019 switch (pInfo->enmType)
5020 {
5021 case RTLDRSIGNATURETYPE_PKCS7_SIGNED_DATA:
5022 {
5023 PCRTCRPKCS7CONTENTINFO pContentInfo = (PCRTCRPKCS7CONTENTINFO)pInfo->pvSignature;
5024
5025 if (pState->cVerbose > 0)
5026 RTMsgInfo("Verifying '%s' signature #%u ...\n", pState->pszFilename, pInfo->iSignature + 1);
5027
5028 /*
5029 * Dump the signed data if so requested and it's the first one, assuming that
5030 * additional signatures in contained wihtin the same ContentInfo structure.
5031 */
5032 if (pState->cVerbose > 1 && pInfo->iSignature == 0)
5033 RTAsn1Dump(&pContentInfo->SeqCore.Asn1Core, 0, 0, RTStrmDumpPrintfV, g_pStdOut);
5034
5035 /*
5036 * We'll try different alternative timestamps here.
5037 */
5038 struct { RTTIMESPEC TimeSpec; const char *pszDesc; } aTimes[3];
5039 unsigned cTimes = 0;
5040
5041 /* The specified timestamp. */
5042 if (RTTimeSpecGetSeconds(&pState->ValidationTime) != 0)
5043 {
5044 aTimes[cTimes].TimeSpec = pState->ValidationTime;
5045 aTimes[cTimes].pszDesc = "validation time";
5046 cTimes++;
5047 }
5048
5049 /* Linking timestamp: */
5050 uint64_t uLinkingTime = 0;
5051 int rc = RTLdrQueryProp(hLdrMod, RTLDRPROP_TIMESTAMP_SECONDS, &uLinkingTime, sizeof(uLinkingTime));
5052 if (RT_SUCCESS(rc))
5053 {
5054 RTTimeSpecSetSeconds(&aTimes[cTimes].TimeSpec, uLinkingTime);
5055 aTimes[cTimes].pszDesc = "at link time";
5056 cTimes++;
5057 }
5058 else if (rc != VERR_NOT_FOUND)
5059 RTMsgError("RTLdrQueryProp/RTLDRPROP_TIMESTAMP_SECONDS failed on '%s': %Rrc\n", pState->pszFilename, rc);
5060
5061 /* Now: */
5062 RTTimeNow(&aTimes[cTimes].TimeSpec);
5063 aTimes[cTimes].pszDesc = "now";
5064 cTimes++;
5065
5066 /*
5067 * Do the actual verification.
5068 */
5069 for (unsigned iTime = 0; iTime < cTimes; iTime++)
5070 {
5071 if (pInfo->pvExternalData)
5072 rc = RTCrPkcs7VerifySignedDataWithExternalData(pContentInfo,
5073 RTCRPKCS7VERIFY_SD_F_COUNTER_SIGNATURE_SIGNING_TIME_ONLY
5074 | RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_SIGNING_TIME_IF_PRESENT
5075 | RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_MS_TIMESTAMP_IF_PRESENT
5076 | RTCRPKCS7VERIFY_SD_F_CHECK_TRUST_ANCHORS,
5077 pState->AdditionalStore.m_hStore, pState->RootStore.m_hStore,
5078 &aTimes[iTime].TimeSpec,
5079 VerifyExecCertVerifyCallback, pState,
5080 pInfo->pvExternalData, pInfo->cbExternalData, pErrInfo);
5081 else
5082 rc = RTCrPkcs7VerifySignedData(pContentInfo,
5083 RTCRPKCS7VERIFY_SD_F_COUNTER_SIGNATURE_SIGNING_TIME_ONLY
5084 | RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_SIGNING_TIME_IF_PRESENT
5085 | RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_MS_TIMESTAMP_IF_PRESENT
5086 | RTCRPKCS7VERIFY_SD_F_CHECK_TRUST_ANCHORS,
5087 pState->AdditionalStore.m_hStore, pState->RootStore.m_hStore,
5088 &aTimes[iTime].TimeSpec,
5089 VerifyExecCertVerifyCallback, pState, pErrInfo);
5090 if (RT_SUCCESS(rc))
5091 {
5092 Assert(rc == VINF_SUCCESS || rc == VINF_CR_DIGEST_DEPRECATED);
5093 const char *pszNote = rc == VINF_CR_DIGEST_DEPRECATED ? " (deprecated digest)" : "";
5094 if (pInfo->cSignatures == 1)
5095 RTMsgInfo("'%s' is valid %s%s.\n", pState->pszFilename, aTimes[iTime].pszDesc, pszNote);
5096 else
5097 RTMsgInfo("'%s' signature #%u is valid %s%s.\n",
5098 pState->pszFilename, pInfo->iSignature + 1, aTimes[iTime].pszDesc, pszNote);
5099 pState->cOkay++;
5100 return VINF_SUCCESS;
5101 }
5102 if (rc != VERR_CR_X509_CPV_NOT_VALID_AT_TIME)
5103 {
5104 if (pInfo->cSignatures == 1)
5105 RTMsgError("%s: Failed to verify signature: %Rrc%#RTeim\n", pState->pszFilename, rc, pErrInfo);
5106 else
5107 RTMsgError("%s: Failed to verify signature #%u: %Rrc%#RTeim\n",
5108 pState->pszFilename, pInfo->iSignature + 1, rc, pErrInfo);
5109 pState->cBad++;
5110 return VINF_SUCCESS;
5111 }
5112 }
5113
5114 if (pInfo->cSignatures == 1)
5115 RTMsgError("%s: Signature is not valid at present or link time.\n", pState->pszFilename);
5116 else
5117 RTMsgError("%s: Signature #%u is not valid at present or link time.\n",
5118 pState->pszFilename, pInfo->iSignature + 1);
5119 pState->cBad++;
5120 return VINF_SUCCESS;
5121 }
5122
5123 default:
5124 return RTErrInfoSetF(pErrInfo, VERR_NOT_SUPPORTED, "Unsupported signature type: %d", pInfo->enmType);
5125 }
5126}
5127
5128/**
5129 * Worker for HandleVerifyExe.
5130 */
5131static RTEXITCODE HandleVerifyExeWorker(VERIFYEXESTATE *pState, const char *pszFilename, PRTERRINFOSTATIC pStaticErrInfo)
5132{
5133 /*
5134 * Open the executable image and verify it.
5135 */
5136 RTLDRMOD hLdrMod;
5137 int rc = RTLdrOpen(pszFilename, RTLDR_O_FOR_VALIDATION, pState->enmLdrArch, &hLdrMod);
5138 if (RT_FAILURE(rc))
5139 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error opening executable image '%s': %Rrc", pszFilename, rc);
5140
5141 /* Reset the state. */
5142 pState->cBad = 0;
5143 pState->cOkay = 0;
5144 pState->pszFilename = pszFilename;
5145
5146 rc = RTLdrVerifySignature(hLdrMod, VerifyExeCallback, pState, RTErrInfoInitStatic(pStaticErrInfo));
5147 if (RT_FAILURE(rc))
5148 RTMsgError("RTLdrVerifySignature failed on '%s': %Rrc - %s\n", pszFilename, rc, pStaticErrInfo->szMsg);
5149
5150 int rc2 = RTLdrClose(hLdrMod);
5151 if (RT_FAILURE(rc2))
5152 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTLdrClose failed: %Rrc\n", rc2);
5153 if (RT_FAILURE(rc))
5154 return rc != VERR_LDRVI_NOT_SIGNED ? RTEXITCODE_FAILURE : RTEXITCODE_SKIPPED;
5155
5156 return pState->cOkay > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
5157}
5158
5159
5160static RTEXITCODE HandleVerifyExe(int cArgs, char **papszArgs)
5161{
5162 RTERRINFOSTATIC StaticErrInfo;
5163
5164 /*
5165 * Parse arguments.
5166 */
5167 static const RTGETOPTDEF s_aOptions[] =
5168 {
5169 { "--kernel", 'k', RTGETOPT_REQ_NOTHING },
5170 { "--root", 'r', RTGETOPT_REQ_STRING },
5171 { "--self-signed-roots-from-system", 'R', RTGETOPT_REQ_NOTHING },
5172 { "--additional", 'a', RTGETOPT_REQ_STRING },
5173 { "--add", 'a', RTGETOPT_REQ_STRING },
5174 { "--type", 't', RTGETOPT_REQ_STRING },
5175 { "--validation-time", 'T', RTGETOPT_REQ_STRING },
5176 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
5177 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
5178 };
5179
5180 VERIFYEXESTATE State;
5181 int rc = RTCrStoreCreateInMem(&State.RootStore.m_hStore, 0);
5182 if (RT_SUCCESS(rc))
5183 rc = RTCrStoreCreateInMem(&State.KernelRootStore.m_hStore, 0);
5184 if (RT_SUCCESS(rc))
5185 rc = RTCrStoreCreateInMem(&State.AdditionalStore.m_hStore, 0);
5186 if (RT_FAILURE(rc))
5187 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error creating in-memory certificate store: %Rrc", rc);
5188
5189 RTGETOPTSTATE GetState;
5190 rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
5191 AssertRCReturn(rc, RTEXITCODE_FAILURE);
5192 RTGETOPTUNION ValueUnion;
5193 int ch;
5194 while ((ch = RTGetOpt(&GetState, &ValueUnion)) && ch != VINF_GETOPT_NOT_OPTION)
5195 {
5196 switch (ch)
5197 {
5198 case 'a':
5199 if (!State.AdditionalStore.addFromFile(ValueUnion.psz, &StaticErrInfo))
5200 return RTEXITCODE_FAILURE;
5201 break;
5202
5203 case 'r':
5204 if (!State.RootStore.addFromFile(ValueUnion.psz, &StaticErrInfo))
5205 return RTEXITCODE_FAILURE;
5206 break;
5207
5208 case 'R':
5209 if (!State.RootStore.addSelfSignedRootsFromSystem(&StaticErrInfo))
5210 return RTEXITCODE_FAILURE;
5211 break;
5212
5213 case 't':
5214 if (!strcmp(ValueUnion.psz, "win") || !strcmp(ValueUnion.psz, "windows"))
5215 State.enmSignType = VERIFYEXESTATE::kSignType_Windows;
5216 else if (!strcmp(ValueUnion.psz, "osx") || !strcmp(ValueUnion.psz, "apple"))
5217 State.enmSignType = VERIFYEXESTATE::kSignType_OSX;
5218 else
5219 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown signing type: '%s'", ValueUnion.psz);
5220 break;
5221
5222 case 'T':
5223 if (!RTTimeSpecFromString(&State.ValidationTime, ValueUnion.psz))
5224 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid validation time (%s): %Rrc", ValueUnion.psz, rc);
5225 break;
5226
5227 case 'k': State.fKernel = true; break;
5228 case 'v': State.cVerbose++; break;
5229 case 'q': State.cVerbose = 0; break;
5230 case 'V': return HandleVersion(cArgs, papszArgs);
5231 case 'h': return HelpVerifyExe(g_pStdOut, RTSIGNTOOLHELP_FULL);
5232 default: return RTGetOptPrintError(ch, &ValueUnion);
5233 }
5234 }
5235 if (ch != VINF_GETOPT_NOT_OPTION)
5236 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No executable given.");
5237
5238 /*
5239 * Populate the certificate stores according to the signing type.
5240 */
5241# ifdef VBOX
5242 unsigned cSets = 0;
5243 struct STSTORESET aSets[6];
5244 switch (State.enmSignType)
5245 {
5246 case VERIFYEXESTATE::kSignType_Windows:
5247 aSets[cSets].hStore = State.RootStore.m_hStore;
5248 aSets[cSets].paTAs = g_aSUPTimestampTAs;
5249 aSets[cSets].cTAs = g_cSUPTimestampTAs;
5250 cSets++;
5251 aSets[cSets].hStore = State.RootStore.m_hStore;
5252 aSets[cSets].paTAs = g_aSUPSpcRootTAs;
5253 aSets[cSets].cTAs = g_cSUPSpcRootTAs;
5254 cSets++;
5255 aSets[cSets].hStore = State.RootStore.m_hStore;
5256 aSets[cSets].paTAs = g_aSUPNtKernelRootTAs;
5257 aSets[cSets].cTAs = g_cSUPNtKernelRootTAs;
5258 cSets++;
5259 aSets[cSets].hStore = State.KernelRootStore.m_hStore;
5260 aSets[cSets].paTAs = g_aSUPNtKernelRootTAs;
5261 aSets[cSets].cTAs = g_cSUPNtKernelRootTAs;
5262 cSets++;
5263 break;
5264
5265 case VERIFYEXESTATE::kSignType_OSX:
5266 aSets[cSets].hStore = State.RootStore.m_hStore;
5267 aSets[cSets].paTAs = g_aSUPAppleRootTAs;
5268 aSets[cSets].cTAs = g_cSUPAppleRootTAs;
5269 cSets++;
5270 break;
5271 }
5272 for (unsigned i = 0; i < cSets; i++)
5273 for (unsigned j = 0; j < aSets[i].cTAs; j++)
5274 {
5275 rc = RTCrStoreCertAddEncoded(aSets[i].hStore, RTCRCERTCTX_F_ENC_TAF_DER, aSets[i].paTAs[j].pch,
5276 aSets[i].paTAs[j].cb, RTErrInfoInitStatic(&StaticErrInfo));
5277 if (RT_FAILURE(rc))
5278 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTCrStoreCertAddEncoded failed (%u/%u): %s",
5279 i, j, StaticErrInfo.szMsg);
5280 }
5281# endif /* VBOX */
5282
5283 /*
5284 * Do it.
5285 */
5286 RTEXITCODE rcExit;
5287 for (;;)
5288 {
5289 rcExit = HandleVerifyExeWorker(&State, ValueUnion.psz, &StaticErrInfo);
5290 if (rcExit != RTEXITCODE_SUCCESS)
5291 break;
5292
5293 /*
5294 * Next file
5295 */
5296 ch = RTGetOpt(&GetState, &ValueUnion);
5297 if (ch == 0)
5298 break;
5299 if (ch != VINF_GETOPT_NOT_OPTION)
5300 {
5301 rcExit = RTGetOptPrintError(ch, &ValueUnion);
5302 break;
5303 }
5304 }
5305
5306 return rcExit;
5307}
5308
5309#endif /* !IPRT_IN_BUILD_TOOL */
5310
5311/*
5312 * common code for show-exe and show-cat:
5313 */
5314
5315/**
5316 * Display an object ID.
5317 *
5318 * @returns IPRT status code.
5319 * @param pThis The show exe instance data.
5320 * @param pObjId The object ID to display.
5321 * @param pszLabel The field label (prefixed by szPrefix).
5322 * @param pszPost What to print after the ID (typically newline).
5323 */
5324static void HandleShowExeWorkerDisplayObjId(PSHOWEXEPKCS7 pThis, PCRTASN1OBJID pObjId, const char *pszLabel, const char *pszPost)
5325{
5326 int rc = RTAsn1QueryObjIdName(pObjId, pThis->szTmp, sizeof(pThis->szTmp));
5327 if (RT_SUCCESS(rc))
5328 {
5329 if (pThis->cVerbosity > 1)
5330 RTPrintf("%s%s%s (%s)%s", pThis->szPrefix, pszLabel, pThis->szTmp, pObjId->szObjId, pszPost);
5331 else
5332 RTPrintf("%s%s%s%s", pThis->szPrefix, pszLabel, pThis->szTmp, pszPost);
5333 }
5334 else
5335 RTPrintf("%s%s%s%s", pThis->szPrefix, pszLabel, pObjId->szObjId, pszPost);
5336}
5337
5338
5339/**
5340 * Display an object ID, without prefix and label
5341 *
5342 * @returns IPRT status code.
5343 * @param pThis The show exe instance data.
5344 * @param pObjId The object ID to display.
5345 * @param pszPost What to print after the ID (typically newline).
5346 */
5347static void HandleShowExeWorkerDisplayObjIdSimple(PSHOWEXEPKCS7 pThis, PCRTASN1OBJID pObjId, const char *pszPost)
5348{
5349 int rc = RTAsn1QueryObjIdName(pObjId, pThis->szTmp, sizeof(pThis->szTmp));
5350 if (RT_SUCCESS(rc))
5351 {
5352 if (pThis->cVerbosity > 1)
5353 RTPrintf("%s (%s)%s", pThis->szTmp, pObjId->szObjId, pszPost);
5354 else
5355 RTPrintf("%s%s", pThis->szTmp, pszPost);
5356 }
5357 else
5358 RTPrintf("%s%s", pObjId->szObjId, pszPost);
5359}
5360
5361
5362/**
5363 * Display a signer info attribute.
5364 *
5365 * @returns IPRT status code.
5366 * @param pThis The show exe instance data.
5367 * @param offPrefix The current prefix offset.
5368 * @param pAttr The attribute to display.
5369 */
5370static int HandleShowExeWorkerPkcs7DisplayAttrib(PSHOWEXEPKCS7 pThis, size_t offPrefix, PCRTCRPKCS7ATTRIBUTE pAttr)
5371{
5372 HandleShowExeWorkerDisplayObjId(pThis, &pAttr->Type, "", ":\n");
5373 if (pThis->cVerbosity > 4 && pAttr->SeqCore.Asn1Core.uData.pu8)
5374 RTPrintf("%s uData.pu8=%p cb=%#x\n", pThis->szPrefix, pAttr->SeqCore.Asn1Core.uData.pu8, pAttr->SeqCore.Asn1Core.cb);
5375
5376 int rc = VINF_SUCCESS;
5377 switch (pAttr->enmType)
5378 {
5379 case RTCRPKCS7ATTRIBUTETYPE_UNKNOWN:
5380 if (pAttr->uValues.pCores->cItems <= 1)
5381 RTPrintf("%s %u bytes\n", pThis->szPrefix,pAttr->uValues.pCores->SetCore.Asn1Core.cb);
5382 else
5383 RTPrintf("%s %u bytes divided by %u items\n", pThis->szPrefix, pAttr->uValues.pCores->SetCore.Asn1Core.cb, pAttr->uValues.pCores->cItems);
5384 break;
5385
5386 /* Object IDs, use pObjIds. */
5387 case RTCRPKCS7ATTRIBUTETYPE_OBJ_IDS:
5388 if (pAttr->uValues.pObjIds->cItems != 1)
5389 RTPrintf("%s%u object IDs:", pThis->szPrefix, pAttr->uValues.pObjIds->cItems);
5390 for (unsigned i = 0; i < pAttr->uValues.pObjIds->cItems; i++)
5391 {
5392 if (pAttr->uValues.pObjIds->cItems == 1)
5393 RTPrintf("%s ", pThis->szPrefix);
5394 else
5395 RTPrintf("%s ObjId[%u]: ", pThis->szPrefix, i);
5396 HandleShowExeWorkerDisplayObjIdSimple(pThis, pAttr->uValues.pObjIds->papItems[i], "\n");
5397 }
5398 break;
5399
5400 /* Sequence of object IDs, use pObjIdSeqs. */
5401 case RTCRPKCS7ATTRIBUTETYPE_MS_STATEMENT_TYPE:
5402 if (pAttr->uValues.pObjIdSeqs->cItems != 1)
5403 RTPrintf("%s%u object IDs:", pThis->szPrefix, pAttr->uValues.pObjIdSeqs->cItems);
5404 for (unsigned i = 0; i < pAttr->uValues.pObjIdSeqs->cItems; i++)
5405 {
5406 uint32_t const cObjIds = pAttr->uValues.pObjIdSeqs->papItems[i]->cItems;
5407 for (unsigned j = 0; j < cObjIds; j++)
5408 {
5409 if (pAttr->uValues.pObjIdSeqs->cItems == 1)
5410 RTPrintf("%s ", pThis->szPrefix);
5411 else
5412 RTPrintf("%s ObjIdSeq[%u]: ", pThis->szPrefix, i);
5413 if (cObjIds != 1)
5414 RTPrintf(" ObjId[%u]: ", j);
5415 HandleShowExeWorkerDisplayObjIdSimple(pThis, pAttr->uValues.pObjIdSeqs->papItems[i]->papItems[i], "\n");
5416 }
5417 }
5418 break;
5419
5420 /* Octet strings, use pOctetStrings. */
5421 case RTCRPKCS7ATTRIBUTETYPE_OCTET_STRINGS:
5422 if (pAttr->uValues.pOctetStrings->cItems != 1)
5423 RTPrintf("%s%u octet strings:", pThis->szPrefix, pAttr->uValues.pOctetStrings->cItems);
5424 for (unsigned i = 0; i < pAttr->uValues.pOctetStrings->cItems; i++)
5425 {
5426 PCRTASN1OCTETSTRING pOctetString = pAttr->uValues.pOctetStrings->papItems[i];
5427 uint32_t cbContent = pOctetString->Asn1Core.cb;
5428 if (cbContent > 0 && (cbContent <= 128 || pThis->cVerbosity >= 2))
5429 {
5430 uint8_t const *pbContent = pOctetString->Asn1Core.uData.pu8;
5431 uint32_t off = 0;
5432 while (off < cbContent)
5433 {
5434 uint32_t cbNow = RT_MIN(cbContent - off, 16);
5435 if (pAttr->uValues.pOctetStrings->cItems == 1)
5436 RTPrintf("%s %#06x: %.*Rhxs\n", pThis->szPrefix, off, cbNow, &pbContent[off]);
5437 else
5438 RTPrintf("%s OctetString[%u]: %#06x: %.*Rhxs\n", pThis->szPrefix, i, off, cbNow, &pbContent[off]);
5439 off += cbNow;
5440 }
5441 }
5442 else
5443 RTPrintf("%s: OctetString[%u]: %u bytes\n", pThis->szPrefix, i, pOctetString->Asn1Core.cb);
5444 }
5445 break;
5446
5447 /* Counter signatures (PKCS \#9), use pCounterSignatures. */
5448 case RTCRPKCS7ATTRIBUTETYPE_COUNTER_SIGNATURES:
5449 RTPrintf("%s%u counter signatures, %u bytes in total\n", pThis->szPrefix,
5450 pAttr->uValues.pCounterSignatures->cItems, pAttr->uValues.pCounterSignatures->SetCore.Asn1Core.cb);
5451 for (uint32_t i = 0; i < pAttr->uValues.pCounterSignatures->cItems; i++)
5452 {
5453 size_t offPrefix2 = offPrefix;
5454 if (pAttr->uValues.pContentInfos->cItems > 1)
5455 offPrefix2 += RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix, "CounterSig[%u]: ", i);
5456 else
5457 offPrefix2 += RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix, " ");
5458
5459 int rc2 = HandleShowExeWorkerPkcs7DisplaySignerInfo(pThis, offPrefix2,
5460 pAttr->uValues.pCounterSignatures->papItems[i]);
5461 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
5462 rc = rc2;
5463 }
5464 break;
5465
5466 /* Signing time (PKCS \#9), use pSigningTime. */
5467 case RTCRPKCS7ATTRIBUTETYPE_SIGNING_TIME:
5468 for (uint32_t i = 0; i < pAttr->uValues.pSigningTime->cItems; i++)
5469 {
5470 PCRTASN1TIME pTime = pAttr->uValues.pSigningTime->papItems[i];
5471 char szTS[RTTIME_STR_LEN];
5472 RTTimeToString(&pTime->Time, szTS, sizeof(szTS));
5473 if (pAttr->uValues.pSigningTime->cItems == 1)
5474 RTPrintf("%s %s (%.*s)\n", pThis->szPrefix, szTS, pTime->Asn1Core.cb, pTime->Asn1Core.uData.pch);
5475 else
5476 RTPrintf("%s #%u: %s (%.*s)\n", pThis->szPrefix, i, szTS, pTime->Asn1Core.cb, pTime->Asn1Core.uData.pch);
5477 }
5478 break;
5479
5480 /* Microsoft timestamp info (RFC-3161) signed data, use pContentInfo. */
5481 case RTCRPKCS7ATTRIBUTETYPE_MS_TIMESTAMP:
5482 case RTCRPKCS7ATTRIBUTETYPE_MS_NESTED_SIGNATURE:
5483 if (pAttr->uValues.pContentInfos->cItems > 1)
5484 RTPrintf("%s%u nested signatures, %u bytes in total\n", pThis->szPrefix,
5485 pAttr->uValues.pContentInfos->cItems, pAttr->uValues.pContentInfos->SetCore.Asn1Core.cb);
5486 for (unsigned i = 0; i < pAttr->uValues.pContentInfos->cItems; i++)
5487 {
5488 size_t offPrefix2 = offPrefix;
5489 if (pAttr->uValues.pContentInfos->cItems > 1)
5490 offPrefix2 += RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix, "NestedSig[%u]: ", i);
5491 else
5492 offPrefix2 += RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix, " ");
5493 // offPrefix2 += RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix, "NestedSig: ", i);
5494 PCRTCRPKCS7CONTENTINFO pContentInfo = pAttr->uValues.pContentInfos->papItems[i];
5495 int rc2;
5496 if (RTCrPkcs7ContentInfo_IsSignedData(pContentInfo))
5497 rc2 = HandleShowExeWorkerPkcs7Display(pThis, pContentInfo->u.pSignedData, offPrefix2, pContentInfo);
5498 else
5499 rc2 = RTMsgErrorRc(VERR_ASN1_UNEXPECTED_OBJ_ID, "%sPKCS#7 content in nested signature is not 'signedData': %s",
5500 pThis->szPrefix, pContentInfo->ContentType.szObjId);
5501 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
5502 rc = rc2;
5503 }
5504 break;
5505
5506 case RTCRPKCS7ATTRIBUTETYPE_APPLE_MULTI_CD_PLIST:
5507 if (pAttr->uValues.pContentInfos->cItems != 1)
5508 RTPrintf("%s%u plists, expected only 1.\n", pThis->szPrefix, pAttr->uValues.pOctetStrings->cItems);
5509 for (unsigned i = 0; i < pAttr->uValues.pOctetStrings->cItems; i++)
5510 {
5511 PCRTASN1OCTETSTRING pOctetString = pAttr->uValues.pOctetStrings->papItems[i];
5512 size_t cbContent = pOctetString->Asn1Core.cb;
5513 char const *pchContent = pOctetString->Asn1Core.uData.pch;
5514 rc = RTStrValidateEncodingEx(pchContent, cbContent, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH);
5515 if (RT_SUCCESS(rc))
5516 {
5517 while (cbContent > 0)
5518 {
5519 const char *pchNewLine = (const char *)memchr(pchContent, '\n', cbContent);
5520 size_t cchToWrite = pchNewLine ? pchNewLine - pchContent : cbContent;
5521 if (pAttr->uValues.pOctetStrings->cItems == 1)
5522 RTPrintf("%s %.*s\n", pThis->szPrefix, cchToWrite, pchContent);
5523 else
5524 RTPrintf("%s plist[%u]: %.*s\n", pThis->szPrefix, i, cchToWrite, pchContent);
5525 if (!pchNewLine)
5526 break;
5527 pchContent = pchNewLine + 1;
5528 cbContent -= cchToWrite + 1;
5529 }
5530 }
5531 else
5532 {
5533 if (pAttr->uValues.pContentInfos->cItems != 1)
5534 RTPrintf("%s: plist[%u]: Invalid UTF-8: %Rrc\n", pThis->szPrefix, i, rc);
5535 else
5536 RTPrintf("%s: Invalid UTF-8: %Rrc\n", pThis->szPrefix, rc);
5537 for (uint32_t off = 0; off < cbContent; off += 16)
5538 {
5539 size_t cbNow = RT_MIN(cbContent - off, 16);
5540 if (pAttr->uValues.pOctetStrings->cItems == 1)
5541 RTPrintf("%s %#06x: %.*Rhxs\n", pThis->szPrefix, off, cbNow, &pchContent[off]);
5542 else
5543 RTPrintf("%s plist[%u]: %#06x: %.*Rhxs\n", pThis->szPrefix, i, off, cbNow, &pchContent[off]);
5544 }
5545 }
5546 }
5547 break;
5548
5549 case RTCRPKCS7ATTRIBUTETYPE_INVALID:
5550 RTPrintf("%sINVALID!\n", pThis->szPrefix);
5551 break;
5552 case RTCRPKCS7ATTRIBUTETYPE_NOT_PRESENT:
5553 RTPrintf("%sNOT PRESENT!\n", pThis->szPrefix);
5554 break;
5555 default:
5556 RTPrintf("%senmType=%d!\n", pThis->szPrefix, pAttr->enmType);
5557 break;
5558 }
5559 return rc;
5560}
5561
5562
5563/**
5564 * Displays a SignerInfo structure.
5565 *
5566 * @returns IPRT status code.
5567 * @param pThis The show exe instance data.
5568 * @param offPrefix The current prefix offset.
5569 * @param pSignerInfo The structure to display.
5570 */
5571static int HandleShowExeWorkerPkcs7DisplaySignerInfo(PSHOWEXEPKCS7 pThis, size_t offPrefix, PCRTCRPKCS7SIGNERINFO pSignerInfo)
5572{
5573 int rc = RTAsn1Integer_ToString(&pSignerInfo->IssuerAndSerialNumber.SerialNumber,
5574 pThis->szTmp, sizeof(pThis->szTmp), 0 /*fFlags*/, NULL);
5575 if (RT_FAILURE(rc))
5576 RTStrPrintf(pThis->szTmp, sizeof(pThis->szTmp), "%Rrc", rc);
5577 RTPrintf("%s Serial No: %s\n", pThis->szPrefix, pThis->szTmp);
5578
5579 rc = RTCrX509Name_FormatAsString(&pSignerInfo->IssuerAndSerialNumber.Name, pThis->szTmp, sizeof(pThis->szTmp), NULL);
5580 if (RT_FAILURE(rc))
5581 RTStrPrintf(pThis->szTmp, sizeof(pThis->szTmp), "%Rrc", rc);
5582 RTPrintf("%s Issuer: %s\n", pThis->szPrefix, pThis->szTmp);
5583
5584 const char *pszType = RTCrDigestTypeToName(RTCrX509AlgorithmIdentifier_GetDigestType(&pSignerInfo->DigestAlgorithm,
5585 true /*fPureDigestsOnly*/));
5586 if (!pszType)
5587 pszType = pSignerInfo->DigestAlgorithm.Algorithm.szObjId;
5588 RTPrintf("%s Digest Algorithm: %s", pThis->szPrefix, pszType);
5589 if (pThis->cVerbosity > 1)
5590 RTPrintf(" (%s)\n", pSignerInfo->DigestAlgorithm.Algorithm.szObjId);
5591 else
5592 RTPrintf("\n");
5593
5594 HandleShowExeWorkerDisplayObjId(pThis, &pSignerInfo->DigestEncryptionAlgorithm.Algorithm,
5595 "Digest Encryption Algorithm: ", "\n");
5596
5597 if (pSignerInfo->AuthenticatedAttributes.cItems == 0)
5598 RTPrintf("%s Authenticated Attributes: none\n", pThis->szPrefix);
5599 else
5600 {
5601 RTPrintf("%s Authenticated Attributes: %u item%s\n", pThis->szPrefix,
5602 pSignerInfo->AuthenticatedAttributes.cItems, pSignerInfo->AuthenticatedAttributes.cItems > 1 ? "s" : "");
5603 for (unsigned j = 0; j < pSignerInfo->AuthenticatedAttributes.cItems; j++)
5604 {
5605 PRTCRPKCS7ATTRIBUTE pAttr = pSignerInfo->AuthenticatedAttributes.papItems[j];
5606 size_t offPrefix3 = offPrefix+ RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix,
5607 " AuthAttrib[%u]: ", j);
5608 HandleShowExeWorkerPkcs7DisplayAttrib(pThis, offPrefix3, pAttr);
5609 }
5610 pThis->szPrefix[offPrefix] = '\0';
5611 }
5612
5613 if (pSignerInfo->UnauthenticatedAttributes.cItems == 0)
5614 RTPrintf("%s Unauthenticated Attributes: none\n", pThis->szPrefix);
5615 else
5616 {
5617 RTPrintf("%s Unauthenticated Attributes: %u item%s\n", pThis->szPrefix,
5618 pSignerInfo->UnauthenticatedAttributes.cItems, pSignerInfo->UnauthenticatedAttributes.cItems > 1 ? "s" : "");
5619 for (unsigned j = 0; j < pSignerInfo->UnauthenticatedAttributes.cItems; j++)
5620 {
5621 PRTCRPKCS7ATTRIBUTE pAttr = pSignerInfo->UnauthenticatedAttributes.papItems[j];
5622 size_t offPrefix3 = offPrefix + RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix,
5623 " UnauthAttrib[%u]: ", j);
5624 HandleShowExeWorkerPkcs7DisplayAttrib(pThis, offPrefix3, pAttr);
5625 }
5626 pThis->szPrefix[offPrefix] = '\0';
5627 }
5628
5629 /** @todo show the encrypted stuff (EncryptedDigest)? */
5630 return rc;
5631}
5632
5633
5634/**
5635 * Displays a Microsoft SPC indirect data structure.
5636 *
5637 * @returns IPRT status code.
5638 * @param pThis The show exe instance data.
5639 * @param offPrefix The current prefix offset.
5640 * @param pIndData The indirect data to display.
5641 */
5642static int HandleShowExeWorkerPkcs7DisplaySpcIdirectDataContent(PSHOWEXEPKCS7 pThis, size_t offPrefix,
5643 PCRTCRSPCINDIRECTDATACONTENT pIndData)
5644{
5645 /*
5646 * The image hash.
5647 */
5648 RTDIGESTTYPE const enmDigestType = RTCrX509AlgorithmIdentifier_GetDigestType(&pIndData->DigestInfo.DigestAlgorithm,
5649 true /*fPureDigestsOnly*/);
5650 const char *pszDigestType = RTCrDigestTypeToName(enmDigestType);
5651 RTPrintf("%s Digest Type: %s", pThis->szPrefix, pszDigestType);
5652 if (pThis->cVerbosity > 1)
5653 RTPrintf(" (%s)\n", pIndData->DigestInfo.DigestAlgorithm.Algorithm.szObjId);
5654 else
5655 RTPrintf("\n");
5656 RTPrintf("%s Digest: %.*Rhxs\n",
5657 pThis->szPrefix, pIndData->DigestInfo.Digest.Asn1Core.cb, pIndData->DigestInfo.Digest.Asn1Core.uData.pu8);
5658
5659 /*
5660 * The data/file/url.
5661 */
5662 switch (pIndData->Data.enmType)
5663 {
5664 case RTCRSPCAAOVTYPE_PE_IMAGE_DATA:
5665 {
5666 RTPrintf("%s Data Type: PE Image Data\n", pThis->szPrefix);
5667 PRTCRSPCPEIMAGEDATA pPeImage = pIndData->Data.uValue.pPeImage;
5668 /** @todo display "Flags". */
5669
5670 switch (pPeImage->T0.File.enmChoice)
5671 {
5672 case RTCRSPCLINKCHOICE_MONIKER:
5673 {
5674 PRTCRSPCSERIALIZEDOBJECT pMoniker = pPeImage->T0.File.u.pMoniker;
5675 if (RTCrSpcSerializedObject_IsPresent(pMoniker))
5676 {
5677 if (RTUuidCompareStr(pMoniker->Uuid.Asn1Core.uData.pUuid, RTCRSPCSERIALIZEDOBJECT_UUID_STR) == 0)
5678 {
5679 RTPrintf("%s Moniker: SpcSerializedObject (%RTuuid)\n",
5680 pThis->szPrefix, pMoniker->Uuid.Asn1Core.uData.pUuid);
5681
5682 PCRTCRSPCSERIALIZEDOBJECTATTRIBUTES pData = pMoniker->u.pData;
5683 if (pData)
5684 for (uint32_t i = 0; i < pData->cItems; i++)
5685 {
5686 RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix,
5687 "MonikerAttrib[%u]: ", i);
5688
5689 switch (pData->papItems[i]->enmType)
5690 {
5691 case RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_PAGE_HASHES_V2:
5692 case RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_PAGE_HASHES_V1:
5693 {
5694 PCRTCRSPCSERIALIZEDPAGEHASHES pPgHashes = pData->papItems[i]->u.pPageHashes;
5695 uint32_t const cbHash = pData->papItems[i]->enmType
5696 == RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_PAGE_HASHES_V1
5697 ? 160/8 /*SHA-1*/ : 256/8 /*SHA-256*/;
5698 uint32_t const cPages = pPgHashes->RawData.Asn1Core.cb / (cbHash + sizeof(uint32_t));
5699
5700 RTPrintf("%sPage Hashes version %u - %u pages (%u bytes total)\n", pThis->szPrefix,
5701 pData->papItems[i]->enmType
5702 == RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_PAGE_HASHES_V1 ? 1 : 2,
5703 cPages, pPgHashes->RawData.Asn1Core.cb);
5704 if (pThis->cVerbosity > 0)
5705 {
5706 PCRTCRSPCPEIMAGEPAGEHASHES pPg = pPgHashes->pData;
5707 for (unsigned iPg = 0; iPg < cPages; iPg++)
5708 {
5709 uint32_t offHash = 0;
5710 do
5711 {
5712 if (offHash == 0)
5713 RTPrintf("%.*s Page#%04u/%#08x: ",
5714 offPrefix, pThis->szPrefix, iPg, pPg->Generic.offFile);
5715 else
5716 RTPrintf("%.*s ", offPrefix, pThis->szPrefix);
5717 uint32_t cbLeft = cbHash - offHash;
5718 if (cbLeft > 24)
5719 cbLeft = 16;
5720 RTPrintf("%.*Rhxs\n", cbLeft, &pPg->Generic.abHash[offHash]);
5721 offHash += cbLeft;
5722 } while (offHash < cbHash);
5723 pPg = (PCRTCRSPCPEIMAGEPAGEHASHES)&pPg->Generic.abHash[cbHash];
5724 }
5725
5726 if (pThis->cVerbosity > 3)
5727 RTPrintf("%.*Rhxd\n",
5728 pPgHashes->RawData.Asn1Core.cb,
5729 pPgHashes->RawData.Asn1Core.uData.pu8);
5730 }
5731 break;
5732 }
5733
5734 case RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_UNKNOWN:
5735 HandleShowExeWorkerDisplayObjIdSimple(pThis, &pData->papItems[i]->Type, "\n");
5736 break;
5737 case RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_NOT_PRESENT:
5738 RTPrintf("%sNot present!\n", pThis->szPrefix);
5739 break;
5740 default:
5741 RTPrintf("%senmType=%d!\n", pThis->szPrefix, pData->papItems[i]->enmType);
5742 break;
5743 }
5744 pThis->szPrefix[offPrefix] = '\0';
5745 }
5746 else
5747 RTPrintf("%s pData is NULL!\n", pThis->szPrefix);
5748 }
5749 else
5750 RTPrintf("%s Moniker: Unknown UUID: %RTuuid\n",
5751 pThis->szPrefix, pMoniker->Uuid.Asn1Core.uData.pUuid);
5752 }
5753 else
5754 RTPrintf("%s Moniker: not present\n", pThis->szPrefix);
5755 break;
5756 }
5757
5758 case RTCRSPCLINKCHOICE_URL:
5759 {
5760 const char *pszUrl = NULL;
5761 int rc = pPeImage->T0.File.u.pUrl
5762 ? RTAsn1String_QueryUtf8(pPeImage->T0.File.u.pUrl, &pszUrl, NULL)
5763 : VERR_NOT_FOUND;
5764 if (RT_SUCCESS(rc))
5765 RTPrintf("%s URL: '%s'\n", pThis->szPrefix, pszUrl);
5766 else
5767 RTPrintf("%s URL: rc=%Rrc\n", pThis->szPrefix, rc);
5768 break;
5769 }
5770
5771 case RTCRSPCLINKCHOICE_FILE:
5772 {
5773 const char *pszFile = NULL;
5774 int rc = pPeImage->T0.File.u.pT2 && pPeImage->T0.File.u.pT2->File.u.pAscii
5775 ? RTAsn1String_QueryUtf8(pPeImage->T0.File.u.pT2->File.u.pAscii, &pszFile, NULL)
5776 : VERR_NOT_FOUND;
5777 if (RT_SUCCESS(rc))
5778 RTPrintf("%s File: '%s'\n", pThis->szPrefix, pszFile);
5779 else
5780 RTPrintf("%s File: rc=%Rrc\n", pThis->szPrefix, rc);
5781 if (pThis->cVerbosity > 4 && pPeImage->T0.File.u.pT2 == NULL)
5782 RTPrintf("%s pT2=NULL\n", pThis->szPrefix);
5783 else if (pThis->cVerbosity > 4)
5784 {
5785 PCRTASN1STRING pStr = pPeImage->T0.File.u.pT2->File.u.pAscii;
5786 RTPrintf("%s pT2=%p/%p LB %#x fFlags=%#x pOps=%p (%s)\n"
5787 "%s enmChoice=%d pStr=%p/%p LB %#x fFlags=%#x\n",
5788 pThis->szPrefix,
5789 pPeImage->T0.File.u.pT2,
5790 pPeImage->T0.File.u.pT2->CtxTag2.Asn1Core.uData.pu8,
5791 pPeImage->T0.File.u.pT2->CtxTag2.Asn1Core.cb,
5792 pPeImage->T0.File.u.pT2->CtxTag2.Asn1Core.fFlags,
5793 pPeImage->T0.File.u.pT2->CtxTag2.Asn1Core.pOps,
5794 pPeImage->T0.File.u.pT2->CtxTag2.Asn1Core.pOps
5795 ? pPeImage->T0.File.u.pT2->CtxTag2.Asn1Core.pOps->pszName : "",
5796 pThis->szPrefix,
5797 pPeImage->T0.File.u.pT2->File.enmChoice,
5798 pStr,
5799 pStr ? pStr->Asn1Core.uData.pu8 : NULL,
5800 pStr ? pStr->Asn1Core.cb : 0,
5801 pStr ? pStr->Asn1Core.fFlags : 0);
5802 }
5803 break;
5804 }
5805
5806 case RTCRSPCLINKCHOICE_NOT_PRESENT:
5807 RTPrintf("%s File not present!\n", pThis->szPrefix);
5808 break;
5809 default:
5810 RTPrintf("%s enmChoice=%d!\n", pThis->szPrefix, pPeImage->T0.File.enmChoice);
5811 break;
5812 }
5813 break;
5814 }
5815
5816 case RTCRSPCAAOVTYPE_UNKNOWN:
5817 HandleShowExeWorkerDisplayObjId(pThis, &pIndData->Data.Type, " Data Type: ", "\n");
5818 break;
5819 case RTCRSPCAAOVTYPE_NOT_PRESENT:
5820 RTPrintf("%s Data Type: Not present!\n", pThis->szPrefix);
5821 break;
5822 default:
5823 RTPrintf("%s Data Type: enmType=%d!\n", pThis->szPrefix, pIndData->Data.enmType);
5824 break;
5825 }
5826
5827 return VINF_SUCCESS;
5828}
5829
5830
5831/**
5832 * Display an PKCS#7 signed data instance.
5833 *
5834 * @returns IPRT status code.
5835 * @param pThis The show exe instance data.
5836 * @param pSignedData The signed data to display.
5837 * @param offPrefix The current prefix offset.
5838 * @param pContentInfo The content info structure (for the size).
5839 */
5840static int HandleShowExeWorkerPkcs7Display(PSHOWEXEPKCS7 pThis, PRTCRPKCS7SIGNEDDATA pSignedData, size_t offPrefix,
5841 PCRTCRPKCS7CONTENTINFO pContentInfo)
5842{
5843 pThis->szPrefix[offPrefix] = '\0';
5844 RTPrintf("%sPKCS#7 signature: %u (%#x) bytes\n", pThis->szPrefix,
5845 RTASN1CORE_GET_RAW_ASN1_SIZE(&pContentInfo->SeqCore.Asn1Core),
5846 RTASN1CORE_GET_RAW_ASN1_SIZE(&pContentInfo->SeqCore.Asn1Core));
5847
5848 /*
5849 * Display list of signing algorithms.
5850 */
5851 RTPrintf("%sDigestAlgorithms: ", pThis->szPrefix);
5852 if (pSignedData->DigestAlgorithms.cItems == 0)
5853 RTPrintf("none");
5854 for (unsigned i = 0; i < pSignedData->DigestAlgorithms.cItems; i++)
5855 {
5856 PCRTCRX509ALGORITHMIDENTIFIER pAlgoId = pSignedData->DigestAlgorithms.papItems[i];
5857 const char *pszDigestType = RTCrDigestTypeToName(RTCrX509AlgorithmIdentifier_GetDigestType(pAlgoId,
5858 true /*fPureDigestsOnly*/));
5859 if (!pszDigestType)
5860 pszDigestType = pAlgoId->Algorithm.szObjId;
5861 RTPrintf(i == 0 ? "%s" : ", %s", pszDigestType);
5862 if (pThis->cVerbosity > 1)
5863 RTPrintf(" (%s)", pAlgoId->Algorithm.szObjId);
5864 }
5865 RTPrintf("\n");
5866
5867 /*
5868 * Display the signed data content.
5869 */
5870 if (RTAsn1ObjId_CompareWithString(&pSignedData->ContentInfo.ContentType, RTCRSPCINDIRECTDATACONTENT_OID) == 0)
5871 {
5872 RTPrintf("%s ContentType: SpcIndirectDataContent (" RTCRSPCINDIRECTDATACONTENT_OID ")\n", pThis->szPrefix);
5873 size_t offPrefix2 = RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix, " SPC Ind Data: ");
5874 HandleShowExeWorkerPkcs7DisplaySpcIdirectDataContent(pThis, offPrefix2 + offPrefix,
5875 pSignedData->ContentInfo.u.pIndirectDataContent);
5876 pThis->szPrefix[offPrefix] = '\0';
5877 }
5878 else
5879 {
5880 HandleShowExeWorkerDisplayObjId(pThis, &pSignedData->ContentInfo.ContentType, " ContentType: ", " - not implemented.\n");
5881 RTPrintf("%s %u (%#x) bytes\n", pThis->szPrefix,
5882 pSignedData->ContentInfo.Content.Asn1Core.cb, pSignedData->ContentInfo.Content.Asn1Core.cb);
5883 }
5884
5885 /*
5886 * Display certificates (Certificates).
5887 */
5888 if (pSignedData->Certificates.cItems > 0)
5889 {
5890 RTPrintf("%s Certificates: %u\n", pThis->szPrefix, pSignedData->Certificates.cItems);
5891 for (uint32_t i = 0; i < pSignedData->Certificates.cItems; i++)
5892 {
5893 PCRTCRPKCS7CERT pCert = pSignedData->Certificates.papItems[i];
5894 if (i != 0 && pThis->cVerbosity >= 2)
5895 RTPrintf("\n");
5896 switch (pCert->enmChoice)
5897 {
5898 case RTCRPKCS7CERTCHOICE_X509:
5899 {
5900 PCRTCRX509CERTIFICATE pX509Cert = pCert->u.pX509Cert;
5901 int rc2 = RTAsn1QueryObjIdName(&pX509Cert->SignatureAlgorithm.Algorithm, pThis->szTmp, sizeof(pThis->szTmp));
5902 RTPrintf("%s Certificate #%u: %s\n", pThis->szPrefix, i,
5903 RT_SUCCESS(rc2) ? pThis->szTmp : pX509Cert->SignatureAlgorithm.Algorithm.szObjId);
5904
5905 rc2 = RTCrX509Name_FormatAsString(&pX509Cert->TbsCertificate.Subject,
5906 pThis->szTmp, sizeof(pThis->szTmp), NULL);
5907 if (RT_FAILURE(rc2))
5908 RTStrPrintf(pThis->szTmp, sizeof(pThis->szTmp), "%Rrc", rc2);
5909 RTPrintf("%s Subject: %s\n", pThis->szPrefix, pThis->szTmp);
5910
5911 rc2 = RTCrX509Name_FormatAsString(&pX509Cert->TbsCertificate.Issuer,
5912 pThis->szTmp, sizeof(pThis->szTmp), NULL);
5913 if (RT_FAILURE(rc2))
5914 RTStrPrintf(pThis->szTmp, sizeof(pThis->szTmp), "%Rrc", rc2);
5915 RTPrintf("%s Issuer: %s\n", pThis->szPrefix, pThis->szTmp);
5916
5917
5918 char szNotAfter[RTTIME_STR_LEN];
5919 RTPrintf("%s Valid: %s thru %s\n", pThis->szPrefix,
5920 RTTimeToString(&pX509Cert->TbsCertificate.Validity.NotBefore.Time,
5921 pThis->szTmp, sizeof(pThis->szTmp)),
5922 RTTimeToString(&pX509Cert->TbsCertificate.Validity.NotAfter.Time,
5923 szNotAfter, sizeof(szNotAfter)));
5924 break;
5925 }
5926
5927 default:
5928 RTPrintf("%s Certificate #%u: Unsupported type\n", pThis->szPrefix, i);
5929 break;
5930 }
5931
5932
5933 if (pThis->cVerbosity >= 2)
5934 RTAsn1Dump(RTCrPkcs7Cert_GetAsn1Core(pSignedData->Certificates.papItems[i]), 0,
5935 ((uint32_t)offPrefix + 9) / 2, RTStrmDumpPrintfV, g_pStdOut);
5936 }
5937
5938 /** @todo display certificates properly. */
5939 }
5940
5941 if (pSignedData->Crls.cb > 0)
5942 RTPrintf("%s CRLs: %u bytes\n", pThis->szPrefix, pSignedData->Crls.cb);
5943
5944 /*
5945 * Show signatures (SignerInfos).
5946 */
5947 unsigned const cSigInfos = pSignedData->SignerInfos.cItems;
5948 if (cSigInfos != 1)
5949 RTPrintf("%s SignerInfos: %u signers\n", pThis->szPrefix, cSigInfos);
5950 else
5951 RTPrintf("%s SignerInfos:\n", pThis->szPrefix);
5952 int rc = VINF_SUCCESS;
5953 for (unsigned i = 0; i < cSigInfos; i++)
5954 {
5955 size_t offPrefix2 = offPrefix;
5956 if (cSigInfos != 1)
5957 offPrefix2 += RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix, "SignerInfo[%u]: ", i);
5958
5959 int rc2 = HandleShowExeWorkerPkcs7DisplaySignerInfo(pThis, offPrefix2, pSignedData->SignerInfos.papItems[i]);
5960 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
5961 rc = rc2;
5962 }
5963 pThis->szPrefix[offPrefix] = '\0';
5964
5965 return rc;
5966}
5967
5968
5969/*
5970 * The 'show-exe' command.
5971 */
5972static RTEXITCODE HelpShowExe(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
5973{
5974 RT_NOREF_PV(enmLevel);
5975 RTStrmWrappedPrintf(pStrm, RTSTRMWRAPPED_F_HANGING_INDENT, "show-exe [--verbose|-v] [--quiet|-q] <exe1> [exe2 [..]]\n");
5976 return RTEXITCODE_SUCCESS;
5977}
5978
5979
5980static RTEXITCODE HandleShowExe(int cArgs, char **papszArgs)
5981{
5982 /*
5983 * Parse arguments.
5984 */
5985 static const RTGETOPTDEF s_aOptions[] =
5986 {
5987 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
5988 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
5989 };
5990
5991 unsigned cVerbosity = 0;
5992 RTLDRARCH enmLdrArch = RTLDRARCH_WHATEVER;
5993
5994 RTGETOPTSTATE GetState;
5995 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
5996 AssertRCReturn(rc, RTEXITCODE_FAILURE);
5997 RTGETOPTUNION ValueUnion;
5998 int ch;
5999 while ((ch = RTGetOpt(&GetState, &ValueUnion)) && ch != VINF_GETOPT_NOT_OPTION)
6000 {
6001 switch (ch)
6002 {
6003 case 'v': cVerbosity++; break;
6004 case 'q': cVerbosity = 0; break;
6005 case 'V': return HandleVersion(cArgs, papszArgs);
6006 case 'h': return HelpShowExe(g_pStdOut, RTSIGNTOOLHELP_FULL);
6007 default: return RTGetOptPrintError(ch, &ValueUnion);
6008 }
6009 }
6010 if (ch != VINF_GETOPT_NOT_OPTION)
6011 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No executable given.");
6012
6013 /*
6014 * Do it.
6015 */
6016 unsigned iFile = 0;
6017 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
6018 do
6019 {
6020 RTPrintf(iFile == 0 ? "%s:\n" : "\n%s:\n", ValueUnion.psz);
6021
6022 SHOWEXEPKCS7 This;
6023 RT_ZERO(This);
6024 This.cVerbosity = cVerbosity;
6025
6026 RTEXITCODE rcExitThis = SignToolPkcs7Exe_InitFromFile(&This, ValueUnion.psz, cVerbosity, enmLdrArch);
6027 if (rcExitThis == RTEXITCODE_SUCCESS)
6028 {
6029 rc = HandleShowExeWorkerPkcs7Display(&This, This.pSignedData, 0, &This.ContentInfo);
6030 if (RT_FAILURE(rc))
6031 rcExit = RTEXITCODE_FAILURE;
6032 SignToolPkcs7Exe_Delete(&This);
6033 }
6034 if (rcExitThis != RTEXITCODE_SUCCESS && rcExit == RTEXITCODE_SUCCESS)
6035 rcExit = rcExitThis;
6036
6037 iFile++;
6038 } while ((ch = RTGetOpt(&GetState, &ValueUnion)) == VINF_GETOPT_NOT_OPTION);
6039 if (ch != 0)
6040 return RTGetOptPrintError(ch, &ValueUnion);
6041
6042 return rcExit;
6043}
6044
6045
6046/*
6047 * The 'show-cat' command.
6048 */
6049static RTEXITCODE HelpShowCat(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
6050{
6051 RT_NOREF_PV(enmLevel);
6052 RTStrmWrappedPrintf(pStrm, RTSTRMWRAPPED_F_HANGING_INDENT, "show-cat [--verbose|-v] [--quiet|-q] <cat1> [cat2 [..]]\n");
6053 return RTEXITCODE_SUCCESS;
6054}
6055
6056
6057static RTEXITCODE HandleShowCat(int cArgs, char **papszArgs)
6058{
6059 /*
6060 * Parse arguments.
6061 */
6062 static const RTGETOPTDEF s_aOptions[] =
6063 {
6064 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
6065 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
6066 };
6067
6068 unsigned cVerbosity = 0;
6069
6070 RTGETOPTSTATE GetState;
6071 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
6072 AssertRCReturn(rc, RTEXITCODE_FAILURE);
6073 RTGETOPTUNION ValueUnion;
6074 int ch;
6075 while ((ch = RTGetOpt(&GetState, &ValueUnion)) && ch != VINF_GETOPT_NOT_OPTION)
6076 {
6077 switch (ch)
6078 {
6079 case 'v': cVerbosity++; break;
6080 case 'q': cVerbosity = 0; break;
6081 case 'V': return HandleVersion(cArgs, papszArgs);
6082 case 'h': return HelpShowCat(g_pStdOut, RTSIGNTOOLHELP_FULL);
6083 default: return RTGetOptPrintError(ch, &ValueUnion);
6084 }
6085 }
6086 if (ch != VINF_GETOPT_NOT_OPTION)
6087 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No executable given.");
6088
6089 /*
6090 * Do it.
6091 */
6092 unsigned iFile = 0;
6093 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
6094 do
6095 {
6096 RTPrintf(iFile == 0 ? "%s:\n" : "\n%s:\n", ValueUnion.psz);
6097
6098 SHOWEXEPKCS7 This;
6099 RT_ZERO(This);
6100 This.cVerbosity = cVerbosity;
6101
6102 RTEXITCODE rcExitThis = SignToolPkcs7_InitFromFile(&This, ValueUnion.psz, cVerbosity);
6103 if (rcExitThis == RTEXITCODE_SUCCESS)
6104 {
6105 This.hLdrMod = NIL_RTLDRMOD;
6106
6107 rc = HandleShowExeWorkerPkcs7Display(&This, This.pSignedData, 0, &This.ContentInfo);
6108 if (RT_FAILURE(rc))
6109 rcExit = RTEXITCODE_FAILURE;
6110 SignToolPkcs7Exe_Delete(&This);
6111 }
6112 if (rcExitThis != RTEXITCODE_SUCCESS && rcExit == RTEXITCODE_SUCCESS)
6113 rcExit = rcExitThis;
6114
6115 iFile++;
6116 } while ((ch = RTGetOpt(&GetState, &ValueUnion)) == VINF_GETOPT_NOT_OPTION);
6117 if (ch != 0)
6118 return RTGetOptPrintError(ch, &ValueUnion);
6119
6120 return rcExit;
6121}
6122
6123
6124/*********************************************************************************************************************************
6125* The 'hash-exe' command. *
6126*********************************************************************************************************************************/
6127static RTEXITCODE HelpHashExe(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
6128{
6129 RT_NOREF_PV(enmLevel);
6130 RTStrmWrappedPrintf(pStrm, RTSTRMWRAPPED_F_HANGING_INDENT, "hash-exe [--verbose|-v] [--quiet|-q] <exe1> [exe2 [..]]\n");
6131 return RTEXITCODE_SUCCESS;
6132}
6133
6134
6135static RTEXITCODE HandleHashExe(int cArgs, char **papszArgs)
6136{
6137 /*
6138 * Parse arguments.
6139 */
6140 static const RTGETOPTDEF s_aOptions[] =
6141 {
6142 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
6143 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
6144 };
6145
6146 unsigned cVerbosity = 0;
6147 RTLDRARCH enmLdrArch = RTLDRARCH_WHATEVER;
6148
6149 RTGETOPTSTATE GetState;
6150 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
6151 AssertRCReturn(rc, RTEXITCODE_FAILURE);
6152 RTGETOPTUNION ValueUnion;
6153 int ch;
6154 while ((ch = RTGetOpt(&GetState, &ValueUnion)) && ch != VINF_GETOPT_NOT_OPTION)
6155 {
6156 switch (ch)
6157 {
6158 case 'v': cVerbosity++; break;
6159 case 'q': cVerbosity = 0; break;
6160 case 'V': return HandleVersion(cArgs, papszArgs);
6161 case 'h': return HelpHashExe(g_pStdOut, RTSIGNTOOLHELP_FULL);
6162 default: return RTGetOptPrintError(ch, &ValueUnion);
6163 }
6164 }
6165 if (ch != VINF_GETOPT_NOT_OPTION)
6166 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No executable given.");
6167
6168 /*
6169 * Do it.
6170 */
6171 unsigned iFile = 0;
6172 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
6173 do
6174 {
6175 RTPrintf(iFile == 0 ? "%s:\n" : "\n%s:\n", ValueUnion.psz);
6176
6177 RTERRINFOSTATIC ErrInfo;
6178 RTLDRMOD hLdrMod;
6179 rc = RTLdrOpenEx(ValueUnion.psz, RTLDR_O_FOR_VALIDATION, enmLdrArch, &hLdrMod, RTErrInfoInitStatic(&ErrInfo));
6180 if (RT_SUCCESS(rc))
6181 {
6182 uint8_t abHash[RTSHA512_HASH_SIZE];
6183 char szDigest[RTSHA512_DIGEST_LEN + 1];
6184
6185 /* SHA-1: */
6186 rc = RTLdrHashImage(hLdrMod, RTDIGESTTYPE_SHA1, abHash, sizeof(abHash));
6187 if (RT_SUCCESS(rc))
6188 RTSha1ToString(abHash, szDigest, sizeof(szDigest));
6189 else
6190 RTStrPrintf(szDigest, sizeof(szDigest), "%Rrc", rc);
6191 RTPrintf(" SHA-1: %s\n", szDigest);
6192
6193 /* SHA-256: */
6194 rc = RTLdrHashImage(hLdrMod, RTDIGESTTYPE_SHA256, abHash, sizeof(abHash));
6195 if (RT_SUCCESS(rc))
6196 RTSha256ToString(abHash, szDigest, sizeof(szDigest));
6197 else
6198 RTStrPrintf(szDigest, sizeof(szDigest), "%Rrc", rc);
6199 RTPrintf(" SHA-256: %s\n", szDigest);
6200
6201 /* SHA-512: */
6202 rc = RTLdrHashImage(hLdrMod, RTDIGESTTYPE_SHA512, abHash, sizeof(abHash));
6203 if (RT_SUCCESS(rc))
6204 RTSha512ToString(abHash, szDigest, sizeof(szDigest));
6205 else
6206 RTStrPrintf(szDigest, sizeof(szDigest), "%Rrc", rc);
6207 RTPrintf(" SHA-512: %s\n", szDigest);
6208
6209 RTLdrClose(hLdrMod);
6210 }
6211 else
6212 rcExit = RTMsgErrorExitFailure("Failed to open '%s': %Rrc%#RTeim", ValueUnion.psz, rc, &ErrInfo.Core);
6213
6214 } while ((ch = RTGetOpt(&GetState, &ValueUnion)) == VINF_GETOPT_NOT_OPTION);
6215 if (ch != 0)
6216 return RTGetOptPrintError(ch, &ValueUnion);
6217
6218 return rcExit;
6219}
6220
6221
6222/*********************************************************************************************************************************
6223* The 'make-tainfo' command. *
6224*********************************************************************************************************************************/
6225static RTEXITCODE HelpMakeTaInfo(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
6226{
6227 RT_NOREF_PV(enmLevel);
6228 RTStrmWrappedPrintf(pStrm, RTSTRMWRAPPED_F_HANGING_INDENT,
6229 "make-tainfo [--verbose|--quiet] [--cert <cert.der>] [-o|--output] <tainfo.der>\n");
6230 return RTEXITCODE_SUCCESS;
6231}
6232
6233
6234typedef struct MAKETAINFOSTATE
6235{
6236 int cVerbose;
6237 const char *pszCert;
6238 const char *pszOutput;
6239} MAKETAINFOSTATE;
6240
6241
6242/** @callback_method_impl{FNRTASN1ENCODEWRITER} */
6243static DECLCALLBACK(int) handleMakeTaInfoWriter(const void *pvBuf, size_t cbToWrite, void *pvUser, PRTERRINFO pErrInfo)
6244{
6245 RT_NOREF_PV(pErrInfo);
6246 return RTStrmWrite((PRTSTREAM)pvUser, pvBuf, cbToWrite);
6247}
6248
6249
6250static RTEXITCODE HandleMakeTaInfo(int cArgs, char **papszArgs)
6251{
6252 /*
6253 * Parse arguments.
6254 */
6255 static const RTGETOPTDEF s_aOptions[] =
6256 {
6257 { "--cert", 'c', RTGETOPT_REQ_STRING },
6258 { "--output", 'o', RTGETOPT_REQ_STRING },
6259 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
6260 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
6261 };
6262
6263 MAKETAINFOSTATE State = { 0, NULL, NULL };
6264
6265 RTGETOPTSTATE GetState;
6266 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
6267 AssertRCReturn(rc, RTEXITCODE_FAILURE);
6268 RTGETOPTUNION ValueUnion;
6269 int ch;
6270 while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
6271 {
6272 switch (ch)
6273 {
6274 case 'c':
6275 if (State.pszCert)
6276 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The --cert option can only be used once.");
6277 State.pszCert = ValueUnion.psz;
6278 break;
6279
6280 case 'o':
6281 case VINF_GETOPT_NOT_OPTION:
6282 if (State.pszOutput)
6283 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Multiple output files specified.");
6284 State.pszOutput = ValueUnion.psz;
6285 break;
6286
6287 case 'v': State.cVerbose++; break;
6288 case 'q': State.cVerbose = 0; break;
6289 case 'V': return HandleVersion(cArgs, papszArgs);
6290 case 'h': return HelpMakeTaInfo(g_pStdOut, RTSIGNTOOLHELP_FULL);
6291 default: return RTGetOptPrintError(ch, &ValueUnion);
6292 }
6293 }
6294 if (!State.pszCert)
6295 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No input certificate was specified.");
6296 if (!State.pszOutput)
6297 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No output file was specified.");
6298
6299 /*
6300 * Read the certificate.
6301 */
6302 RTERRINFOSTATIC StaticErrInfo;
6303 RTCRX509CERTIFICATE Certificate;
6304 rc = RTCrX509Certificate_ReadFromFile(&Certificate, State.pszCert, 0, &g_RTAsn1DefaultAllocator,
6305 RTErrInfoInitStatic(&StaticErrInfo));
6306 if (RT_FAILURE(rc))
6307 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error reading certificate from %s: %Rrc - %s",
6308 State.pszCert, rc, StaticErrInfo.szMsg);
6309 /*
6310 * Construct the trust anchor information.
6311 */
6312 RTCRTAFTRUSTANCHORINFO TrustAnchor;
6313 rc = RTCrTafTrustAnchorInfo_Init(&TrustAnchor, &g_RTAsn1DefaultAllocator);
6314 if (RT_SUCCESS(rc))
6315 {
6316 /* Public key. */
6317 Assert(RTCrX509SubjectPublicKeyInfo_IsPresent(&TrustAnchor.PubKey));
6318 RTCrX509SubjectPublicKeyInfo_Delete(&TrustAnchor.PubKey);
6319 rc = RTCrX509SubjectPublicKeyInfo_Clone(&TrustAnchor.PubKey, &Certificate.TbsCertificate.SubjectPublicKeyInfo,
6320 &g_RTAsn1DefaultAllocator);
6321 if (RT_FAILURE(rc))
6322 RTMsgError("RTCrX509SubjectPublicKeyInfo_Clone failed: %Rrc", rc);
6323 RTAsn1Core_ResetImplict(RTCrX509SubjectPublicKeyInfo_GetAsn1Core(&TrustAnchor.PubKey)); /* temporary hack. */
6324
6325 /* Key Identifier. */
6326 PCRTASN1OCTETSTRING pKeyIdentifier = NULL;
6327 if (Certificate.TbsCertificate.T3.fFlags & RTCRX509TBSCERTIFICATE_F_PRESENT_SUBJECT_KEY_IDENTIFIER)
6328 pKeyIdentifier = Certificate.TbsCertificate.T3.pSubjectKeyIdentifier;
6329 else if ( (Certificate.TbsCertificate.T3.fFlags & RTCRX509TBSCERTIFICATE_F_PRESENT_AUTHORITY_KEY_IDENTIFIER)
6330 && RTCrX509Certificate_IsSelfSigned(&Certificate)
6331 && RTAsn1OctetString_IsPresent(&Certificate.TbsCertificate.T3.pAuthorityKeyIdentifier->KeyIdentifier) )
6332 pKeyIdentifier = &Certificate.TbsCertificate.T3.pAuthorityKeyIdentifier->KeyIdentifier;
6333 else if ( (Certificate.TbsCertificate.T3.fFlags & RTCRX509TBSCERTIFICATE_F_PRESENT_OLD_AUTHORITY_KEY_IDENTIFIER)
6334 && RTCrX509Certificate_IsSelfSigned(&Certificate)
6335 && RTAsn1OctetString_IsPresent(&Certificate.TbsCertificate.T3.pOldAuthorityKeyIdentifier->KeyIdentifier) )
6336 pKeyIdentifier = &Certificate.TbsCertificate.T3.pOldAuthorityKeyIdentifier->KeyIdentifier;
6337 if (pKeyIdentifier && pKeyIdentifier->Asn1Core.cb > 0)
6338 {
6339 Assert(RTAsn1OctetString_IsPresent(&TrustAnchor.KeyIdentifier));
6340 RTAsn1OctetString_Delete(&TrustAnchor.KeyIdentifier);
6341 rc = RTAsn1OctetString_Clone(&TrustAnchor.KeyIdentifier, pKeyIdentifier, &g_RTAsn1DefaultAllocator);
6342 if (RT_FAILURE(rc))
6343 RTMsgError("RTAsn1OctetString_Clone failed: %Rrc", rc);
6344 RTAsn1Core_ResetImplict(RTAsn1OctetString_GetAsn1Core(&TrustAnchor.KeyIdentifier)); /* temporary hack. */
6345 }
6346 else
6347 RTMsgWarning("No key identifier found or has zero length.");
6348
6349 /* Subject */
6350 if (RT_SUCCESS(rc))
6351 {
6352 Assert(!RTCrTafCertPathControls_IsPresent(&TrustAnchor.CertPath));
6353 rc = RTCrTafCertPathControls_Init(&TrustAnchor.CertPath, &g_RTAsn1DefaultAllocator);
6354 if (RT_SUCCESS(rc))
6355 {
6356 Assert(RTCrX509Name_IsPresent(&TrustAnchor.CertPath.TaName));
6357 RTCrX509Name_Delete(&TrustAnchor.CertPath.TaName);
6358 rc = RTCrX509Name_Clone(&TrustAnchor.CertPath.TaName, &Certificate.TbsCertificate.Subject,
6359 &g_RTAsn1DefaultAllocator);
6360 if (RT_SUCCESS(rc))
6361 {
6362 RTAsn1Core_ResetImplict(RTCrX509Name_GetAsn1Core(&TrustAnchor.CertPath.TaName)); /* temporary hack. */
6363 rc = RTCrX509Name_RecodeAsUtf8(&TrustAnchor.CertPath.TaName, &g_RTAsn1DefaultAllocator);
6364 if (RT_FAILURE(rc))
6365 RTMsgError("RTCrX509Name_RecodeAsUtf8 failed: %Rrc", rc);
6366 }
6367 else
6368 RTMsgError("RTCrX509Name_Clone failed: %Rrc", rc);
6369 }
6370 else
6371 RTMsgError("RTCrTafCertPathControls_Init failed: %Rrc", rc);
6372 }
6373
6374 /* Check that what we've constructed makes some sense. */
6375 if (RT_SUCCESS(rc))
6376 {
6377 rc = RTCrTafTrustAnchorInfo_CheckSanity(&TrustAnchor, 0, RTErrInfoInitStatic(&StaticErrInfo), "TAI");
6378 if (RT_FAILURE(rc))
6379 RTMsgError("RTCrTafTrustAnchorInfo_CheckSanity failed: %Rrc - %s", rc, StaticErrInfo.szMsg);
6380 }
6381
6382 if (RT_SUCCESS(rc))
6383 {
6384 /*
6385 * Encode it and write it to the output file.
6386 */
6387 uint32_t cbEncoded;
6388 rc = RTAsn1EncodePrepare(RTCrTafTrustAnchorInfo_GetAsn1Core(&TrustAnchor), RTASN1ENCODE_F_DER, &cbEncoded,
6389 RTErrInfoInitStatic(&StaticErrInfo));
6390 if (RT_SUCCESS(rc))
6391 {
6392 if (State.cVerbose >= 1)
6393 RTAsn1Dump(RTCrTafTrustAnchorInfo_GetAsn1Core(&TrustAnchor), 0, 0, RTStrmDumpPrintfV, g_pStdOut);
6394
6395 PRTSTREAM pStrm;
6396 rc = RTStrmOpen(State.pszOutput, "wb", &pStrm);
6397 if (RT_SUCCESS(rc))
6398 {
6399 rc = RTAsn1EncodeWrite(RTCrTafTrustAnchorInfo_GetAsn1Core(&TrustAnchor), RTASN1ENCODE_F_DER,
6400 handleMakeTaInfoWriter, pStrm, RTErrInfoInitStatic(&StaticErrInfo));
6401 if (RT_SUCCESS(rc))
6402 {
6403 rc = RTStrmClose(pStrm);
6404 if (RT_SUCCESS(rc))
6405 RTMsgInfo("Successfully wrote TrustedAnchorInfo to '%s'.", State.pszOutput);
6406 else
6407 RTMsgError("RTStrmClose failed: %Rrc", rc);
6408 }
6409 else
6410 {
6411 RTMsgError("RTAsn1EncodeWrite failed: %Rrc - %s", rc, StaticErrInfo.szMsg);
6412 RTStrmClose(pStrm);
6413 }
6414 }
6415 else
6416 RTMsgError("Error opening '%s' for writing: %Rrcs", State.pszOutput, rc);
6417 }
6418 else
6419 RTMsgError("RTAsn1EncodePrepare failed: %Rrc - %s", rc, StaticErrInfo.szMsg);
6420 }
6421
6422 RTCrTafTrustAnchorInfo_Delete(&TrustAnchor);
6423 }
6424 else
6425 RTMsgError("RTCrTafTrustAnchorInfo_Init failed: %Rrc", rc);
6426
6427 RTCrX509Certificate_Delete(&Certificate);
6428 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
6429}
6430
6431
6432
6433/*********************************************************************************************************************************
6434* The 'create-self-signed-rsa-cert' command. *
6435*********************************************************************************************************************************/
6436#ifndef IPRT_IN_BUILD_TOOL
6437
6438static RTEXITCODE HelpCreateSelfSignedRsaCert(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
6439{
6440 RT_NOREF_PV(enmLevel);
6441 RTStrmWrappedPrintf(pStrm, RTSTRMWRAPPED_F_HANGING_INDENT,
6442 "create-self-signed-rsa-cert [--verbose|--quiet] [--key-bits <count>] [--digest <hash>] [--out-cert=]<certificate-file.pem> [--out-pkey=]<private-key-file.pem>\n");
6443 return RTEXITCODE_SUCCESS;
6444}
6445
6446
6447static RTDIGESTTYPE DigestTypeStringToValue(const char *pszType)
6448{
6449 for (int iType = RTDIGESTTYPE_INVALID + 1; iType < RTDIGESTTYPE_END; iType++)
6450 if (iType != RTDIGESTTYPE_UNKNOWN)
6451 {
6452 const char * const pszName = RTCrDigestTypeToName((RTDIGESTTYPE)iType);
6453 size_t offType = 0;
6454 size_t offName = 0;
6455 for (;;)
6456 {
6457 char chType = RT_C_TO_UPPER(pszType[offType]);
6458 char chName = RT_C_TO_UPPER(pszName[offType]);
6459 if (chType != chName)
6460 {
6461 /* allow 'sha1' as well as 'sha-1' */
6462 if (chName != '-')
6463 break;
6464 chName = pszName[++offName];
6465 chName = RT_C_TO_UPPER(chName);
6466 if (chType != chName)
6467 break;
6468 }
6469 if (chType == '\0')
6470 return (RTDIGESTTYPE)iType;
6471 }
6472 }
6473 return RTDIGESTTYPE_INVALID;
6474}
6475
6476
6477static RTEXITCODE HandleCreateSelfSignedRsaCert(int cArgs, char **papszArgs)
6478{
6479 /*
6480 * Parse arguments.
6481 */
6482 static const RTGETOPTDEF s_aOptions[] =
6483 {
6484 { "--digest", 'd', RTGETOPT_REQ_STRING },
6485 { "--bits", 'b', RTGETOPT_REQ_UINT32 },
6486 { "--key-bits", 'b', RTGETOPT_REQ_UINT32 },
6487 { "--days", 'D', RTGETOPT_REQ_UINT32 },
6488 { "--days", 'D', RTGETOPT_REQ_UINT32 },
6489 { "--out-cert", 'c', RTGETOPT_REQ_UINT32 },
6490 { "--out-certificate", 'c', RTGETOPT_REQ_UINT32 },
6491 { "--out-pkey", 'p', RTGETOPT_REQ_UINT32 },
6492 { "--out-private-key", 'p', RTGETOPT_REQ_UINT32 },
6493 { "--secs", 's', RTGETOPT_REQ_UINT32 },
6494 { "--seconds", 's', RTGETOPT_REQ_UINT32 },
6495 };
6496
6497 RTDIGESTTYPE enmDigestType = RTDIGESTTYPE_SHA384;
6498 uint32_t cKeyBits = 4096;
6499 uint32_t cSecsValidFor = 365 * RT_SEC_1DAY;
6500 uint32_t fKeyUsage = 0;
6501 uint32_t fExtKeyUsage = 0;
6502 const char *pszOutCert = NULL;
6503 const char *pszOutPrivKey = NULL;
6504
6505 RTGETOPTSTATE GetState;
6506 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
6507 AssertRCReturn(rc, RTEXITCODE_FAILURE);
6508 RTGETOPTUNION ValueUnion;
6509 int ch;
6510 while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
6511 {
6512 switch (ch)
6513 {
6514 case 'b':
6515 cKeyBits = ValueUnion.u32;
6516 break;
6517
6518 case 'd':
6519 enmDigestType = DigestTypeStringToValue(ValueUnion.psz);
6520 if (enmDigestType == RTDIGESTTYPE_INVALID)
6521 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Unknown digest type: %s", ValueUnion.psz);
6522 break;
6523
6524 case 'D':
6525 cSecsValidFor = ValueUnion.u32 * RT_SEC_1DAY;
6526 if (cSecsValidFor / RT_SEC_1DAY != ValueUnion.u32)
6527 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The --days option value is out of range: %u", ValueUnion.u32);
6528 break;
6529
6530 case 'c':
6531 if (pszOutCert)
6532 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The --out-cert option can only be used once.");
6533 pszOutCert = ValueUnion.psz;
6534 break;
6535
6536 case 'p':
6537 if (pszOutPrivKey)
6538 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The --out-pkey option can only be used once.");
6539 pszOutPrivKey = ValueUnion.psz;
6540 break;
6541
6542 case 's':
6543 cSecsValidFor = ValueUnion.u32;
6544 break;
6545
6546 case VINF_GETOPT_NOT_OPTION:
6547 if (!pszOutCert)
6548 pszOutCert = ValueUnion.psz;
6549 else if (!pszOutPrivKey)
6550 pszOutPrivKey = ValueUnion.psz;
6551 else
6552 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Too many output files specified: %s", ValueUnion.psz);
6553 break;
6554
6555 case 'V': return HandleVersion(cArgs, papszArgs);
6556 case 'h': return HelpCreateSelfSignedRsaCert(g_pStdOut, RTSIGNTOOLHELP_FULL);
6557 default: return RTGetOptPrintError(ch, &ValueUnion);
6558 }
6559 }
6560 if (!pszOutCert)
6561 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No output certificate file name specified.");
6562 if (!pszOutPrivKey)
6563 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No output private key file name specified.");
6564
6565 /*
6566 * Do the work.
6567 */
6568 RTERRINFOSTATIC StaticErrInfo;
6569 rc = RTCrX509Certificate_GenerateSelfSignedRsa(enmDigestType, cKeyBits, cSecsValidFor,
6570 fKeyUsage, fExtKeyUsage, NULL /*pvSubjectTodo*/,
6571 pszOutCert, pszOutPrivKey, RTErrInfoInitStatic(&StaticErrInfo));
6572 if (RT_SUCCESS(rc))
6573 {
6574 /*
6575 * Test load it.
6576 */
6577 RTCRX509CERTIFICATE Certificate;
6578 rc = RTCrX509Certificate_ReadFromFile(&Certificate, pszOutCert, RTCRX509CERT_READ_F_PEM_ONLY,
6579 &g_RTAsn1DefaultAllocator, RTErrInfoInitStatic(&StaticErrInfo));
6580 if (RT_FAILURE(rc))
6581 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error reading the new certificate from %s: %Rrc%#RTeim",
6582 pszOutCert, rc, &StaticErrInfo.Core);
6583 RTCrX509Certificate_Delete(&Certificate);
6584 return RTEXITCODE_SUCCESS;
6585 }
6586
6587 return RTMsgErrorExitFailure("RTCrX509Certificate_GenerateSelfSignedRsa(%d,%u,%u,%s,%s,) failed: %Rrc%#RTeim",
6588 enmDigestType, cKeyBits, cSecsValidFor, pszOutCert, pszOutPrivKey, rc, &StaticErrInfo.Core);
6589}
6590
6591#endif /* !IPRT_IN_BUILD_TOOL */
6592
6593
6594/*
6595 * The 'version' command.
6596 */
6597static RTEXITCODE HelpVersion(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
6598{
6599 RT_NOREF_PV(enmLevel);
6600 RTStrmPrintf(pStrm, "version\n");
6601 return RTEXITCODE_SUCCESS;
6602}
6603
6604static RTEXITCODE HandleVersion(int cArgs, char **papszArgs)
6605{
6606 RT_NOREF_PV(cArgs); RT_NOREF_PV(papszArgs);
6607#ifndef IN_BLD_PROG /* RTBldCfgVersion or RTBldCfgRevision in build time IPRT lib. */
6608 RTPrintf("%s\n", RTBldCfgVersion());
6609 return RTEXITCODE_SUCCESS;
6610#else
6611 return RTEXITCODE_FAILURE;
6612#endif
6613}
6614
6615
6616
6617/**
6618 * Command mapping.
6619 */
6620static struct
6621{
6622 /** The command. */
6623 const char *pszCmd;
6624 /**
6625 * Handle the command.
6626 * @returns Program exit code.
6627 * @param cArgs Number of arguments.
6628 * @param papszArgs The argument vector, starting with the command name.
6629 */
6630 RTEXITCODE (*pfnHandler)(int cArgs, char **papszArgs);
6631 /**
6632 * Produce help.
6633 * @returns RTEXITCODE_SUCCESS to simplify handling '--help' in the handler.
6634 * @param pStrm Where to send help text.
6635 * @param enmLevel The level of the help information.
6636 */
6637 RTEXITCODE (*pfnHelp)(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel);
6638}
6639/** Mapping commands to handler and helper functions. */
6640const g_aCommands[] =
6641{
6642 { "extract-exe-signer-cert", HandleExtractExeSignerCert, HelpExtractExeSignerCert },
6643 { "extract-signer-root", HandleExtractSignerRoot, HelpExtractSignerRoot },
6644 { "extract-timestamp-root", HandleExtractTimestampRoot, HelpExtractTimestampRoot },
6645 { "extract-exe-signature", HandleExtractExeSignature, HelpExtractExeSignature },
6646 { "add-nested-exe-signature", HandleAddNestedExeSignature, HelpAddNestedExeSignature },
6647 { "add-nested-cat-signature", HandleAddNestedCatSignature, HelpAddNestedCatSignature },
6648#ifndef IPRT_SIGNTOOL_NO_SIGNING
6649 { "add-timestamp-exe-signature", HandleAddTimestampExeSignature, HelpAddTimestampExeSignature },
6650 { "sign", HandleSign, HelpSign },
6651#endif
6652#ifndef IPRT_IN_BUILD_TOOL
6653 { "verify-exe", HandleVerifyExe, HelpVerifyExe },
6654#endif
6655 { "show-exe", HandleShowExe, HelpShowExe },
6656 { "show-cat", HandleShowCat, HelpShowCat },
6657 { "hash-exe", HandleHashExe, HelpHashExe },
6658 { "make-tainfo", HandleMakeTaInfo, HelpMakeTaInfo },
6659#ifndef IPRT_IN_BUILD_TOOL
6660 { "create-self-signed-rsa-cert", HandleCreateSelfSignedRsaCert, HelpCreateSelfSignedRsaCert },
6661#endif
6662 { "help", HandleHelp, HelpHelp },
6663 { "--help", HandleHelp, NULL },
6664 { "-h", HandleHelp, NULL },
6665 { "version", HandleVersion, HelpVersion },
6666 { "--version", HandleVersion, NULL },
6667 { "-V", HandleVersion, NULL },
6668};
6669
6670
6671/*
6672 * The 'help' command.
6673 */
6674static RTEXITCODE HelpHelp(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
6675{
6676 RT_NOREF_PV(enmLevel);
6677 RTStrmPrintf(pStrm, "help [cmd-patterns]\n");
6678 return RTEXITCODE_SUCCESS;
6679}
6680
6681static RTEXITCODE HandleHelp(int cArgs, char **papszArgs)
6682{
6683 PRTSTREAM const pStrm = g_pStdOut;
6684 RTSIGNTOOLHELP enmLevel = cArgs <= 1 ? RTSIGNTOOLHELP_USAGE : RTSIGNTOOLHELP_FULL;
6685 uint32_t cShowed = 0;
6686 uint32_t cchWidth;
6687 if (RT_FAILURE(RTStrmQueryTerminalWidth(g_pStdOut, &cchWidth)))
6688 cchWidth = 80;
6689
6690 RTStrmPrintf(pStrm,
6691 "Usage: RTSignTool <command> [command-options]\n"
6692 " or: RTSignTool <-V|--version|version>\n"
6693 " or: RTSignTool <-h|--help|help> [command-pattern [..]]\n"
6694 "\n"
6695 );
6696
6697 if (enmLevel == RTSIGNTOOLHELP_USAGE)
6698 RTStrmPrintf(pStrm, "Syntax summary for the RTSignTool commands:\n");
6699
6700 for (uint32_t iCmd = 0; iCmd < RT_ELEMENTS(g_aCommands); iCmd++)
6701 {
6702 if (g_aCommands[iCmd].pfnHelp)
6703 {
6704 bool fShow = false;
6705 if (cArgs <= 1)
6706 fShow = true;
6707 else
6708 for (int iArg = 1; iArg < cArgs; iArg++)
6709 if (RTStrSimplePatternMultiMatch(papszArgs[iArg], RTSTR_MAX, g_aCommands[iCmd].pszCmd, RTSTR_MAX, NULL))
6710 {
6711 fShow = true;
6712 break;
6713 }
6714 if (fShow)
6715 {
6716 if (enmLevel == RTSIGNTOOLHELP_FULL)
6717 RTPrintf("%.*s\n", RT_MIN(cchWidth, 100),
6718 "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
6719 g_aCommands[iCmd].pfnHelp(pStrm, enmLevel);
6720 cShowed++;
6721 }
6722 }
6723 }
6724 return cShowed ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
6725}
6726
6727
6728
6729int main(int argc, char **argv)
6730{
6731 int rc = RTR3InitExe(argc, &argv, 0);
6732 if (RT_FAILURE(rc))
6733 return RTMsgInitFailure(rc);
6734
6735 /*
6736 * Parse global arguments.
6737 */
6738 int iArg = 1;
6739 /* none presently. */
6740
6741 /*
6742 * Command dispatcher.
6743 */
6744 if (iArg < argc)
6745 {
6746 const char *pszCmd = argv[iArg];
6747 uint32_t i = RT_ELEMENTS(g_aCommands);
6748 while (i-- > 0)
6749 if (!strcmp(g_aCommands[i].pszCmd, pszCmd))
6750 return g_aCommands[i].pfnHandler(argc - iArg, &argv[iArg]);
6751 RTMsgError("Unknown command '%s'.", pszCmd);
6752 }
6753 else
6754 RTMsgError("No command given. (try --help)");
6755
6756 return RTEXITCODE_SYNTAX;
6757}
6758
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette