VirtualBox

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

Last change on this file since 96407 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 255.3 KB
Line 
1/* $Id: RTSignTool.cpp 96407 2022-08-22 17:43:14Z vboxsync $ */
2/** @file
3 * IPRT - Signing Tool.
4 */
5
6/*
7 * Copyright (C) 2006-2022 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#endif
64#include <iprt/crypto/applecodesign.h>
65#include <iprt/crypto/digest.h>
66#include <iprt/crypto/key.h>
67#include <iprt/crypto/x509.h>
68#include <iprt/crypto/pkcs7.h>
69#include <iprt/crypto/store.h>
70#include <iprt/crypto/spc.h>
71#include <iprt/crypto/tsp.h>
72#include <iprt/cpp/ministring.h>
73#ifdef VBOX
74# include <VBox/sup.h> /* Certificates */
75#endif
76#ifdef RT_OS_WINDOWS
77# include <iprt/win/windows.h>
78# include <iprt/win/imagehlp.h>
79# include <wincrypt.h>
80# include <ncrypt.h>
81#endif
82#include "internal/ldr.h" /* for IMAGE_XX_SIGNATURE defines */
83
84
85/*********************************************************************************************************************************
86* Defined Constants And Macros *
87*********************************************************************************************************************************/
88#define OPT_OFF_CERT_FILE 0 /**< signtool /f file */
89#define OPT_OFF_CERT_SHA1 1 /**< signtool /sha1 thumbprint */
90#define OPT_OFF_CERT_SUBJECT 2 /**< signtool /n name */
91#define OPT_OFF_CERT_STORE 3 /**< signtool /s store */
92#define OPT_OFF_CERT_STORE_MACHINE 4 /**< signtool /sm */
93#define OPT_OFF_KEY_FILE 5 /**< no signtool equivalent, other than maybe /f. */
94#define OPT_OFF_KEY_PASSWORD 6 /**< signtool /p pass */
95#define OPT_OFF_KEY_PASSWORD_FILE 7 /**< no signtool equivalent. */
96#define OPT_OFF_KEY_NAME 8 /**< signtool /kc name */
97#define OPT_OFF_KEY_PROVIDER 9 /**< signtool /csp name (CSP = cryptographic service provider) */
98
99#define OPT_CERT_KEY_SWITCH_CASES(a_Instance, a_uBase, a_chOpt, a_ValueUnion, a_rcExit) \
100 case (a_uBase) + OPT_OFF_CERT_FILE: \
101 case (a_uBase) + OPT_OFF_CERT_SHA1: \
102 case (a_uBase) + OPT_OFF_CERT_SUBJECT: \
103 case (a_uBase) + OPT_OFF_CERT_STORE: \
104 case (a_uBase) + OPT_OFF_CERT_STORE_MACHINE: \
105 case (a_uBase) + OPT_OFF_KEY_FILE: \
106 case (a_uBase) + OPT_OFF_KEY_PASSWORD: \
107 case (a_uBase) + OPT_OFF_KEY_PASSWORD_FILE: \
108 case (a_uBase) + OPT_OFF_KEY_NAME: \
109 case (a_uBase) + OPT_OFF_KEY_PROVIDER: \
110 a_rcExit = a_Instance.handleOption((a_chOpt) - (a_uBase), &(a_ValueUnion)); \
111 break
112
113#define OPT_CERT_KEY_GETOPTDEF_ENTRIES(a_szPrefix, a_uBase) \
114 { a_szPrefix "cert-file", (a_uBase) + OPT_OFF_CERT_FILE, RTGETOPT_REQ_STRING }, \
115 { a_szPrefix "cert-sha1", (a_uBase) + OPT_OFF_CERT_SHA1, RTGETOPT_REQ_STRING }, \
116 { a_szPrefix "cert-subject", (a_uBase) + OPT_OFF_CERT_SUBJECT, RTGETOPT_REQ_STRING }, \
117 { a_szPrefix "cert-store", (a_uBase) + OPT_OFF_CERT_STORE, RTGETOPT_REQ_STRING }, \
118 { a_szPrefix "cert-machine-store", (a_uBase) + OPT_OFF_CERT_STORE_MACHINE, RTGETOPT_REQ_NOTHING }, \
119 { a_szPrefix "key-file", (a_uBase) + OPT_OFF_KEY_FILE, RTGETOPT_REQ_STRING }, \
120 { a_szPrefix "key-password", (a_uBase) + OPT_OFF_KEY_PASSWORD, RTGETOPT_REQ_STRING }, \
121 { a_szPrefix "key-password-file", (a_uBase) + OPT_OFF_KEY_PASSWORD_FILE, RTGETOPT_REQ_STRING }, \
122 { a_szPrefix "key-name", (a_uBase) + OPT_OFF_KEY_NAME, RTGETOPT_REQ_STRING }, \
123 { a_szPrefix "key-provider", (a_uBase) + OPT_OFF_KEY_PROVIDER, RTGETOPT_REQ_STRING }
124
125#define OPT_CERT_KEY_GETOPTDEF_COMPAT_ENTRIES(a_uBase) \
126 { "/f", (a_uBase) + OPT_OFF_CERT_FILE, RTGETOPT_REQ_STRING }, \
127 { "/sha1", (a_uBase) + OPT_OFF_CERT_SHA1, RTGETOPT_REQ_STRING }, \
128 { "/n", (a_uBase) + OPT_OFF_CERT_SUBJECT, RTGETOPT_REQ_STRING }, \
129 { "/s", (a_uBase) + OPT_OFF_CERT_STORE, RTGETOPT_REQ_STRING }, \
130 { "/sm", (a_uBase) + OPT_OFF_CERT_STORE_MACHINE, RTGETOPT_REQ_NOTHING }, \
131 { "/p", (a_uBase) + OPT_OFF_KEY_PASSWORD, RTGETOPT_REQ_STRING }, \
132 { "/kc", (a_uBase) + OPT_OFF_KEY_NAME, RTGETOPT_REQ_STRING }, \
133 { "/csp", (a_uBase) + OPT_OFF_KEY_PROVIDER, RTGETOPT_REQ_STRING }
134
135#define OPT_CERT_KEY_SYNOPSIS(a_szPrefix) \
136 "[" a_szPrefix "cert-file <file.pem|file.crt>] " \
137 "[" a_szPrefix "cert-sha1 <fingerprint>] " \
138 "[" a_szPrefix "cert-subject <part-name>] " \
139 "[" a_szPrefix "cert-store <store>] " \
140 "[" a_szPrefix "cert-machine-store] " \
141 "[" a_szPrefix "key-file <file.pem|file.p12>] " \
142 "[" a_szPrefix "key-password <password>] " \
143 "[" a_szPrefix "key-password-file <file>|stdin] " \
144 "[" a_szPrefix "key-name <name>] " \
145 "[" a_szPrefix "key-provider <csp>] "
146
147#define OPT_HASH_PAGES 1040
148#define OPT_NO_HASH_PAGES 1041
149#define OPT_ADD_CERT 1042
150#define OPT_TIMESTAMP_TYPE 1043
151#define OPT_TIMESTAMP_OVERRIDE 1044
152#define OPT_NO_SIGNING_TIME 1045
153#define OPT_FILE_TYPE 1046
154#define OPT_IGNORED 1047
155
156
157/*********************************************************************************************************************************
158* Structures and Typedefs *
159*********************************************************************************************************************************/
160/** Help detail levels. */
161typedef enum RTSIGNTOOLHELP
162{
163 RTSIGNTOOLHELP_USAGE,
164 RTSIGNTOOLHELP_FULL
165} RTSIGNTOOLHELP;
166
167
168/** Filetypes. */
169typedef enum RTSIGNTOOLFILETYPE
170{
171 RTSIGNTOOLFILETYPE_INVALID = 0,
172 RTSIGNTOOLFILETYPE_DETECT,
173 RTSIGNTOOLFILETYPE_EXE,
174 RTSIGNTOOLFILETYPE_CAT,
175 RTSIGNTOOLFILETYPE_UNKNOWN,
176 RTSIGNTOOLFILETYPE_END
177} RTSIGNTOOLFILETYPE;
178
179
180/**
181 * PKCS\#7 signature data.
182 */
183typedef struct SIGNTOOLPKCS7
184{
185 /** The file type. */
186 RTSIGNTOOLFILETYPE enmType;
187 /** The raw signature. */
188 uint8_t *pbBuf;
189 /** Size of the raw signature. */
190 size_t cbBuf;
191 /** The filename. */
192 const char *pszFilename;
193 /** The outer content info wrapper. */
194 RTCRPKCS7CONTENTINFO ContentInfo;
195 /** Pointer to the decoded SignedData inside the ContentInfo member. */
196 PRTCRPKCS7SIGNEDDATA pSignedData;
197
198 /** Newly encoded raw signature.
199 * @sa SignToolPkcs7_Encode() */
200 uint8_t *pbNewBuf;
201 /** Size of newly encoded raw signature. */
202 size_t cbNewBuf;
203
204} SIGNTOOLPKCS7;
205typedef SIGNTOOLPKCS7 *PSIGNTOOLPKCS7;
206
207
208/**
209 * PKCS\#7 signature data for executable.
210 */
211typedef struct SIGNTOOLPKCS7EXE : public SIGNTOOLPKCS7
212{
213 /** The module handle. */
214 RTLDRMOD hLdrMod;
215} SIGNTOOLPKCS7EXE;
216typedef SIGNTOOLPKCS7EXE *PSIGNTOOLPKCS7EXE;
217
218
219/**
220 * Data for the show exe (signature) command.
221 */
222typedef struct SHOWEXEPKCS7 : public SIGNTOOLPKCS7EXE
223{
224 /** The verbosity. */
225 unsigned cVerbosity;
226 /** The prefix buffer. */
227 char szPrefix[256];
228 /** Temporary buffer. */
229 char szTmp[4096];
230} SHOWEXEPKCS7;
231typedef SHOWEXEPKCS7 *PSHOWEXEPKCS7;
232
233
234/*********************************************************************************************************************************
235* Internal Functions *
236*********************************************************************************************************************************/
237static RTEXITCODE HandleHelp(int cArgs, char **papszArgs);
238static RTEXITCODE HelpHelp(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel);
239static RTEXITCODE HandleVersion(int cArgs, char **papszArgs);
240static int HandleShowExeWorkerPkcs7DisplaySignerInfo(PSHOWEXEPKCS7 pThis, size_t offPrefix, PCRTCRPKCS7SIGNERINFO pSignerInfo);
241static int HandleShowExeWorkerPkcs7Display(PSHOWEXEPKCS7 pThis, PRTCRPKCS7SIGNEDDATA pSignedData, size_t offPrefix,
242 PCRTCRPKCS7CONTENTINFO pContentInfo);
243
244
245/*********************************************************************************************************************************
246* Certificate and Private Key Handling (options, ++). *
247*********************************************************************************************************************************/
248#ifdef RT_OS_WINDOWS
249
250/** @todo create a better fake certificate. */
251const unsigned char g_abFakeCertificate[] =
252{
253 0x30, 0x82, 0x03, 0xb2, 0x30, 0x82, 0x02, 0x9a, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x31, /* 0x00000000: 0...0..........1 */
254 0xba, 0xd6, 0xbc, 0x5d, 0x9a, 0xe0, 0xb0, 0x4e, 0xd4, 0xfa, 0xcc, 0xfb, 0x47, 0x00, 0x5c, 0x30, /* 0x00000010: ...]...N....G.\0 */
255 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x71, /* 0x00000020: ...*.H........0q */
256 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x13, 0x54, 0x69, 0x6d, 0x65, 0x73, /* 0x00000030: 1.0...U....Times */
257 0x74, 0x61, 0x6d, 0x70, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x32, 0x31, 0x0c, /* 0x00000040: tamp Signing 21. */
258 0x30, 0x0a, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x03, 0x44, 0x65, 0x76, 0x31, 0x15, 0x30, 0x13, /* 0x00000050: 0...U....Dev1.0. */
259 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0c, 0x54, 0x65, 0x73, 0x74, 0x20, 0x43, 0x6f, 0x6d, 0x70, /* 0x00000060: ..U....Test Comp */
260 0x61, 0x6e, 0x79, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x09, 0x53, 0x74, /* 0x00000070: any1.0...U....St */
261 0x75, 0x74, 0x74, 0x67, 0x61, 0x72, 0x74, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, /* 0x00000080: uttgart1.0...U.. */
262 0x0c, 0x02, 0x42, 0x42, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x44, /* 0x00000090: ..BB1.0...U....D */
263 0x45, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x30, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x31, 0x30, /* 0x000000a0: E0...00010100010 */
264 0x31, 0x5a, 0x17, 0x0d, 0x33, 0x36, 0x31, 0x32, 0x33, 0x31, 0x32, 0x32, 0x35, 0x39, 0x35, 0x39, /* 0x000000b0: 1Z..361231225959 */
265 0x5a, 0x30, 0x71, 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x13, 0x54, 0x69, /* 0x000000c0: Z0q1.0...U....Ti */
266 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x20, /* 0x000000d0: mestamp Signing */
267 0x32, 0x31, 0x0c, 0x30, 0x0a, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x03, 0x44, 0x65, 0x76, 0x31, /* 0x000000e0: 21.0...U....Dev1 */
268 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0c, 0x54, 0x65, 0x73, 0x74, 0x20, 0x43, /* 0x000000f0: .0...U....Test C */
269 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, /* 0x00000100: ompany1.0...U... */
270 0x09, 0x53, 0x74, 0x75, 0x74, 0x74, 0x67, 0x61, 0x72, 0x74, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, /* 0x00000110: .Stuttgart1.0... */
271 0x55, 0x04, 0x08, 0x0c, 0x02, 0x42, 0x42, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, /* 0x00000120: U....BB1.0...U.. */
272 0x13, 0x02, 0x44, 0x45, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, /* 0x00000130: ..DE0.."0...*.H. */
273 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, /* 0x00000140: ............0... */
274 0x02, 0x82, 0x01, 0x01, 0x00, 0xdb, 0x18, 0x63, 0x33, 0xf2, 0x08, 0x90, 0x5a, 0xab, 0xda, 0x88, /* 0x00000150: .......c3...Z... */
275 0x73, 0x86, 0x49, 0xea, 0x8b, 0xaf, 0xcf, 0x67, 0x15, 0xa5, 0x39, 0xe6, 0xa2, 0x94, 0x0c, 0x3f, /* 0x00000160: s.I....g..9....? */
276 0xa1, 0x2e, 0x6c, 0xd2, 0xdf, 0x01, 0x65, 0x6d, 0xed, 0x6c, 0x4c, 0xac, 0xe7, 0x77, 0x7a, 0x45, /* 0x00000170: ..l...em.lL..wzE */
277 0x05, 0x6b, 0x24, 0xf3, 0xaf, 0x45, 0x35, 0x6e, 0x64, 0x0a, 0xac, 0x1d, 0x37, 0xe1, 0x33, 0xa4, /* 0x00000180: .k$..E5nd...7.3. */
278 0x92, 0xec, 0x45, 0xe8, 0x99, 0xc1, 0xde, 0x6f, 0xab, 0x7c, 0xf0, 0xdc, 0xe2, 0xc5, 0x42, 0xa3, /* 0x00000190: ..E....o.|....B. */
279 0xea, 0xf5, 0x8a, 0xf9, 0x0e, 0xe7, 0xb3, 0x35, 0xa2, 0x75, 0x5e, 0x87, 0xd2, 0x2a, 0xd1, 0x27, /* 0x000001a0: .......5.u^..*.' */
280 0xa6, 0x79, 0x9e, 0xfe, 0x90, 0xbf, 0x97, 0xa4, 0xa1, 0xd8, 0xf7, 0xd7, 0x05, 0x59, 0x44, 0x27, /* 0x000001b0: .y...........YD' */
281 0x39, 0x6e, 0x33, 0x01, 0x2e, 0x46, 0x92, 0x47, 0xbe, 0x50, 0x91, 0x26, 0x27, 0xe5, 0x4b, 0x3a, /* 0x000001c0: 9n3..F.G.P.&'.K: */
282 0x76, 0x26, 0x64, 0x92, 0x0c, 0xa0, 0x54, 0x43, 0x6f, 0x56, 0xcc, 0x7b, 0xd0, 0xe3, 0xd8, 0x39, /* 0x000001d0: v&d...TCoV.{...9 */
283 0x5f, 0xb9, 0x41, 0xda, 0x1c, 0x62, 0x88, 0x0c, 0x45, 0x03, 0x63, 0xf8, 0xff, 0xe5, 0x3e, 0x87, /* 0x000001e0: _.A..b..E.c...>. */
284 0x0c, 0x75, 0xc9, 0xdd, 0xa2, 0xc0, 0x1b, 0x63, 0x19, 0xeb, 0x09, 0x9d, 0xa1, 0xbb, 0x0f, 0x63, /* 0x000001f0: .u.....c.......c */
285 0x67, 0x1c, 0xa3, 0xfd, 0x2f, 0xd1, 0x2a, 0xda, 0xd8, 0x93, 0x66, 0x45, 0x54, 0xef, 0x8b, 0x6d, /* 0x00000200: g.....*...fET..m */
286 0x12, 0x15, 0x0f, 0xd4, 0xb5, 0x04, 0x17, 0x30, 0x5b, 0xfa, 0x12, 0x96, 0x48, 0x5b, 0x38, 0x65, /* 0x00000210: .......0[...H[8e */
287 0xfd, 0x8f, 0x0c, 0xa3, 0x11, 0x46, 0x49, 0xe0, 0x62, 0xc3, 0xcc, 0x34, 0xe6, 0xfb, 0xab, 0x51, /* 0x00000220: .....FI.b..4...Q */
288 0xc3, 0xd4, 0x0b, 0xdc, 0x39, 0x93, 0x87, 0x90, 0x10, 0x9f, 0xce, 0x43, 0x27, 0x31, 0xd5, 0x4e, /* 0x00000230: ....9......C'1.N */
289 0x52, 0x60, 0xf1, 0x93, 0xd5, 0x06, 0xc4, 0x4e, 0x65, 0xb6, 0x35, 0x4a, 0x64, 0x15, 0xf8, 0xaf, /* 0x00000240: R`.....Ne.5Jd... */
290 0x71, 0xb2, 0x42, 0x50, 0x89, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x46, 0x30, 0x44, 0x30, 0x0e, /* 0x00000250: q.BP.......F0D0. */
291 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x07, 0x80, 0x30, 0x13, /* 0x00000260: ..U...........0. */
292 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x0c, 0x30, 0x0a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, /* 0x00000270: ..U.%..0...+.... */
293 0x07, 0x03, 0x08, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x52, 0x9d, /* 0x00000280: ...0...U......R. */
294 0x4d, 0xcd, 0x41, 0xe1, 0xd2, 0x68, 0x22, 0xd3, 0x10, 0x33, 0x01, 0xca, 0xff, 0x00, 0x1d, 0x27, /* 0x00000290: M.A..h"..3.....' */
295 0xa4, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, /* 0x000002a0: ..0...*.H....... */
296 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0xc5, 0x5a, 0x51, 0x83, 0x68, 0x3f, 0x06, 0x39, 0x79, 0x13, /* 0x000002b0: .......ZQ.h?.9y. */
297 0xa6, 0xf0, 0x1a, 0xf9, 0x29, 0x16, 0x2d, 0xa2, 0x07, 0xaa, 0x9b, 0xc3, 0x13, 0x88, 0x39, 0x69, /* 0x000002c0: ....).-.......9i */
298 0xba, 0xf7, 0x0d, 0xfb, 0xc0, 0x6e, 0x3a, 0x0b, 0x49, 0x10, 0xd1, 0xbe, 0x36, 0x91, 0x3f, 0x9d, /* 0x000002d0: .....n:.I...6.?. */
299 0xa1, 0xe8, 0xc4, 0x91, 0xf9, 0x02, 0xe1, 0xf1, 0x01, 0x15, 0x09, 0xb7, 0xa1, 0xf1, 0xec, 0x43, /* 0x000002e0: ...............C */
300 0x0d, 0x73, 0xd1, 0x31, 0x02, 0x4a, 0xce, 0x21, 0xf2, 0xa7, 0x99, 0x7c, 0xee, 0x85, 0x54, 0xc0, /* 0x000002f0: .s.1.J.!...|..T. */
301 0x55, 0x9b, 0x19, 0x37, 0xe8, 0xcf, 0x94, 0x41, 0x10, 0x6e, 0x67, 0xdd, 0x86, 0xaf, 0xb7, 0xfe, /* 0x00000300: U..7...A.ng..... */
302 0x50, 0x05, 0xf6, 0xfb, 0x0a, 0xdf, 0x88, 0xb5, 0x59, 0x69, 0x98, 0x27, 0xf8, 0x81, 0x6a, 0x4a, /* 0x00000310: P.......Yi.'..jJ */
303 0x7c, 0xf3, 0x63, 0xa9, 0x41, 0x78, 0x76, 0x12, 0xdb, 0x0e, 0x94, 0x0a, 0xdb, 0x1d, 0x3c, 0x87, /* 0x00000320: |.c.Axv.......<. */
304 0x35, 0xca, 0x28, 0xeb, 0xb0, 0x62, 0x27, 0x69, 0xe2, 0xf3, 0x84, 0x48, 0xa2, 0x2d, 0xd7, 0x0e, /* 0x00000330: 5.(..b'i...H.-.. */
305 0x4b, 0x6d, 0x39, 0xa7, 0x3e, 0x04, 0x94, 0x8e, 0xb6, 0x4b, 0x91, 0x01, 0x68, 0xf9, 0xd2, 0x75, /* 0x00000340: Km9.>....K..h..u */
306 0x1b, 0xac, 0x42, 0x3b, 0x85, 0xfc, 0x5b, 0x48, 0x3a, 0x13, 0xe7, 0x1c, 0x17, 0xcd, 0x84, 0x89, /* 0x00000350: ..B;..[H:....... */
307 0x9e, 0x5f, 0xe3, 0x77, 0xc0, 0xae, 0x34, 0xc3, 0x87, 0x76, 0x4a, 0x23, 0x30, 0xa0, 0xe1, 0x45, /* 0x00000360: ._.w..4..vJ#0..E */
308 0x94, 0x2a, 0x5b, 0x6b, 0x5a, 0xf0, 0x1a, 0x7e, 0xa6, 0xc4, 0xed, 0xe4, 0xac, 0x5d, 0xdf, 0x87, /* 0x00000370: .*[kZ..~.....].. */
309 0x8f, 0xc5, 0xb4, 0x8c, 0xbc, 0x70, 0xc1, 0xf7, 0xb2, 0x72, 0xbd, 0x73, 0xc9, 0x4e, 0xed, 0x8d, /* 0x00000380: .....p...r.s.N.. */
310 0x29, 0x33, 0xe9, 0x14, 0xc1, 0x5e, 0xff, 0x39, 0xa8, 0xe7, 0x9a, 0x3b, 0x7a, 0x3c, 0xce, 0x5d, /* 0x00000390: )3...^.9...;z<.] */
311 0x0f, 0x3c, 0x82, 0x90, 0xff, 0x81, 0x82, 0x00, 0x82, 0x5f, 0xba, 0x08, 0x79, 0xb1, 0x97, 0xc3, /* 0x000003a0: .<......._..y... */
312 0x09, 0x75, 0xc0, 0x04, 0x9b, 0x67, /* 0x000003b0: .u...g */
313};
314
315const unsigned char g_abFakeRsaKey[] =
316{
317 0x30, 0x82, 0x04, 0xa4, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00, 0xdb, 0x18, 0x63, 0x33, /* 0x00000000: 0.............c3 */
318 0xf2, 0x08, 0x90, 0x5a, 0xab, 0xda, 0x88, 0x73, 0x86, 0x49, 0xea, 0x8b, 0xaf, 0xcf, 0x67, 0x15, /* 0x00000010: ...Z...s.I....g. */
319 0xa5, 0x39, 0xe6, 0xa2, 0x94, 0x0c, 0x3f, 0xa1, 0x2e, 0x6c, 0xd2, 0xdf, 0x01, 0x65, 0x6d, 0xed, /* 0x00000020: .9....?..l...em. */
320 0x6c, 0x4c, 0xac, 0xe7, 0x77, 0x7a, 0x45, 0x05, 0x6b, 0x24, 0xf3, 0xaf, 0x45, 0x35, 0x6e, 0x64, /* 0x00000030: lL..wzE.k$..E5nd */
321 0x0a, 0xac, 0x1d, 0x37, 0xe1, 0x33, 0xa4, 0x92, 0xec, 0x45, 0xe8, 0x99, 0xc1, 0xde, 0x6f, 0xab, /* 0x00000040: ...7.3...E....o. */
322 0x7c, 0xf0, 0xdc, 0xe2, 0xc5, 0x42, 0xa3, 0xea, 0xf5, 0x8a, 0xf9, 0x0e, 0xe7, 0xb3, 0x35, 0xa2, /* 0x00000050: |....B........5. */
323 0x75, 0x5e, 0x87, 0xd2, 0x2a, 0xd1, 0x27, 0xa6, 0x79, 0x9e, 0xfe, 0x90, 0xbf, 0x97, 0xa4, 0xa1, /* 0x00000060: u^..*.'.y....... */
324 0xd8, 0xf7, 0xd7, 0x05, 0x59, 0x44, 0x27, 0x39, 0x6e, 0x33, 0x01, 0x2e, 0x46, 0x92, 0x47, 0xbe, /* 0x00000070: ....YD'9n3..F.G. */
325 0x50, 0x91, 0x26, 0x27, 0xe5, 0x4b, 0x3a, 0x76, 0x26, 0x64, 0x92, 0x0c, 0xa0, 0x54, 0x43, 0x6f, /* 0x00000080: P.&'.K:v&d...TCo */
326 0x56, 0xcc, 0x7b, 0xd0, 0xe3, 0xd8, 0x39, 0x5f, 0xb9, 0x41, 0xda, 0x1c, 0x62, 0x88, 0x0c, 0x45, /* 0x00000090: V.{...9_.A..b..E */
327 0x03, 0x63, 0xf8, 0xff, 0xe5, 0x3e, 0x87, 0x0c, 0x75, 0xc9, 0xdd, 0xa2, 0xc0, 0x1b, 0x63, 0x19, /* 0x000000a0: .c...>..u.....c. */
328 0xeb, 0x09, 0x9d, 0xa1, 0xbb, 0x0f, 0x63, 0x67, 0x1c, 0xa3, 0xfd, 0x2f, 0xd1, 0x2a, 0xda, 0xd8, /* 0x000000b0: ......cg.....*.. */
329 0x93, 0x66, 0x45, 0x54, 0xef, 0x8b, 0x6d, 0x12, 0x15, 0x0f, 0xd4, 0xb5, 0x04, 0x17, 0x30, 0x5b, /* 0x000000c0: .fET..m.......0[ */
330 0xfa, 0x12, 0x96, 0x48, 0x5b, 0x38, 0x65, 0xfd, 0x8f, 0x0c, 0xa3, 0x11, 0x46, 0x49, 0xe0, 0x62, /* 0x000000d0: ...H[8e.....FI.b */
331 0xc3, 0xcc, 0x34, 0xe6, 0xfb, 0xab, 0x51, 0xc3, 0xd4, 0x0b, 0xdc, 0x39, 0x93, 0x87, 0x90, 0x10, /* 0x000000e0: ..4...Q....9.... */
332 0x9f, 0xce, 0x43, 0x27, 0x31, 0xd5, 0x4e, 0x52, 0x60, 0xf1, 0x93, 0xd5, 0x06, 0xc4, 0x4e, 0x65, /* 0x000000f0: ..C'1.NR`.....Ne */
333 0xb6, 0x35, 0x4a, 0x64, 0x15, 0xf8, 0xaf, 0x71, 0xb2, 0x42, 0x50, 0x89, 0x02, 0x03, 0x01, 0x00, /* 0x00000100: .5Jd...q.BP..... */
334 0x01, 0x02, 0x82, 0x01, 0x01, 0x00, 0xd0, 0x5e, 0x09, 0x3a, 0xc5, 0xdc, 0xcf, 0x2c, 0xec, 0x74, /* 0x00000110: .......^.:...,.t */
335 0x11, 0x81, 0x8d, 0x1d, 0x8f, 0x2a, 0xfa, 0x31, 0x4d, 0xe0, 0x90, 0x1a, 0xd8, 0xf5, 0x95, 0xc7, /* 0x00000120: .....*.1M....... */
336 0x70, 0x5c, 0x62, 0x42, 0xac, 0xe9, 0xd9, 0xf2, 0x14, 0xf1, 0xd0, 0x25, 0xbb, 0xeb, 0x06, 0xfe, /* 0x00000130: p\bB.......%.... */
337 0x09, 0xd6, 0x75, 0x67, 0xd7, 0x39, 0xc1, 0xa0, 0x67, 0x34, 0x4d, 0xd2, 0x12, 0x97, 0xaa, 0x5d, /* 0x00000140: ..ug.9..g4M....] */
338 0xeb, 0x0e, 0xb0, 0x16, 0x6c, 0x78, 0x8e, 0xa0, 0x75, 0xa3, 0xaa, 0x57, 0x88, 0x3b, 0x43, 0x4f, /* 0x00000150: ....lx..u..W.;CO */
339 0x75, 0x85, 0x67, 0xb0, 0x9b, 0xdd, 0x49, 0x0e, 0x6e, 0xdb, 0xea, 0xb3, 0xd4, 0x88, 0x54, 0xa0, /* 0x00000160: u.g...I.n.....T. */
340 0x46, 0x0d, 0x55, 0x6d, 0x98, 0xbd, 0x20, 0xf9, 0x9f, 0x61, 0x2d, 0x6f, 0xc7, 0xd7, 0x16, 0x66, /* 0x00000170: F.Um.. ..a-o...f */
341 0x72, 0xc7, 0x73, 0xbe, 0x9e, 0x48, 0xdc, 0x65, 0x12, 0x46, 0x35, 0x69, 0x55, 0xd8, 0x6b, 0x81, /* 0x00000180: r.s..H.e.F5iU.k. */
342 0x78, 0x40, 0x15, 0x93, 0x60, 0x31, 0x4e, 0x87, 0x15, 0x2a, 0x74, 0x74, 0x7b, 0xa0, 0x1f, 0x59, /* 0x00000190: x@..`1N..*tt{..Y */
343 0x8d, 0xc8, 0x3f, 0xdd, 0xf0, 0x13, 0x88, 0x2a, 0x4a, 0xf2, 0xf5, 0xf1, 0x9e, 0xf3, 0x2d, 0x9c, /* 0x000001a0: ..?....*J.....-. */
344 0x8e, 0xbc, 0xb1, 0x21, 0x45, 0xc7, 0x44, 0x0c, 0x6a, 0xfe, 0x4c, 0x20, 0xdc, 0x73, 0xda, 0x62, /* 0x000001b0: ...!E.D.j.L .s.b */
345 0x21, 0xcb, 0xdf, 0x06, 0xfc, 0x90, 0xc2, 0xbd, 0xd6, 0xde, 0xfb, 0xf6, 0x08, 0x69, 0x5d, 0xea, /* 0x000001c0: !............i]. */
346 0xb3, 0x7f, 0x93, 0x61, 0xf2, 0xc1, 0xd0, 0x61, 0x4f, 0xd5, 0x5b, 0x63, 0xba, 0xb0, 0x3b, 0x07, /* 0x000001d0: ...a...aO.[c..;. */
347 0x7a, 0x55, 0xcd, 0xa1, 0xae, 0x8a, 0x92, 0x21, 0xcc, 0x2f, 0x5b, 0xf8, 0x40, 0x6a, 0xcd, 0xd5, /* 0x000001e0: zU.....!..[.@j.. */
348 0x5f, 0x15, 0xf4, 0xb6, 0xbd, 0xe5, 0x91, 0xb9, 0xa8, 0xcc, 0x2a, 0xa8, 0xa6, 0x67, 0x57, 0x2b, /* 0x000001f0: _.........*..gW+ */
349 0x4b, 0xe9, 0x88, 0xe0, 0xbb, 0x58, 0xac, 0x69, 0x5f, 0x3c, 0x76, 0x28, 0xa6, 0x9d, 0xbc, 0x71, /* 0x00000200: K....X.i_<v(...q */
350 0x7f, 0xcb, 0x0c, 0xc0, 0xbd, 0x61, 0x02, 0x81, 0x81, 0x00, 0xfc, 0x62, 0x79, 0x5b, 0xac, 0xf6, /* 0x00000210: .....a.....by[.. */
351 0x9b, 0x8c, 0xaa, 0x76, 0x2a, 0x30, 0x0e, 0xcf, 0x6b, 0x88, 0x72, 0x54, 0x8c, 0xdf, 0xf3, 0x9d, /* 0x00000220: ...v*0..k.rT.... */
352 0x84, 0xbb, 0xe7, 0x9d, 0xd4, 0x04, 0x29, 0x3c, 0xb5, 0x9d, 0x60, 0x9a, 0xcc, 0x12, 0xf3, 0xfa, /* 0x00000230: ......)<..`..... */
353 0x64, 0x30, 0x23, 0x47, 0xc6, 0xa4, 0x8b, 0x6c, 0x73, 0x6c, 0x6b, 0x78, 0x82, 0xec, 0x05, 0x19, /* 0x00000240: d0#G...lslkx.... */
354 0xde, 0xdd, 0xde, 0x52, 0xc5, 0x20, 0xd1, 0x11, 0x58, 0x19, 0x07, 0x5a, 0x90, 0xdd, 0x22, 0x91, /* 0x00000250: ...R. ..X..Z..". */
355 0x89, 0x22, 0x3f, 0x12, 0x54, 0x1a, 0xb8, 0x79, 0xd8, 0x6c, 0xbc, 0xf5, 0x0d, 0xc7, 0x73, 0x5c, /* 0x00000260: ."?.T..y.l....s\ */
356 0xed, 0xba, 0x40, 0x2b, 0x72, 0x34, 0x34, 0x97, 0xfa, 0x49, 0xf6, 0x43, 0x7c, 0xbc, 0x61, 0x30, /* 0x00000270: ..@+r44..I.C|.a0 */
357 0x54, 0x22, 0x21, 0x5f, 0x77, 0x68, 0x6b, 0x83, 0x95, 0xc6, 0x8d, 0xb8, 0x25, 0x3a, 0xd3, 0xb2, /* 0x00000280: T"!_whk.....%:.. */
358 0xbe, 0x29, 0x94, 0x01, 0x15, 0xf0, 0x36, 0x9d, 0x3e, 0xff, 0x02, 0x81, 0x81, 0x00, 0xde, 0x3b, /* 0x00000290: .)....6.>......; */
359 0xd6, 0x4b, 0x38, 0x69, 0x9b, 0x71, 0x29, 0x89, 0xd4, 0x6d, 0x8c, 0x41, 0xee, 0xe2, 0x4d, 0xfc, /* 0x000002a0: .K8i.q)..m.A..M. */
360 0xf0, 0x9a, 0x73, 0xf1, 0x15, 0x94, 0xac, 0x1b, 0x68, 0x5f, 0x79, 0x15, 0x3a, 0x41, 0x55, 0x09, /* 0x000002b0: ..s.....h_y.:AU. */
361 0xc7, 0x1e, 0xec, 0x27, 0x67, 0xe2, 0xdc, 0x54, 0xa8, 0x09, 0xe6, 0x46, 0x92, 0x92, 0x03, 0x8d, /* 0x000002c0: ...'g..T...F.... */
362 0xe5, 0x96, 0xfb, 0x1a, 0xdd, 0x59, 0x6f, 0x92, 0xf1, 0xf6, 0x8f, 0x76, 0xb0, 0xc5, 0xe6, 0xd7, /* 0x000002d0: .....Yo....v.... */
363 0x1b, 0x25, 0xaf, 0x04, 0x9f, 0xd8, 0x71, 0x27, 0x97, 0x99, 0x23, 0x09, 0x7d, 0xef, 0x06, 0x13, /* 0x000002e0: .%....q'..#.}... */
364 0xab, 0xdc, 0xa2, 0xd8, 0x5f, 0xc5, 0xec, 0xf3, 0x62, 0x20, 0x72, 0x7b, 0xa8, 0xc7, 0x09, 0x24, /* 0x000002f0: ...._...b r{...$ */
365 0xaf, 0x72, 0xc9, 0xea, 0xb8, 0x2d, 0xda, 0x00, 0xc8, 0xfe, 0xb4, 0x9f, 0x9f, 0xc7, 0xa9, 0xf7, /* 0x00000300: .r...-.......... */
366 0x1d, 0xce, 0xb1, 0xdb, 0xc5, 0x8a, 0x4e, 0xe8, 0x88, 0x77, 0x68, 0xdd, 0xf8, 0x77, 0x02, 0x81, /* 0x00000310: ......N..wh..w.. */
367 0x80, 0x5b, 0xa5, 0x8e, 0x98, 0x01, 0xa8, 0xd3, 0x37, 0x33, 0x37, 0x11, 0x7e, 0xbe, 0x02, 0x07, /* 0x00000320: .[......737.~... */
368 0xf4, 0x56, 0x3f, 0xe9, 0x9f, 0xf1, 0x20, 0xc3, 0xf0, 0x4f, 0xdc, 0xf9, 0xfe, 0x40, 0xd3, 0x30, /* 0x00000330: .V?... ..O...@.0 */
369 0xc7, 0xe3, 0x2a, 0x92, 0xec, 0x56, 0xf8, 0x17, 0xa5, 0x7b, 0x4a, 0x37, 0x11, 0xcd, 0x27, 0x26, /* 0x00000340: ..*..V...{J7..'& */
370 0x8a, 0xba, 0x43, 0xda, 0x96, 0xc6, 0x0b, 0x6c, 0xe8, 0x78, 0x30, 0xea, 0x30, 0x4e, 0x7a, 0xd3, /* 0x00000350: ..C....l.x0.0Nz. */
371 0xd8, 0xd2, 0xd8, 0xca, 0x3d, 0xe2, 0xad, 0xa2, 0x74, 0x73, 0x1e, 0xbe, 0xb7, 0xad, 0x41, 0x61, /* 0x00000360: ....=...ts....Aa */
372 0x9b, 0xaa, 0xc9, 0xf9, 0xa4, 0xf1, 0x79, 0x4f, 0x42, 0x10, 0xc7, 0x36, 0x03, 0x4b, 0x0d, 0xdc, /* 0x00000370: ......yOB..6.K.. */
373 0xef, 0x3a, 0xa3, 0xab, 0x09, 0xe4, 0xe8, 0xdd, 0xc4, 0x3f, 0x06, 0x21, 0xa0, 0x23, 0x5a, 0x76, /* 0x00000380: .:.......?.!.#Zv */
374 0xea, 0xd0, 0xcf, 0x8b, 0x85, 0x5f, 0x16, 0x4b, 0x03, 0x62, 0x21, 0x3a, 0xcc, 0x2d, 0xa8, 0xd0, /* 0x00000390: ....._.K.b!:.-.. */
375 0x15, 0x02, 0x81, 0x80, 0x51, 0xf6, 0x89, 0xbb, 0xa6, 0x6b, 0xb4, 0xcb, 0xd0, 0xc1, 0x27, 0xda, /* 0x000003a0: ....Q....k....'. */
376 0xdb, 0x6e, 0xf9, 0xd6, 0xf7, 0x62, 0x81, 0xae, 0xc5, 0x72, 0x36, 0x3e, 0x66, 0x17, 0x99, 0xb0, /* 0x000003b0: .n...b...r6>f... */
377 0x14, 0xad, 0x52, 0x96, 0x03, 0xf2, 0x1e, 0x41, 0x76, 0x61, 0xb6, 0x3c, 0x02, 0x7d, 0x2a, 0x98, /* 0x000003c0: ..R....Ava.<.}*. */
378 0xb4, 0x18, 0x75, 0x38, 0x6b, 0x1d, 0x2b, 0x7f, 0x3a, 0xcf, 0x96, 0xb1, 0xc4, 0xa7, 0xd2, 0x9b, /* 0x000003d0: ..u8k.+.:....... */
379 0xd8, 0x1f, 0xb3, 0x64, 0xda, 0x15, 0x9d, 0xca, 0x91, 0x39, 0x48, 0x67, 0x00, 0x9c, 0xd4, 0x99, /* 0x000003e0: ...d.....9Hg.... */
380 0xc3, 0x45, 0x5d, 0xf0, 0x09, 0x32, 0xba, 0x21, 0x1e, 0xe2, 0x64, 0xb8, 0x50, 0x03, 0x17, 0xbe, /* 0x000003f0: .E]..2.!..d.P... */
381 0xd5, 0xda, 0x6b, 0xce, 0x34, 0xbe, 0x16, 0x03, 0x65, 0x1b, 0x2f, 0xa0, 0xa1, 0x95, 0xc6, 0x8b, /* 0x00000400: ..k.4...e....... */
382 0xc2, 0x3c, 0x59, 0x26, 0xbf, 0xb6, 0x07, 0x85, 0x53, 0x2d, 0xb6, 0x36, 0xa3, 0x91, 0xb9, 0xbb, /* 0x00000410: .<Y&....S-.6.... */
383 0x28, 0xaf, 0x2d, 0x53, 0x02, 0x81, 0x81, 0x00, 0xd7, 0xbc, 0x70, 0xd8, 0x18, 0x4f, 0x65, 0x8c, /* 0x00000420: (.-S......p..Oe. */
384 0x68, 0xca, 0x35, 0x77, 0x43, 0x50, 0x9b, 0xa1, 0xa3, 0x9a, 0x0e, 0x2d, 0x7b, 0x38, 0xf8, 0xba, /* 0x00000430: h.5wCP.....-{8.. */
385 0x14, 0x91, 0x3b, 0xc3, 0x3b, 0x1b, 0xa0, 0x6d, 0x45, 0xe4, 0xa8, 0x28, 0x97, 0xf6, 0x89, 0x13, /* 0x00000440: ..;.;..mE..(.... */
386 0xb6, 0x16, 0x6d, 0x65, 0x47, 0x8c, 0xa6, 0x21, 0xf8, 0x6a, 0xce, 0x4e, 0x44, 0x5e, 0x81, 0x47, /* 0x00000450: ..meG..!.j.ND^.G */
387 0xd9, 0xad, 0x8a, 0xb9, 0xd9, 0xe9, 0x3e, 0x33, 0x1e, 0x5f, 0xe9, 0xe9, 0xa7, 0xea, 0x60, 0x75, /* 0x00000460: ......>3._....`u */
388 0x02, 0x57, 0x71, 0xb5, 0xed, 0x47, 0x77, 0xda, 0x1a, 0x40, 0x38, 0xab, 0x82, 0xd2, 0x0d, 0xf5, /* 0x00000470: .Wq..Gw..@8..... */
389 0x0e, 0x8e, 0xa9, 0x24, 0xdc, 0x30, 0xc9, 0x98, 0xa2, 0x05, 0xcd, 0xca, 0x01, 0xcf, 0xae, 0x1d, /* 0x00000480: ...$.0.......... */
390 0xe9, 0x02, 0x47, 0x0e, 0x46, 0x1d, 0x52, 0x02, 0x9a, 0x99, 0x22, 0x23, 0x7f, 0xf8, 0x9e, 0xc2, /* 0x00000490: ..G.F.R..."#.... */
391 0x16, 0x86, 0xca, 0xa0, 0xa7, 0x34, 0xfb, 0xbc, /* 0x000004a0: .....4.. */
392};
393
394#endif /* RT_OS_WINDOWS */
395
396
397/**
398 * Certificate w/ public key + private key pair for signing.
399 */
400class SignToolKeyPair
401{
402protected:
403 /* Context: */
404 const char *m_pszWhat;
405 bool m_fMandatory;
406
407 /* Parameters kept till finalizing parsing: */
408 const char *m_pszCertFile;
409 const char *m_pszCertSha1;
410 uint8_t m_abCertSha1[RTSHA1_HASH_SIZE];
411 const char *m_pszCertSubject;
412 const char *m_pszCertStore;
413 bool m_fMachineStore; /**< false = personal store */
414
415 const char *m_pszKeyFile;
416 const char *m_pszKeyPassword;
417 const char *m_pszKeyName;
418 const char *m_pszKeyProvider;
419
420 /** String buffer for m_pszKeyPassword when read from file. */
421 RTCString m_strPassword;
422 /** Storage for pCertificate when it's loaded from a file. */
423 RTCRX509CERTIFICATE m_DecodedCert;
424#ifdef RT_OS_WINDOWS
425 /** For the fake certificate */
426 RTCRX509CERTIFICATE m_DecodedFakeCert;
427 /** The certificate store. */
428 HCERTSTORE m_hStore;
429 /** The windows certificate context. */
430 PCCERT_CONTEXT m_pCertCtx;
431 /** Whether hNCryptPrivateKey/hLegacyPrivateKey needs freeing or not. */
432 BOOL m_fFreePrivateHandle;
433#endif
434
435 /** Set if already finalized. */
436 bool m_fFinalized;
437
438 /** Store containing the intermediate certificates available to the host.
439 * */
440 static RTCRSTORE s_hStoreIntermediate;
441 /** Instance counter for helping cleaning up m_hStoreIntermediate. */
442 static uint32_t s_cInstances;
443
444public: /* used to be a struct, thus not prefix either. */
445 /* Result: */
446 PCRTCRX509CERTIFICATE pCertificate;
447 RTCRKEY hPrivateKey;
448#ifdef RT_OS_WINDOWS
449 PCRTCRX509CERTIFICATE pCertificateReal;
450 NCRYPT_KEY_HANDLE hNCryptPrivateKey;
451 HCRYPTPROV hLegacyPrivateKey;
452#endif
453
454public:
455 SignToolKeyPair(const char *a_pszWhat, bool a_fMandatory = false)
456 : m_pszWhat(a_pszWhat)
457 , m_fMandatory(a_fMandatory)
458 , m_pszCertFile(NULL)
459 , m_pszCertSha1(NULL)
460 , m_pszCertSubject(NULL)
461 , m_pszCertStore("MY")
462 , m_fMachineStore(false)
463 , m_pszKeyFile(NULL)
464 , m_pszKeyPassword(NULL)
465 , m_pszKeyName(NULL)
466 , m_pszKeyProvider(NULL)
467#ifdef RT_OS_WINDOWS
468 , m_hStore(NULL)
469 , m_pCertCtx(NULL)
470 , m_fFreePrivateHandle(FALSE)
471#endif
472 , m_fFinalized(false)
473 , pCertificate(NULL)
474 , hPrivateKey(NIL_RTCRKEY)
475#ifdef RT_OS_WINDOWS
476 , pCertificateReal(NULL)
477 , hNCryptPrivateKey(0)
478 , hLegacyPrivateKey(0)
479#endif
480 {
481 RT_ZERO(m_DecodedCert);
482#ifdef RT_OS_WINDOWS
483 RT_ZERO(m_DecodedFakeCert);
484#endif
485 s_cInstances++;
486 }
487
488 ~SignToolKeyPair()
489 {
490 if (hPrivateKey != NIL_RTCRKEY)
491 {
492 RTCrKeyRelease(hPrivateKey);
493 hPrivateKey = NIL_RTCRKEY;
494 }
495 if (pCertificate == &m_DecodedCert)
496 {
497 RTCrX509Certificate_Delete(&m_DecodedCert);
498 pCertificate = NULL;
499 }
500#ifdef RT_OS_WINDOWS
501 if (pCertificate == &m_DecodedFakeCert)
502 {
503 RTCrX509Certificate_Delete(&m_DecodedFakeCert);
504 RTCrX509Certificate_Delete(&m_DecodedCert);
505 pCertificate = NULL;
506 pCertificateReal = NULL;
507 }
508#endif
509#ifdef RT_OS_WINDOWS
510 if (m_pCertCtx != NULL)
511 {
512 CertFreeCertificateContext(m_pCertCtx);
513 m_pCertCtx = NULL;
514 }
515 if (m_hStore != NULL)
516 {
517 CertCloseStore(m_hStore, 0);
518 m_hStore = NULL;
519 }
520#endif
521 s_cInstances--;
522 if (s_cInstances == 0)
523 {
524 RTCrStoreRelease(s_hStoreIntermediate);
525 s_hStoreIntermediate = NIL_RTCRSTORE;
526 }
527 }
528
529 bool isComplete(void) const
530 {
531 return pCertificate && hPrivateKey != NIL_RTCRKEY;
532 }
533
534 bool isNull(void) const
535 {
536 return pCertificate == NULL && hPrivateKey == NIL_RTCRKEY;
537 }
538
539 RTEXITCODE handleOption(unsigned offOpt, PRTGETOPTUNION pValueUnion)
540 {
541 AssertReturn(!m_fFinalized, RTMsgErrorExitFailure("Cannot handle options after finalizeOptions was called!"));
542 switch (offOpt)
543 {
544 case OPT_OFF_CERT_FILE:
545 m_pszCertFile = pValueUnion->psz;
546 m_pszCertSha1 = NULL;
547 m_pszCertSubject = NULL;
548 break;
549 case OPT_OFF_CERT_SHA1:
550 {
551 /* Crude normalization of input separators to colons, since it's likely
552 to use spaces and our conversion function only does colons or nothing. */
553 char szDigest[RTSHA1_DIGEST_LEN * 3 + 1];
554 int rc = RTStrCopy(szDigest, sizeof(szDigest), pValueUnion->psz);
555 if (RT_SUCCESS(rc))
556 {
557 char *pszDigest = RTStrStrip(szDigest);
558 size_t offDst = 0;
559 size_t offSrc = 0;
560 char ch;
561 while ((ch = pszDigest[offSrc++]) != '\0')
562 {
563 if (ch == ' ' || ch == '\t' || ch == ':')
564 {
565 while ((ch = pszDigest[offSrc]) == ' ' || ch == '\t' || ch == ':')
566 offSrc++;
567 ch = ch ? ':' : '\0';
568 }
569 pszDigest[offDst++] = ch;
570 }
571 pszDigest[offDst] = '\0';
572
573 /** @todo add a more relaxed input mode to RTStrConvertHexBytes that can deal
574 * with spaces as well as multi-byte cluster of inputs. */
575 rc = RTStrConvertHexBytes(pszDigest, m_abCertSha1, RTSHA1_HASH_SIZE, RTSTRCONVERTHEXBYTES_F_SEP_COLON);
576 if (RT_SUCCESS(rc))
577 {
578 m_pszCertFile = NULL;
579 m_pszCertSha1 = pValueUnion->psz;
580 m_pszCertSubject = NULL;
581 break;
582 }
583 }
584 return RTMsgErrorExitFailure("malformed SHA-1 certificate fingerprint (%Rrc): %s", rc, pValueUnion->psz);
585 }
586 case OPT_OFF_CERT_SUBJECT:
587 m_pszCertFile = NULL;
588 m_pszCertSha1 = NULL;
589 m_pszCertSubject = pValueUnion->psz;
590 break;
591 case OPT_OFF_CERT_STORE:
592 m_pszCertStore = pValueUnion->psz;
593 break;
594 case OPT_OFF_CERT_STORE_MACHINE:
595 m_fMachineStore = true;
596 break;
597
598 case OPT_OFF_KEY_FILE:
599 m_pszKeyFile = pValueUnion->psz;
600 m_pszKeyName = NULL;
601 break;
602 case OPT_OFF_KEY_NAME:
603 m_pszKeyFile = NULL;
604 m_pszKeyName = pValueUnion->psz;
605 break;
606 case OPT_OFF_KEY_PROVIDER:
607 m_pszKeyProvider = pValueUnion->psz;
608 break;
609 case OPT_OFF_KEY_PASSWORD:
610 m_pszKeyPassword = pValueUnion->psz;
611 break;
612 case OPT_OFF_KEY_PASSWORD_FILE:
613 {
614 m_pszKeyPassword = NULL;
615
616 size_t const cchMax = 512;
617 int rc = m_strPassword.reserveNoThrow(cchMax + 1);
618 if (RT_FAILURE(rc))
619 return RTMsgErrorExitFailure("out of memory");
620
621 PRTSTREAM pStrm = g_pStdIn;
622 bool const fClose = strcmp(pValueUnion->psz, "stdin") != 0;
623 if (fClose)
624 {
625 rc = RTStrmOpen(pValueUnion->psz, "r", &pStrm);
626 if (RT_FAILURE(rc))
627 return RTMsgErrorExitFailure("Failed to open password file '%s' for reading: %Rrc", pValueUnion->psz, rc);
628 }
629 rc = RTStrmGetLine(pStrm, m_strPassword.mutableRaw(), cchMax);
630 if (fClose)
631 RTStrmClose(pStrm);
632 if (rc == VERR_BUFFER_OVERFLOW || rc == VINF_BUFFER_OVERFLOW)
633 return RTMsgErrorExitFailure("Password from '%s' is too long (max %zu)", pValueUnion->psz, cchMax);
634 if (RT_FAILURE(rc))
635 return RTMsgErrorExitFailure("Error reading password from '%s': %Rrc", pValueUnion->psz, rc);
636
637 m_strPassword.jolt();
638 m_strPassword.stripRight();
639 m_pszKeyPassword = m_strPassword.c_str();
640 break;
641 }
642 default:
643 AssertFailedReturn(RTMsgErrorExitFailure("Invalid offOpt=%u!\n", offOpt));
644 }
645 return RTEXITCODE_SUCCESS;
646 }
647
648 RTEXITCODE finalizeOptions(unsigned cVerbosity)
649 {
650 RT_NOREF(cVerbosity);
651
652 /* Only do this once. */
653 if (m_fFinalized)
654 return RTEXITCODE_SUCCESS;
655 m_fFinalized = true;
656
657 /*
658 * Got a cert? Is it required?
659 */
660 bool const fHasKey = ( m_pszKeyFile != NULL
661 || m_pszKeyName != NULL);
662 bool const fHasCert = ( m_pszCertFile != NULL
663 || m_pszCertSha1 != NULL
664 || m_pszCertSubject != NULL);
665 if (!fHasCert)
666 {
667 if (m_fMandatory)
668 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Specifying a %s certificiate is required.", m_pszWhat);
669 return RTEXITCODE_SUCCESS;
670 }
671
672 /*
673 * Get the certificate.
674 */
675 RTERRINFOSTATIC ErrInfo;
676 /* From file: */
677 if (m_pszCertFile)
678 {
679 int rc = RTCrX509Certificate_ReadFromFile(&m_DecodedCert, m_pszCertFile, 0, &g_RTAsn1DefaultAllocator,
680 RTErrInfoInitStatic(&ErrInfo));
681 if (RT_FAILURE(rc))
682 return RTMsgErrorExitFailure("Error reading %s certificate from '%s': %Rrc%#RTeim",
683 m_pszWhat, m_pszCertFile, rc, &ErrInfo.Core);
684 pCertificate = &m_DecodedCert;
685 }
686 /* From certificate store by name (substring) or fingerprint: */
687 else
688 {
689#ifdef RT_OS_WINDOWS
690 m_hStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, X509_ASN_ENCODING, NULL,
691 CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG | CERT_STORE_READONLY_FLAG
692 | CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_ENUM_ARCHIVED_FLAG
693 | (m_fMachineStore ? CERT_SYSTEM_STORE_LOCAL_MACHINE : CERT_SYSTEM_STORE_CURRENT_USER),
694 m_pszCertStore);
695 if (m_hStore == NULL)
696 return RTMsgErrorExitFailure("Failed to open %s store '%s': %Rwc (%u)", m_fMachineStore ? "machine" : "user",
697 m_pszCertStore, GetLastError(), GetLastError());
698
699 CRYPT_HASH_BLOB Thumbprint = { RTSHA1_HASH_SIZE, m_abCertSha1 };
700 PRTUTF16 pwszSubject = NULL;
701 void const *pvFindParam = &Thumbprint;
702 DWORD fFind = CERT_FIND_SHA1_HASH;
703 if (!m_pszCertSha1)
704 {
705 int rc = RTStrToUtf16(m_pszCertSubject, &pwszSubject);
706 if (RT_FAILURE(rc))
707 return RTMsgErrorExitFailure("RTStrToUtf16 failed: %Rrc, input %.*Rhxs",
708 rc, strlen(m_pszCertSubject), m_pszCertSubject);
709 pvFindParam = pwszSubject;
710 fFind = CERT_FIND_SUBJECT_STR;
711 }
712
713 while ((m_pCertCtx = CertFindCertificateInStore(m_hStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0 /*fFlags*/,
714 fFind, pvFindParam, m_pCertCtx)) != NULL)
715 {
716 if (m_pCertCtx->dwCertEncodingType & X509_ASN_ENCODING)
717 {
718 RTASN1CURSORPRIMARY PrimaryCursor;
719 RTAsn1CursorInitPrimary(&PrimaryCursor, m_pCertCtx->pbCertEncoded, m_pCertCtx->cbCertEncoded,
720 RTErrInfoInitStatic(&ErrInfo),
721 &g_RTAsn1DefaultAllocator, RTASN1CURSOR_FLAGS_DER, "CurCtx");
722 int rc = RTCrX509Certificate_DecodeAsn1(&PrimaryCursor.Cursor, 0, &m_DecodedCert, "Cert");
723 if (RT_SUCCESS(rc))
724 {
725 pCertificate = &m_DecodedCert;
726 break;
727 }
728 RTMsgError("failed to decode certificate %p: %Rrc%#RTeim", m_pCertCtx, rc, &ErrInfo.Core);
729 }
730 }
731
732 RTUtf16Free(pwszSubject);
733 if (!m_pCertCtx)
734 return RTMsgErrorExitFailure("No certificate found matching %s '%s' (%Rwc / %u)",
735 m_pszCertSha1 ? "thumbprint" : "subject substring",
736 m_pszCertSha1 ? m_pszCertSha1 : m_pszCertSubject, GetLastError(), GetLastError());
737
738 /* Use this for private key too? */
739 if (!fHasKey)
740 {
741 HCRYPTPROV_OR_NCRYPT_KEY_HANDLE hTmpPrivateKey = 0;
742 DWORD dwKeySpec = 0;
743 if (CryptAcquireCertificatePrivateKey(m_pCertCtx,
744 CRYPT_ACQUIRE_SILENT_FLAG | CRYPT_ACQUIRE_COMPARE_KEY_FLAG
745 | CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG
746 | CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG,
747 NULL, &hTmpPrivateKey, &dwKeySpec, &m_fFreePrivateHandle))
748 {
749 if (cVerbosity > 1)
750 RTMsgInfo("hTmpPrivateKey=%p m_fFreePrivateHandle=%d dwKeySpec=%#x",
751 hTmpPrivateKey, m_fFreePrivateHandle, dwKeySpec);
752 Assert(dwKeySpec == CERT_NCRYPT_KEY_SPEC);
753 if (dwKeySpec == CERT_NCRYPT_KEY_SPEC)
754 hNCryptPrivateKey = hTmpPrivateKey;
755 else
756 hLegacyPrivateKey = hTmpPrivateKey; /** @todo remove or drop CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG */
757 return loadFakePrivateKeyAndCert();
758 }
759 return RTMsgErrorExitFailure("CryptAcquireCertificatePrivateKey failed: %Rwc (%d)", GetLastError(), GetLastError());
760 }
761#else
762 return RTMsgErrorExitFailure("Certificate store support is missing on this host");
763#endif
764 }
765
766 /*
767 * Get hold of the private key (if someone above already did, they'd returned already).
768 */
769 Assert(hPrivateKey == NIL_RTCRKEY);
770 /* Use cert file if nothing else specified. */
771 if (!fHasKey && m_pszCertFile)
772 m_pszKeyFile = m_pszCertFile;
773
774 /* Load from file:*/
775 if (m_pszKeyFile)
776 {
777 int rc = RTCrKeyCreateFromFile(&hPrivateKey, 0 /*fFlags*/, m_pszKeyFile, m_pszKeyPassword,
778 RTErrInfoInitStatic(&ErrInfo));
779 if (RT_FAILURE(rc))
780 return RTMsgErrorExitFailure("Error reading the %s private key from '%s': %Rrc%#RTeim",
781 m_pszWhat, m_pszKeyFile, rc, &ErrInfo.Core);
782 }
783 /* From key store: */
784 else
785 {
786 return RTMsgErrorExitFailure("Key store support is missing on this host");
787 }
788
789 return RTEXITCODE_SUCCESS;
790 }
791
792 /** Returns the real certificate. */
793 PCRTCRX509CERTIFICATE getRealCertificate() const
794 {
795#ifdef RT_OS_WINDOWS
796 if (pCertificateReal)
797 return pCertificateReal;
798#endif
799 return pCertificate;
800 }
801
802#ifdef RT_OS_WINDOWS
803 RTEXITCODE loadFakePrivateKeyAndCert()
804 {
805 int rc = RTCrX509Certificate_ReadFromBuffer(&m_DecodedFakeCert, g_abFakeCertificate, sizeof(g_abFakeCertificate),
806 0 /*fFlags*/, &g_RTAsn1DefaultAllocator, NULL, NULL);
807 if (RT_FAILURE(rc))
808 return RTMsgErrorExitFailure("RTCrX509Certificate_ReadFromBuffer/g_abFakeCertificate failed: %Rrc", rc);
809 pCertificateReal = pCertificate;
810 pCertificate = &m_DecodedFakeCert;
811
812 rc = RTCrKeyCreateFromBuffer(&hPrivateKey, 0 /*fFlags*/, g_abFakeRsaKey, sizeof(g_abFakeRsaKey), NULL, NULL, NULL);
813 if (RT_FAILURE(rc))
814 return RTMsgErrorExitFailure("RTCrKeyCreateFromBuffer/g_abFakeRsaKey failed: %Rrc", rc);
815 return RTEXITCODE_SUCCESS;
816 }
817
818#endif
819
820 /**
821 * Search for intermediate CA.
822 *
823 * Currently this only do a single certificate path, so this may go south if
824 * there are multiple paths available. It may work fine for a cross signing
825 * path, as long as the cross over is at the level immediately below the root.
826 */
827 PCRTCRCERTCTX findNextIntermediateCert(PCRTCRCERTCTX pPrev)
828 {
829 /*
830 * Make sure the store is loaded before we start.
831 */
832 if (s_hStoreIntermediate == NIL_RTCRSTORE)
833 {
834 Assert(!pPrev);
835 RTERRINFOSTATIC ErrInfo;
836 int rc = RTCrStoreCreateSnapshotById(&s_hStoreIntermediate,
837 !m_fMachineStore
838 ? RTCRSTOREID_USER_INTERMEDIATE_CAS : RTCRSTOREID_SYSTEM_INTERMEDIATE_CAS,
839 RTErrInfoInitStatic(&ErrInfo));
840 if (RT_FAILURE(rc))
841 {
842 RTMsgError("RTCrStoreCreateSnapshotById/%s-intermediate-CAs failed: %Rrc%#RTeim",
843 m_fMachineStore ? "user" : "machine", rc, &ErrInfo.Core);
844 return NULL;
845 }
846 }
847
848 /*
849 * Open the search handle for the parent of the previous/end certificate.
850 *
851 * We don't need to consider RTCRCERTCTX::pTaInfo here as we're not
852 * after trust anchors, only intermediate certificates.
853 */
854#ifdef RT_OS_WINDOWS
855 PCRTCRX509CERTIFICATE pChildCert = pPrev ? pPrev->pCert : pCertificateReal ? pCertificateReal : pCertificate;
856#else
857 PCRTCRX509CERTIFICATE pChildCert = pPrev ? pPrev->pCert : pCertificate;
858#endif
859 AssertReturnStmt(pChildCert, RTCrCertCtxRelease(pPrev), NULL);
860
861 RTCRSTORECERTSEARCH Search;
862 int rc = RTCrStoreCertFindBySubjectOrAltSubjectByRfc5280(s_hStoreIntermediate, &pChildCert->TbsCertificate.Issuer,
863 &Search);
864 if (RT_FAILURE(rc))
865 {
866 RTMsgError("RTCrStoreCertFindBySubjectOrAltSubjectByRfc5280 failed: %Rrc", rc);
867 return NULL;
868 }
869
870 /*
871 * We only gave the subject so, we have to check the serial number our selves.
872 */
873 PCRTCRCERTCTX pCertCtx;
874 while ((pCertCtx = RTCrStoreCertSearchNext(s_hStoreIntermediate, &Search)) != NULL)
875 {
876 if ( pCertCtx->pCert
877 && RTAsn1BitString_Compare(&pCertCtx->pCert->TbsCertificate.T1.IssuerUniqueId,
878 &pChildCert->TbsCertificate.T1.IssuerUniqueId) == 0 /* compares presentness too */
879 && !RTCrX509Certificate_IsSelfSigned(pCertCtx->pCert))
880 {
881 break; /** @todo compare valid periode too and keep a best match when outside the desired period? */
882 }
883 RTCrCertCtxRelease(pCertCtx);
884 }
885
886 RTCrStoreCertSearchDestroy(s_hStoreIntermediate, & Search);
887 RTCrCertCtxRelease(pPrev);
888 return pCertCtx;
889 }
890
891 /**
892 * Merges the user specified certificates with the signing certificate and any
893 * intermediate CAs we can find in the system store.
894 *
895 * @returns Merged store, NIL_RTCRSTORE on failure (messaged).
896 * @param hUserSpecifiedCertificates The user certificate store.
897 */
898 RTCRSTORE assembleAllAdditionalCertificates(RTCRSTORE hUserSpecifiedCertificates)
899 {
900 RTCRSTORE hRetStore;
901 int rc = RTCrStoreCreateInMemEx(&hRetStore, 0, hUserSpecifiedCertificates);
902 if (RT_SUCCESS(rc))
903 {
904 /* Add the signing certificate: */
905 RTERRINFOSTATIC ErrInfo;
906 rc = RTCrStoreCertAddX509(hRetStore, RTCRCERTCTX_F_ENC_X509_DER | RTCRCERTCTX_F_ADD_IF_NOT_FOUND,
907#ifdef RT_OS_WINDOWS
908 (PRTCRX509CERTIFICATE)(pCertificateReal ? pCertificateReal : pCertificate),
909#else
910 (PRTCRX509CERTIFICATE)pCertificate,
911#endif
912 RTErrInfoInitStatic(&ErrInfo));
913 if (RT_SUCCESS(rc))
914 {
915 /* Add all intermediate CAs certificates we can find. */
916 PCRTCRCERTCTX pInterCaCert = NULL;
917 while ((pInterCaCert = findNextIntermediateCert(pInterCaCert)) != NULL)
918 {
919 rc = RTCrStoreCertAddEncoded(hRetStore, RTCRCERTCTX_F_ENC_X509_DER | RTCRCERTCTX_F_ADD_IF_NOT_FOUND,
920 pInterCaCert->pabEncoded, pInterCaCert->cbEncoded,
921 RTErrInfoInitStatic(&ErrInfo));
922 if (RT_FAILURE(rc))
923 {
924 RTMsgError("RTCrStoreCertAddEncoded/InterCA failed: %Rrc%#RTeim", rc, &ErrInfo.Core);
925 RTCrCertCtxRelease(pInterCaCert);
926 break;
927 }
928 }
929 if (RT_SUCCESS(rc))
930 return hRetStore;
931 }
932 else
933 RTMsgError("RTCrStoreCertAddX509/signer failed: %Rrc%#RTeim", rc, &ErrInfo.Core);
934 RTCrStoreRelease(hRetStore);
935 }
936 else
937 RTMsgError("RTCrStoreCreateInMemEx failed: %Rrc", rc);
938 return NIL_RTCRSTORE;
939 }
940
941};
942
943/*static*/ RTCRSTORE SignToolKeyPair::s_hStoreIntermediate = NIL_RTCRSTORE;
944/*static*/ uint32_t SignToolKeyPair::s_cInstances = 0;
945
946
947
948/*********************************************************************************************************************************
949* Workers. *
950*********************************************************************************************************************************/
951
952
953/**
954 * Deletes the structure.
955 *
956 * @param pThis The structure to initialize.
957 */
958static void SignToolPkcs7_Delete(PSIGNTOOLPKCS7 pThis)
959{
960 RTCrPkcs7ContentInfo_Delete(&pThis->ContentInfo);
961 pThis->pSignedData = NULL;
962 RTMemFree(pThis->pbBuf);
963 pThis->pbBuf = NULL;
964 pThis->cbBuf = 0;
965 RTMemFree(pThis->pbNewBuf);
966 pThis->pbNewBuf = NULL;
967 pThis->cbNewBuf = 0;
968}
969
970
971/**
972 * Deletes the structure.
973 *
974 * @param pThis The structure to initialize.
975 */
976static void SignToolPkcs7Exe_Delete(PSIGNTOOLPKCS7EXE pThis)
977{
978 if (pThis->hLdrMod != NIL_RTLDRMOD)
979 {
980 int rc2 = RTLdrClose(pThis->hLdrMod);
981 if (RT_FAILURE(rc2))
982 RTMsgError("RTLdrClose failed: %Rrc\n", rc2);
983 pThis->hLdrMod = NIL_RTLDRMOD;
984 }
985 SignToolPkcs7_Delete(pThis);
986}
987
988
989/**
990 * Decodes the PKCS #7 blob pointed to by pThis->pbBuf.
991 *
992 * @returns IPRT status code (error message already shown on failure).
993 * @param pThis The PKCS\#7 signature to decode.
994 * @param fCatalog Set if catalog file, clear if executable.
995 */
996static int SignToolPkcs7_Decode(PSIGNTOOLPKCS7 pThis, bool fCatalog)
997{
998 RTERRINFOSTATIC ErrInfo;
999 RTASN1CURSORPRIMARY PrimaryCursor;
1000 RTAsn1CursorInitPrimary(&PrimaryCursor, pThis->pbBuf, (uint32_t)pThis->cbBuf, RTErrInfoInitStatic(&ErrInfo),
1001 &g_RTAsn1DefaultAllocator, 0, "WinCert");
1002
1003 int rc = RTCrPkcs7ContentInfo_DecodeAsn1(&PrimaryCursor.Cursor, 0, &pThis->ContentInfo, "CI");
1004 if (RT_SUCCESS(rc))
1005 {
1006 if (RTCrPkcs7ContentInfo_IsSignedData(&pThis->ContentInfo))
1007 {
1008 pThis->pSignedData = pThis->ContentInfo.u.pSignedData;
1009
1010 /*
1011 * Decode the authenticode bits.
1012 */
1013 if (!strcmp(pThis->pSignedData->ContentInfo.ContentType.szObjId, RTCRSPCINDIRECTDATACONTENT_OID))
1014 {
1015 PRTCRSPCINDIRECTDATACONTENT pIndData = pThis->pSignedData->ContentInfo.u.pIndirectDataContent;
1016 Assert(pIndData);
1017
1018 /*
1019 * Check that things add up.
1020 */
1021 rc = RTCrPkcs7SignedData_CheckSanity(pThis->pSignedData,
1022 RTCRPKCS7SIGNEDDATA_SANITY_F_AUTHENTICODE
1023 | RTCRPKCS7SIGNEDDATA_SANITY_F_ONLY_KNOWN_HASH
1024 | RTCRPKCS7SIGNEDDATA_SANITY_F_SIGNING_CERT_PRESENT,
1025 RTErrInfoInitStatic(&ErrInfo), "SD");
1026 if (RT_SUCCESS(rc))
1027 {
1028 rc = RTCrSpcIndirectDataContent_CheckSanityEx(pIndData,
1029 pThis->pSignedData,
1030 RTCRSPCINDIRECTDATACONTENT_SANITY_F_ONLY_KNOWN_HASH,
1031 RTErrInfoInitStatic(&ErrInfo));
1032 if (RT_FAILURE(rc))
1033 RTMsgError("SPC indirect data content sanity check failed for '%s': %Rrc - %s\n",
1034 pThis->pszFilename, rc, ErrInfo.szMsg);
1035 }
1036 else
1037 RTMsgError("PKCS#7 sanity check failed for '%s': %Rrc - %s\n", pThis->pszFilename, rc, ErrInfo.szMsg);
1038 }
1039 else if (!strcmp(pThis->pSignedData->ContentInfo.ContentType.szObjId, RTCR_PKCS7_DATA_OID))
1040 { /* apple code signing */ }
1041 else if (!fCatalog)
1042 RTMsgError("Unexpected the signed content in '%s': %s (expected %s)", pThis->pszFilename,
1043 pThis->pSignedData->ContentInfo.ContentType.szObjId, RTCRSPCINDIRECTDATACONTENT_OID);
1044 }
1045 else
1046 rc = RTMsgErrorRc(VERR_CR_PKCS7_NOT_SIGNED_DATA,
1047 "PKCS#7 content is inside '%s' is not 'signedData': %s\n",
1048 pThis->pszFilename, pThis->ContentInfo.ContentType.szObjId);
1049 }
1050 else
1051 RTMsgError("RTCrPkcs7ContentInfo_DecodeAsn1 failed on '%s': %Rrc - %s\n", pThis->pszFilename, rc, ErrInfo.szMsg);
1052 return rc;
1053}
1054
1055
1056/**
1057 * Reads and decodes PKCS\#7 signature from the given cat file.
1058 *
1059 * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE with error message
1060 * on failure.
1061 * @param pThis The structure to initialize.
1062 * @param pszFilename The catalog (or any other DER PKCS\#7) filename.
1063 * @param cVerbosity The verbosity.
1064 */
1065static RTEXITCODE SignToolPkcs7_InitFromFile(PSIGNTOOLPKCS7 pThis, const char *pszFilename, unsigned cVerbosity)
1066{
1067 /*
1068 * Init the return structure.
1069 */
1070 RT_ZERO(*pThis);
1071 pThis->pszFilename = pszFilename;
1072 pThis->enmType = RTSIGNTOOLFILETYPE_CAT;
1073
1074 /*
1075 * Lazy bird uses RTFileReadAll and duplicates the allocation.
1076 */
1077 void *pvFile;
1078 int rc = RTFileReadAll(pszFilename, &pvFile, &pThis->cbBuf);
1079 if (RT_SUCCESS(rc))
1080 {
1081 pThis->pbBuf = (uint8_t *)RTMemDup(pvFile, pThis->cbBuf);
1082 RTFileReadAllFree(pvFile, pThis->cbBuf);
1083 if (pThis->pbBuf)
1084 {
1085 if (cVerbosity > 2)
1086 RTPrintf("PKCS#7 signature: %u bytes\n", pThis->cbBuf);
1087
1088 /*
1089 * Decode it.
1090 */
1091 rc = SignToolPkcs7_Decode(pThis, true /*fCatalog*/);
1092 if (RT_SUCCESS(rc))
1093 return RTEXITCODE_SUCCESS;
1094 }
1095 else
1096 RTMsgError("Out of memory!");
1097 }
1098 else
1099 RTMsgError("Error reading '%s' into memory: %Rrc", pszFilename, rc);
1100
1101 SignToolPkcs7_Delete(pThis);
1102 return RTEXITCODE_FAILURE;
1103}
1104
1105
1106/**
1107 * Encodes the signature into the SIGNTOOLPKCS7::pbNewBuf and
1108 * SIGNTOOLPKCS7::cbNewBuf members.
1109 *
1110 * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE with error message
1111 * on failure.
1112 * @param pThis The signature to encode.
1113 * @param cVerbosity The verbosity.
1114 */
1115static RTEXITCODE SignToolPkcs7_Encode(PSIGNTOOLPKCS7 pThis, unsigned cVerbosity)
1116{
1117 RTERRINFOSTATIC StaticErrInfo;
1118 PRTASN1CORE pRoot = RTCrPkcs7ContentInfo_GetAsn1Core(&pThis->ContentInfo);
1119 uint32_t cbEncoded;
1120 int rc = RTAsn1EncodePrepare(pRoot, RTASN1ENCODE_F_DER, &cbEncoded, RTErrInfoInitStatic(&StaticErrInfo));
1121 if (RT_SUCCESS(rc))
1122 {
1123 if (cVerbosity >= 4)
1124 RTAsn1Dump(pRoot, 0, 0, RTStrmDumpPrintfV, g_pStdOut);
1125
1126 RTMemFree(pThis->pbNewBuf);
1127 pThis->cbNewBuf = cbEncoded;
1128 pThis->pbNewBuf = (uint8_t *)RTMemAllocZ(cbEncoded);
1129 if (pThis->pbNewBuf)
1130 {
1131 rc = RTAsn1EncodeToBuffer(pRoot, RTASN1ENCODE_F_DER, pThis->pbNewBuf, pThis->cbNewBuf,
1132 RTErrInfoInitStatic(&StaticErrInfo));
1133 if (RT_SUCCESS(rc))
1134 {
1135 if (cVerbosity > 1)
1136 RTMsgInfo("Encoded signature to %u bytes", cbEncoded);
1137 return RTEXITCODE_SUCCESS;
1138 }
1139 RTMsgError("RTAsn1EncodeToBuffer failed: %Rrc", rc);
1140
1141 RTMemFree(pThis->pbNewBuf);
1142 pThis->pbNewBuf = NULL;
1143 }
1144 else
1145 RTMsgError("Failed to allocate %u bytes!", cbEncoded);
1146 }
1147 else
1148 RTMsgError("RTAsn1EncodePrepare failed: %Rrc - %s", rc, StaticErrInfo.szMsg);
1149 return RTEXITCODE_FAILURE;
1150}
1151
1152
1153/**
1154 * Helper that makes sure the UnauthenticatedAttributes are present in the given
1155 * SignerInfo structure.
1156 *
1157 * Call this before trying to modify the array.
1158 *
1159 * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE with error already
1160 * displayed on failure.
1161 * @param pSignerInfo The SignerInfo structure in question.
1162 */
1163static RTEXITCODE SignToolPkcs7_EnsureUnauthenticatedAttributesPresent(PRTCRPKCS7SIGNERINFO pSignerInfo)
1164{
1165 if (pSignerInfo->UnauthenticatedAttributes.cItems == 0)
1166 {
1167 /* HACK ALERT! Invent ASN.1 setters/whatever for members to replace this mess. */
1168
1169 if (pSignerInfo->AuthenticatedAttributes.cItems == 0)
1170 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No authenticated or unauthenticated attributes! Sorry, no can do.");
1171
1172 Assert(pSignerInfo->UnauthenticatedAttributes.SetCore.Asn1Core.uTag == 0);
1173 int rc = RTAsn1SetCore_Init(&pSignerInfo->UnauthenticatedAttributes.SetCore,
1174 pSignerInfo->AuthenticatedAttributes.SetCore.Asn1Core.pOps);
1175 if (RT_FAILURE(rc))
1176 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTAsn1SetCore_Init failed: %Rrc", rc);
1177 pSignerInfo->UnauthenticatedAttributes.SetCore.Asn1Core.uTag = 1;
1178 pSignerInfo->UnauthenticatedAttributes.SetCore.Asn1Core.fClass = ASN1_TAGCLASS_CONTEXT | ASN1_TAGFLAG_CONSTRUCTED;
1179 RTAsn1MemInitArrayAllocation(&pSignerInfo->UnauthenticatedAttributes.Allocation,
1180 pSignerInfo->AuthenticatedAttributes.Allocation.pAllocator,
1181 sizeof(**pSignerInfo->UnauthenticatedAttributes.papItems));
1182 }
1183 return RTEXITCODE_SUCCESS;
1184}
1185
1186
1187/**
1188 * Adds the @a pSrc signature as a nested signature.
1189 *
1190 * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE with error message
1191 * on failure.
1192 * @param pThis The signature to modify.
1193 * @param pSrc The signature to add as nested.
1194 * @param cVerbosity The verbosity.
1195 * @param fPrepend Whether to prepend (true) or append (false) the
1196 * source signature to the nested attribute.
1197 */
1198static RTEXITCODE SignToolPkcs7_AddNestedSignature(PSIGNTOOLPKCS7 pThis, PSIGNTOOLPKCS7 pSrc,
1199 unsigned cVerbosity, bool fPrepend)
1200{
1201 PRTCRPKCS7SIGNERINFO pSignerInfo = pThis->pSignedData->SignerInfos.papItems[0];
1202
1203 /*
1204 * Deal with UnauthenticatedAttributes being absent before trying to append to the array.
1205 */
1206 RTEXITCODE rcExit = SignToolPkcs7_EnsureUnauthenticatedAttributesPresent(pSignerInfo);
1207 if (rcExit != RTEXITCODE_SUCCESS)
1208 return rcExit;
1209
1210 /*
1211 * Find or add an unauthenticated attribute for nested signatures.
1212 */
1213 int rc = VERR_NOT_FOUND;
1214 PRTCRPKCS7ATTRIBUTE pAttr = NULL;
1215 int32_t iPos = pSignerInfo->UnauthenticatedAttributes.cItems;
1216 while (iPos-- > 0)
1217 if (pSignerInfo->UnauthenticatedAttributes.papItems[iPos]->enmType == RTCRPKCS7ATTRIBUTETYPE_MS_NESTED_SIGNATURE)
1218 {
1219 pAttr = pSignerInfo->UnauthenticatedAttributes.papItems[iPos];
1220 rc = VINF_SUCCESS;
1221 break;
1222 }
1223 if (iPos < 0)
1224 {
1225 iPos = RTCrPkcs7Attributes_Append(&pSignerInfo->UnauthenticatedAttributes);
1226 if (iPos >= 0)
1227 {
1228 if (cVerbosity >= 3)
1229 RTMsgInfo("Adding UnauthenticatedAttribute #%u...", iPos);
1230 Assert((uint32_t)iPos < pSignerInfo->UnauthenticatedAttributes.cItems);
1231
1232 pAttr = pSignerInfo->UnauthenticatedAttributes.papItems[iPos];
1233 rc = RTAsn1ObjId_InitFromString(&pAttr->Type, RTCR_PKCS9_ID_MS_NESTED_SIGNATURE, pAttr->Allocation.pAllocator);
1234 if (RT_SUCCESS(rc))
1235 {
1236 /** @todo Generalize the Type + enmType DYN stuff and generate setters. */
1237 Assert(pAttr->enmType == RTCRPKCS7ATTRIBUTETYPE_NOT_PRESENT);
1238 Assert(pAttr->uValues.pContentInfos == NULL);
1239 pAttr->enmType = RTCRPKCS7ATTRIBUTETYPE_MS_NESTED_SIGNATURE;
1240 rc = RTAsn1MemAllocZ(&pAttr->Allocation, (void **)&pAttr->uValues.pContentInfos,
1241 sizeof(*pAttr->uValues.pContentInfos));
1242 if (RT_SUCCESS(rc))
1243 {
1244 rc = RTCrPkcs7SetOfContentInfos_Init(pAttr->uValues.pContentInfos, pAttr->Allocation.pAllocator);
1245 if (!RT_SUCCESS(rc))
1246 RTMsgError("RTCrPkcs7ContentInfos_Init failed: %Rrc", rc);
1247 }
1248 else
1249 RTMsgError("RTAsn1MemAllocZ failed: %Rrc", rc);
1250 }
1251 else
1252 RTMsgError("RTAsn1ObjId_InitFromString failed: %Rrc", rc);
1253 }
1254 else
1255 RTMsgError("RTCrPkcs7Attributes_Append failed: %Rrc", iPos);
1256 }
1257 else if (cVerbosity >= 2)
1258 RTMsgInfo("Found UnauthenticatedAttribute #%u...", iPos);
1259 if (RT_SUCCESS(rc))
1260 {
1261 /*
1262 * Append/prepend the signature.
1263 */
1264 uint32_t iActualPos = UINT32_MAX;
1265 iPos = fPrepend ? 0 : pAttr->uValues.pContentInfos->cItems;
1266 rc = RTCrPkcs7SetOfContentInfos_InsertEx(pAttr->uValues.pContentInfos, iPos, &pSrc->ContentInfo,
1267 pAttr->Allocation.pAllocator, &iActualPos);
1268 if (RT_SUCCESS(rc))
1269 {
1270 if (cVerbosity > 0)
1271 RTMsgInfo("Added nested signature (#%u)", iActualPos);
1272 if (cVerbosity >= 3)
1273 {
1274 RTMsgInfo("SingerInfo dump after change:");
1275 RTAsn1Dump(RTCrPkcs7SignerInfo_GetAsn1Core(pSignerInfo), 0, 2, RTStrmDumpPrintfV, g_pStdOut);
1276 }
1277 return RTEXITCODE_SUCCESS;
1278 }
1279
1280 RTMsgError("RTCrPkcs7ContentInfos_InsertEx failed: %Rrc", rc);
1281 }
1282 return RTEXITCODE_FAILURE;
1283}
1284
1285
1286/**
1287 * Writes the signature to the file.
1288 *
1289 * Caller must have called SignToolPkcs7_Encode() prior to this function.
1290 *
1291 * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE with error
1292 * message on failure.
1293 * @param pThis The file which to write.
1294 * @param cVerbosity The verbosity.
1295 */
1296static RTEXITCODE SignToolPkcs7_WriteSignatureToFile(PSIGNTOOLPKCS7 pThis, const char *pszFilename, unsigned cVerbosity)
1297{
1298 AssertReturn(pThis->cbNewBuf && pThis->pbNewBuf, RTEXITCODE_FAILURE);
1299
1300 /*
1301 * Open+truncate file, write new signature, close. Simple.
1302 */
1303 RTFILE hFile;
1304 int rc = RTFileOpen(&hFile, pszFilename, RTFILE_O_WRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_TRUNCATE | RTFILE_O_DENY_WRITE);
1305 if (RT_SUCCESS(rc))
1306 {
1307 rc = RTFileWrite(hFile, pThis->pbNewBuf, pThis->cbNewBuf, NULL);
1308 if (RT_SUCCESS(rc))
1309 {
1310 rc = RTFileClose(hFile);
1311 if (RT_SUCCESS(rc))
1312 {
1313 if (cVerbosity > 0)
1314 RTMsgInfo("Wrote %u bytes to %s", pThis->cbNewBuf, pszFilename);
1315 return RTEXITCODE_SUCCESS;
1316 }
1317
1318 RTMsgError("RTFileClose failed on %s: %Rrc", pszFilename, rc);
1319 }
1320 else
1321 RTMsgError("Write error on %s: %Rrc", pszFilename, rc);
1322 }
1323 else
1324 RTMsgError("Failed to open %s for writing: %Rrc", pszFilename, rc);
1325 return RTEXITCODE_FAILURE;
1326}
1327
1328
1329
1330/**
1331 * Worker for recursively searching for MS nested signatures and signer infos.
1332 *
1333 * @returns Pointer to the signer info corresponding to @a iReqSignature. NULL
1334 * if not found.
1335 * @param pSignedData The signature to search.
1336 * @param piNextSignature Pointer to the variable keeping track of the next
1337 * signature number.
1338 * @param iReqSignature The request signature number.
1339 * @param ppSignedData Where to return the signature data structure.
1340 * Optional.
1341 */
1342static PRTCRPKCS7SIGNERINFO SignToolPkcs7_FindNestedSignatureByIndexWorker(PRTCRPKCS7SIGNEDDATA pSignedData,
1343 uint32_t *piNextSignature,
1344 uint32_t iReqSignature,
1345 PRTCRPKCS7SIGNEDDATA *ppSignedData)
1346{
1347 for (uint32_t iSignerInfo = 0; iSignerInfo < pSignedData->SignerInfos.cItems; iSignerInfo++)
1348 {
1349 /* Match?*/
1350 PRTCRPKCS7SIGNERINFO pSignerInfo = pSignedData->SignerInfos.papItems[iSignerInfo];
1351 if (*piNextSignature == iReqSignature)
1352 {
1353 if (ppSignedData)
1354 *ppSignedData = pSignedData;
1355 return pSignerInfo;
1356 }
1357 *piNextSignature += 1;
1358
1359 /* Look for nested signatures. */
1360 for (uint32_t iAttrib = 0; iAttrib < pSignerInfo->UnauthenticatedAttributes.cItems; iAttrib++)
1361 if (pSignerInfo->UnauthenticatedAttributes.papItems[iAttrib]->enmType == RTCRPKCS7ATTRIBUTETYPE_MS_NESTED_SIGNATURE)
1362 {
1363 PRTCRPKCS7SETOFCONTENTINFOS pCntInfos;
1364 pCntInfos = pSignerInfo->UnauthenticatedAttributes.papItems[iAttrib]->uValues.pContentInfos;
1365 for (uint32_t iCntInfo = 0; iCntInfo < pCntInfos->cItems; iCntInfo++)
1366 {
1367 PRTCRPKCS7CONTENTINFO pCntInfo = pCntInfos->papItems[iCntInfo];
1368 if (RTCrPkcs7ContentInfo_IsSignedData(pCntInfo))
1369 {
1370 PRTCRPKCS7SIGNERINFO pRet;
1371 pRet = SignToolPkcs7_FindNestedSignatureByIndexWorker(pCntInfo->u.pSignedData, piNextSignature,
1372 iReqSignature, ppSignedData);
1373 if (pRet)
1374 return pRet;
1375 }
1376 }
1377 }
1378 }
1379 return NULL;
1380}
1381
1382
1383/**
1384 * Locates the given nested signature.
1385 *
1386 * @returns Pointer to the signer info corresponding to @a iReqSignature. NULL
1387 * if not found.
1388 * @param pThis The PKCS\#7 structure to search.
1389 * @param iReqSignature The requested signature number.
1390 * @param ppSignedData Where to return the pointer to the signed data that
1391 * the returned signer info belongs to.
1392 *
1393 * @todo Move into SPC or PKCS\#7.
1394 */
1395static PRTCRPKCS7SIGNERINFO SignToolPkcs7_FindNestedSignatureByIndex(PSIGNTOOLPKCS7 pThis, uint32_t iReqSignature,
1396 PRTCRPKCS7SIGNEDDATA *ppSignedData)
1397{
1398 uint32_t iNextSignature = 0;
1399 return SignToolPkcs7_FindNestedSignatureByIndexWorker(pThis->pSignedData, &iNextSignature, iReqSignature, ppSignedData);
1400}
1401
1402
1403
1404/**
1405 * Reads and decodes PKCS\#7 signature from the given executable, if it has one.
1406 *
1407 * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE with error message
1408 * on failure.
1409 * @param pThis The structure to initialize.
1410 * @param pszFilename The executable filename.
1411 * @param cVerbosity The verbosity.
1412 * @param enmLdrArch For FAT binaries.
1413 * @param fAllowUnsigned Whether to allow unsigned binaries.
1414 */
1415static RTEXITCODE SignToolPkcs7Exe_InitFromFile(PSIGNTOOLPKCS7EXE pThis, const char *pszFilename, unsigned cVerbosity,
1416 RTLDRARCH enmLdrArch = RTLDRARCH_WHATEVER, bool fAllowUnsigned = false)
1417{
1418 /*
1419 * Init the return structure.
1420 */
1421 RT_ZERO(*pThis);
1422 pThis->hLdrMod = NIL_RTLDRMOD;
1423 pThis->pszFilename = pszFilename;
1424 pThis->enmType = RTSIGNTOOLFILETYPE_EXE;
1425
1426 /*
1427 * Open the image and check if it's signed.
1428 */
1429 int rc = RTLdrOpen(pszFilename, RTLDR_O_FOR_VALIDATION, enmLdrArch, &pThis->hLdrMod);
1430 if (RT_SUCCESS(rc))
1431 {
1432 bool fIsSigned = false;
1433 rc = RTLdrQueryProp(pThis->hLdrMod, RTLDRPROP_IS_SIGNED, &fIsSigned, sizeof(fIsSigned));
1434 if (RT_SUCCESS(rc) && fIsSigned)
1435 {
1436 /*
1437 * Query the PKCS#7 data (assuming M$ style signing) and hand it to a worker.
1438 */
1439 size_t cbActual = 0;
1440#ifdef DEBUG
1441 size_t cbBuf = 64;
1442#else
1443 size_t cbBuf = _512K;
1444#endif
1445 void *pvBuf = RTMemAllocZ(cbBuf);
1446 if (pvBuf)
1447 {
1448 rc = RTLdrQueryPropEx(pThis->hLdrMod, RTLDRPROP_PKCS7_SIGNED_DATA, NULL /*pvBits*/, pvBuf, cbBuf, &cbActual);
1449 if (rc == VERR_BUFFER_OVERFLOW)
1450 {
1451 RTMemFree(pvBuf);
1452 cbBuf = cbActual;
1453 pvBuf = RTMemAllocZ(cbActual);
1454 if (pvBuf)
1455 rc = RTLdrQueryPropEx(pThis->hLdrMod, RTLDRPROP_PKCS7_SIGNED_DATA, NULL /*pvBits*/,
1456 pvBuf, cbBuf, &cbActual);
1457 else
1458 rc = VERR_NO_MEMORY;
1459 }
1460 }
1461 else
1462 rc = VERR_NO_MEMORY;
1463
1464 pThis->pbBuf = (uint8_t *)pvBuf;
1465 pThis->cbBuf = cbActual;
1466 if (RT_SUCCESS(rc))
1467 {
1468 if (cVerbosity > 2)
1469 RTPrintf("PKCS#7 signature: %u bytes\n", cbActual);
1470 if (cVerbosity > 3)
1471 RTPrintf("%.*Rhxd\n", cbActual, pvBuf);
1472
1473 /*
1474 * Decode it.
1475 */
1476 rc = SignToolPkcs7_Decode(pThis, false /*fCatalog*/);
1477 if (RT_SUCCESS(rc))
1478 return RTEXITCODE_SUCCESS;
1479 }
1480 else
1481 RTMsgError("RTLdrQueryPropEx/RTLDRPROP_PKCS7_SIGNED_DATA failed on '%s': %Rrc\n", pszFilename, rc);
1482 }
1483 else if (RT_SUCCESS(rc))
1484 {
1485 if (!fAllowUnsigned || cVerbosity >= 2)
1486 RTMsgInfo("'%s': not signed\n", pszFilename);
1487 if (fAllowUnsigned)
1488 return RTEXITCODE_SUCCESS;
1489 }
1490 else
1491 RTMsgError("RTLdrQueryProp/RTLDRPROP_IS_SIGNED failed on '%s': %Rrc\n", pszFilename, rc);
1492 }
1493 else
1494 RTMsgError("Error opening executable image '%s': %Rrc", pszFilename, rc);
1495
1496 SignToolPkcs7Exe_Delete(pThis);
1497 return RTEXITCODE_FAILURE;
1498}
1499
1500
1501/**
1502 * Calculates the checksum of an executable.
1503 *
1504 * @returns Success indicator (errors are reported)
1505 * @param pThis The exe file to checksum.
1506 * @param hFile The file handle.
1507 * @param puCheckSum Where to return the checksum.
1508 */
1509static bool SignToolPkcs7Exe_CalcPeCheckSum(PSIGNTOOLPKCS7EXE pThis, RTFILE hFile, uint32_t *puCheckSum)
1510{
1511#ifdef RT_OS_WINDOWS
1512 /*
1513 * Try use IMAGEHLP!MapFileAndCheckSumW first.
1514 */
1515 PRTUTF16 pwszPath;
1516 int rc = RTStrToUtf16(pThis->pszFilename, &pwszPath);
1517 if (RT_SUCCESS(rc))
1518 {
1519 decltype(MapFileAndCheckSumW) *pfnMapFileAndCheckSumW;
1520 pfnMapFileAndCheckSumW = (decltype(MapFileAndCheckSumW) *)RTLdrGetSystemSymbol("IMAGEHLP.DLL", "MapFileAndCheckSumW");
1521 if (pfnMapFileAndCheckSumW)
1522 {
1523 DWORD uOldSum = UINT32_MAX;
1524 DWORD uCheckSum = UINT32_MAX;
1525 DWORD dwRc = pfnMapFileAndCheckSumW(pwszPath, &uOldSum, &uCheckSum);
1526 if (dwRc == CHECKSUM_SUCCESS)
1527 {
1528 *puCheckSum = uCheckSum;
1529 return true;
1530 }
1531 }
1532 }
1533#endif
1534
1535 RT_NOREF(pThis, hFile, puCheckSum);
1536 RTMsgError("Implement check sum calcuation fallback!");
1537 return false;
1538}
1539
1540
1541/**
1542 * Writes the signature to the file.
1543 *
1544 * This has the side-effect of closing the hLdrMod member. So, it can only be
1545 * called once!
1546 *
1547 * Caller must have called SignToolPkcs7_Encode() prior to this function.
1548 *
1549 * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE with error
1550 * message on failure.
1551 * @param pThis The file which to write.
1552 * @param cVerbosity The verbosity.
1553 */
1554static RTEXITCODE SignToolPkcs7Exe_WriteSignatureToFile(PSIGNTOOLPKCS7EXE pThis, unsigned cVerbosity)
1555{
1556 AssertReturn(pThis->cbNewBuf && pThis->pbNewBuf, RTEXITCODE_FAILURE);
1557
1558 /*
1559 * Get the file header offset and arch before closing the destination handle.
1560 */
1561 uint32_t offNtHdrs;
1562 int rc = RTLdrQueryProp(pThis->hLdrMod, RTLDRPROP_FILE_OFF_HEADER, &offNtHdrs, sizeof(offNtHdrs));
1563 if (RT_SUCCESS(rc))
1564 {
1565 RTLDRARCH enmLdrArch = RTLdrGetArch(pThis->hLdrMod);
1566 if (enmLdrArch != RTLDRARCH_INVALID)
1567 {
1568 RTLdrClose(pThis->hLdrMod);
1569 pThis->hLdrMod = NIL_RTLDRMOD;
1570 unsigned cbNtHdrs = 0;
1571 switch (enmLdrArch)
1572 {
1573 case RTLDRARCH_AMD64:
1574 cbNtHdrs = sizeof(IMAGE_NT_HEADERS64);
1575 break;
1576 case RTLDRARCH_X86_32:
1577 cbNtHdrs = sizeof(IMAGE_NT_HEADERS32);
1578 break;
1579 default:
1580 RTMsgError("Unknown image arch: %d", enmLdrArch);
1581 }
1582 if (cbNtHdrs > 0)
1583 {
1584 if (cVerbosity > 0)
1585 RTMsgInfo("offNtHdrs=%#x cbNtHdrs=%u\n", offNtHdrs, cbNtHdrs);
1586
1587 /*
1588 * Open the executable file for writing.
1589 */
1590 RTFILE hFile;
1591 rc = RTFileOpen(&hFile, pThis->pszFilename, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1592 if (RT_SUCCESS(rc))
1593 {
1594 /* Read the file header and locate the security directory entry. */
1595 union
1596 {
1597 IMAGE_NT_HEADERS32 NtHdrs32;
1598 IMAGE_NT_HEADERS64 NtHdrs64;
1599 } uBuf;
1600 PIMAGE_DATA_DIRECTORY pSecDir = cbNtHdrs == sizeof(IMAGE_NT_HEADERS64)
1601 ? &uBuf.NtHdrs64.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY]
1602 : &uBuf.NtHdrs32.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY];
1603
1604 rc = RTFileReadAt(hFile, offNtHdrs, &uBuf, cbNtHdrs, NULL);
1605 if ( RT_SUCCESS(rc)
1606 && uBuf.NtHdrs32.Signature == IMAGE_NT_SIGNATURE)
1607 {
1608 /*
1609 * Drop any old signature by truncating the file.
1610 */
1611 if ( pSecDir->Size > 8
1612 && pSecDir->VirtualAddress > offNtHdrs + sizeof(IMAGE_NT_HEADERS32))
1613 {
1614 rc = RTFileSetSize(hFile, pSecDir->VirtualAddress);
1615 if (RT_FAILURE(rc))
1616 RTMsgError("Error truncating file to %#x bytes: %Rrc", pSecDir->VirtualAddress, rc);
1617 }
1618 else if (pSecDir->Size != 0 && pSecDir->VirtualAddress == 0)
1619 rc = RTMsgErrorRc(VERR_BAD_EXE_FORMAT, "Bad security directory entry: VA=%#x Size=%#x",
1620 pSecDir->VirtualAddress, pSecDir->Size);
1621 if (RT_SUCCESS(rc))
1622 {
1623 /*
1624 * Sector align the signature portion.
1625 */
1626 uint32_t const cbWinCert = RT_UOFFSETOF(WIN_CERTIFICATE, bCertificate);
1627 uint64_t offCur = 0;
1628 rc = RTFileQuerySize(hFile, &offCur);
1629 if ( RT_SUCCESS(rc)
1630 && offCur < _2G)
1631 {
1632 if (offCur & 0x1ff)
1633 {
1634 uint32_t cbNeeded = 0x200 - ((uint32_t)offCur & 0x1ff);
1635 rc = RTFileWriteAt(hFile, offCur, g_abRTZero4K, cbNeeded, NULL);
1636 if (RT_SUCCESS(rc))
1637 offCur += cbNeeded;
1638 }
1639 if (RT_SUCCESS(rc))
1640 {
1641 /*
1642 * Write the header followed by the signature data.
1643 */
1644 uint32_t const cbZeroPad = (uint32_t)(RT_ALIGN_Z(pThis->cbNewBuf, 8) - pThis->cbNewBuf);
1645 pSecDir->VirtualAddress = (uint32_t)offCur;
1646 pSecDir->Size = cbWinCert + (uint32_t)pThis->cbNewBuf + cbZeroPad;
1647 if (cVerbosity >= 2)
1648 RTMsgInfo("Writing %u (%#x) bytes of signature at %#x (%u).\n",
1649 pSecDir->Size, pSecDir->Size, pSecDir->VirtualAddress, pSecDir->VirtualAddress);
1650
1651 WIN_CERTIFICATE WinCert;
1652 WinCert.dwLength = pSecDir->Size;
1653 WinCert.wRevision = WIN_CERT_REVISION_2_0;
1654 WinCert.wCertificateType = WIN_CERT_TYPE_PKCS_SIGNED_DATA;
1655
1656 rc = RTFileWriteAt(hFile, offCur, &WinCert, cbWinCert, NULL);
1657 if (RT_SUCCESS(rc))
1658 {
1659 offCur += cbWinCert;
1660 rc = RTFileWriteAt(hFile, offCur, pThis->pbNewBuf, pThis->cbNewBuf, NULL);
1661 }
1662 if (RT_SUCCESS(rc) && cbZeroPad)
1663 {
1664 offCur += pThis->cbNewBuf;
1665 rc = RTFileWriteAt(hFile, offCur, g_abRTZero4K, cbZeroPad, NULL);
1666 }
1667 if (RT_SUCCESS(rc))
1668 {
1669 /*
1670 * Reset the checksum (sec dir updated already) and rewrite the header.
1671 */
1672 uBuf.NtHdrs32.OptionalHeader.CheckSum = 0;
1673 offCur = offNtHdrs;
1674 rc = RTFileWriteAt(hFile, offNtHdrs, &uBuf, cbNtHdrs, NULL);
1675 if (RT_SUCCESS(rc))
1676 rc = RTFileFlush(hFile);
1677 if (RT_SUCCESS(rc))
1678 {
1679 /*
1680 * Calc checksum and write out the header again.
1681 */
1682 uint32_t uCheckSum = UINT32_MAX;
1683 if (SignToolPkcs7Exe_CalcPeCheckSum(pThis, hFile, &uCheckSum))
1684 {
1685 uBuf.NtHdrs32.OptionalHeader.CheckSum = uCheckSum;
1686 rc = RTFileWriteAt(hFile, offNtHdrs, &uBuf, cbNtHdrs, NULL);
1687 if (RT_SUCCESS(rc))
1688 rc = RTFileFlush(hFile);
1689 if (RT_SUCCESS(rc))
1690 {
1691 rc = RTFileClose(hFile);
1692 if (RT_SUCCESS(rc))
1693 return RTEXITCODE_SUCCESS;
1694 RTMsgError("RTFileClose failed: %Rrc\n", rc);
1695 return RTEXITCODE_FAILURE;
1696 }
1697 }
1698 }
1699 }
1700 }
1701 if (RT_FAILURE(rc))
1702 RTMsgError("Write error at %#RX64: %Rrc", offCur, rc);
1703 }
1704 else if (RT_SUCCESS(rc))
1705 RTMsgError("File to big: %'RU64 bytes", offCur);
1706 else
1707 RTMsgError("RTFileQuerySize failed: %Rrc", rc);
1708 }
1709 }
1710 else if (RT_SUCCESS(rc))
1711 RTMsgError("Not NT executable header!");
1712 else
1713 RTMsgError("Error reading NT headers (%#x bytes) at %#x: %Rrc", cbNtHdrs, offNtHdrs, rc);
1714 RTFileClose(hFile);
1715 }
1716 else
1717 RTMsgError("Failed to open '%s' for writing: %Rrc", pThis->pszFilename, rc);
1718 }
1719 }
1720 else
1721 RTMsgError("RTLdrGetArch failed!");
1722 }
1723 else
1724 RTMsgError("RTLdrQueryProp/RTLDRPROP_FILE_OFF_HEADER failed: %Rrc", rc);
1725 return RTEXITCODE_FAILURE;
1726}
1727
1728#ifndef IPRT_SIGNTOOL_NO_SIGNING
1729
1730static PRTCRPKCS7ATTRIBUTE SignToolPkcs7_AuthAttribAppend(PRTCRPKCS7ATTRIBUTES pAuthAttribs)
1731{
1732 int32_t iPos = RTCrPkcs7Attributes_Append(pAuthAttribs);
1733 if (iPos >= 0)
1734 return pAuthAttribs->papItems[iPos];
1735 RTMsgError("RTCrPkcs7Attributes_Append failed: %Rrc", iPos);
1736 return NULL;
1737}
1738
1739
1740static RTEXITCODE SignToolPkcs7_AuthAttribsAddSigningTime(PRTCRPKCS7ATTRIBUTES pAuthAttribs, RTTIMESPEC SigningTime)
1741{
1742 /*
1743 * Signing time. For the old-style timestamps, Symantec used ASN.1 UTC TIME.
1744 * start -vv vv=ASN1_TAG_UTC_TIME
1745 * 00000187d6a65fd0/23b0: 0d 01 09 05 31 0f 17 0d-31 36 31 30 30 35 30 37 ....1...16100507
1746 * 00000187d6a65fe0/23c0: 35 30 33 30 5a 30 23 06-09 2a 86 48 86 f7 0d 01 5030Z0#..*.H....
1747 * ^^- end 2016-10-05T07:50:30.000000000Z (161005075030Z)
1748 */
1749 PRTCRPKCS7ATTRIBUTE pAttr = SignToolPkcs7_AuthAttribAppend(pAuthAttribs);
1750 if (!pAttr)
1751 return RTEXITCODE_FAILURE;
1752
1753 int rc = RTCrPkcs7Attribute_SetSigningTime(pAttr, NULL, pAuthAttribs->Allocation.pAllocator);
1754 if (RT_FAILURE(rc))
1755 return RTMsgErrorExitFailure("RTCrPkcs7Attribute_SetSigningTime failed: %Rrc", rc);
1756
1757 /* Create the timestamp. */
1758 int32_t iPos = RTAsn1SetOfTimes_Append(pAttr->uValues.pSigningTime);
1759 if (iPos < 0)
1760 return RTMsgErrorExitFailure("RTAsn1SetOfTimes_Append failed: %Rrc", iPos);
1761
1762 PRTASN1TIME pTime = pAttr->uValues.pSigningTime->papItems[iPos];
1763 rc = RTAsn1Time_SetTimeSpec(pTime, pAttr->Allocation.pAllocator, &SigningTime);
1764 if (RT_FAILURE(rc))
1765 return RTMsgErrorExitFailure("RTAsn1Time_SetTimeSpec failed: %Rrc", rc);
1766
1767 return RTEXITCODE_SUCCESS;
1768}
1769
1770
1771static RTEXITCODE SignToolPkcs7_AuthAttribsAddSpcOpusInfo(PRTCRPKCS7ATTRIBUTES pAuthAttribs, void *pvInfo)
1772{
1773 /** @todo The OpusInfo is a structure with an optional SpcString and an
1774 * optional SpcLink (url). The two attributes can be set using the /d and /du
1775 * options of MS signtool.exe, I think. We shouldn't be using them atm. */
1776
1777 PRTCRPKCS7ATTRIBUTE pAttr = SignToolPkcs7_AuthAttribAppend(pAuthAttribs);
1778 if (!pAttr)
1779 return RTEXITCODE_FAILURE;
1780
1781 int rc = RTCrPkcs7Attribute_SetMsStatementType(pAttr, NULL, pAuthAttribs->Allocation.pAllocator);
1782 if (RT_FAILURE(rc))
1783 return RTMsgErrorExitFailure("RTCrPkcs7Attribute_SetMsStatementType failed: %Rrc", rc);
1784
1785 /* Override the ID. */
1786 rc = RTAsn1ObjId_SetFromString(&pAttr->Type, RTCR_PKCS9_ID_MS_SP_OPUS_INFO, pAuthAttribs->Allocation.pAllocator);
1787 if (RT_FAILURE(rc))
1788 return RTMsgErrorExitFailure("RTAsn1ObjId_SetFromString failed: %Rrc", rc);
1789
1790 /* Add attribute value entry. */
1791 int32_t iPos = RTAsn1SetOfObjIdSeqs_Append(pAttr->uValues.pObjIdSeqs);
1792 if (iPos < 0)
1793 return RTMsgErrorExitFailure("RTAsn1SetOfObjIdSeqs_Append failed: %Rrc", iPos);
1794
1795 RT_NOREF(pvInfo); Assert(!pvInfo);
1796 return RTEXITCODE_SUCCESS;
1797}
1798
1799
1800static RTEXITCODE SignToolPkcs7_AuthAttribsAddMsStatementType(PRTCRPKCS7ATTRIBUTES pAuthAttribs, const char *pszTypeId)
1801{
1802 PRTCRPKCS7ATTRIBUTE pAttr = SignToolPkcs7_AuthAttribAppend(pAuthAttribs);
1803 if (!pAttr)
1804 return RTEXITCODE_FAILURE;
1805
1806 int rc = RTCrPkcs7Attribute_SetMsStatementType(pAttr, NULL, pAuthAttribs->Allocation.pAllocator);
1807 if (RT_FAILURE(rc))
1808 return RTMsgErrorExitFailure("RTCrPkcs7Attribute_SetMsStatementType failed: %Rrc", rc);
1809
1810 /* Add attribute value entry. */
1811 int32_t iPos = RTAsn1SetOfObjIdSeqs_Append(pAttr->uValues.pObjIdSeqs);
1812 if (iPos < 0)
1813 return RTMsgErrorExitFailure("RTAsn1SetOfObjIdSeqs_Append failed: %Rrc", iPos);
1814 PRTASN1SEQOFOBJIDS pSeqObjIds = pAttr->uValues.pObjIdSeqs->papItems[iPos];
1815
1816 /* Add a object id to the value. */
1817 RTASN1OBJID ObjIdValue;
1818 rc = RTAsn1ObjId_InitFromString(&ObjIdValue, pszTypeId, &g_RTAsn1DefaultAllocator);
1819 if (RT_FAILURE(rc))
1820 return RTMsgErrorExitFailure("RTAsn1ObjId_InitFromString/%s failed: %Rrc", pszTypeId, rc);
1821
1822 rc = RTAsn1SeqOfObjIds_InsertEx(pSeqObjIds, 0 /*iPos*/, &ObjIdValue, &g_RTAsn1DefaultAllocator, NULL);
1823 RTAsn1ObjId_Delete(&ObjIdValue);
1824 if (RT_FAILURE(rc))
1825 return RTMsgErrorExitFailure("RTAsn1SeqOfObjIds_InsertEx failed: %Rrc", rc);
1826
1827 return RTEXITCODE_SUCCESS;
1828}
1829
1830
1831static RTEXITCODE SignToolPkcs7_AuthAttribsAddContentType(PRTCRPKCS7ATTRIBUTES pAuthAttribs, const char *pszContentTypeId)
1832{
1833 PRTCRPKCS7ATTRIBUTE pAttr = SignToolPkcs7_AuthAttribAppend(pAuthAttribs);
1834 if (!pAttr)
1835 return RTEXITCODE_FAILURE;
1836
1837 int rc = RTCrPkcs7Attribute_SetContentType(pAttr, NULL, pAuthAttribs->Allocation.pAllocator);
1838 if (RT_FAILURE(rc))
1839 return RTMsgErrorExitFailure("RTCrPkcs7Attribute_SetContentType failed: %Rrc", rc);
1840
1841 /* Add a object id to the value. */
1842 RTASN1OBJID ObjIdValue;
1843 rc = RTAsn1ObjId_InitFromString(&ObjIdValue, pszContentTypeId, pAuthAttribs->Allocation.pAllocator);
1844 if (RT_FAILURE(rc))
1845 return RTMsgErrorExitFailure("RTAsn1ObjId_InitFromString/%s failed: %Rrc", pszContentTypeId, rc);
1846
1847 rc = RTAsn1SetOfObjIds_InsertEx(pAttr->uValues.pObjIds, 0 /*iPos*/, &ObjIdValue, pAuthAttribs->Allocation.pAllocator, NULL);
1848 RTAsn1ObjId_Delete(&ObjIdValue);
1849 if (RT_FAILURE(rc))
1850 return RTMsgErrorExitFailure("RTAsn1SetOfObjIds_InsertEx failed: %Rrc", rc);
1851
1852 return RTEXITCODE_SUCCESS;
1853}
1854
1855
1856static RTEXITCODE SignToolPkcs7_AddAuthAttribsForTimestamp(PRTCRPKCS7ATTRIBUTES pAuthAttribs, bool fTimestampTypeOld,
1857 RTTIMESPEC SigningTime, PCRTCRX509CERTIFICATE pTimestampCert)
1858{
1859 /*
1860 * Add content type.
1861 */
1862 RTEXITCODE rcExit = SignToolPkcs7_AuthAttribsAddContentType(pAuthAttribs,
1863 fTimestampTypeOld ? RTCR_PKCS7_DATA_OID : RTCRTSPTSTINFO_OID);
1864 if (rcExit != RTEXITCODE_SUCCESS)
1865 return rcExit;
1866
1867 /*
1868 * Add signing time.
1869 */
1870 rcExit = SignToolPkcs7_AuthAttribsAddSigningTime(pAuthAttribs, SigningTime);
1871 if (rcExit != RTEXITCODE_SUCCESS)
1872 return rcExit;
1873
1874 /*
1875 * More later if we want to support fTimestampTypeOld = false perhaps?
1876 */
1877 Assert(fTimestampTypeOld);
1878 RT_NOREF(fTimestampTypeOld, pTimestampCert);
1879
1880 return RTEXITCODE_SUCCESS;
1881}
1882
1883
1884static RTEXITCODE SignToolPkcs7_AddAuthAttribsForImageOrCatSignature(PRTCRPKCS7ATTRIBUTES pAuthAttribs, RTTIMESPEC SigningTime,
1885 bool fNoSigningTime, const char *pszContentTypeId)
1886{
1887 /*
1888 * Add SpcOpusInfo. No attribute values.
1889 * SEQ start -vv vv- Type ObjId
1890 * 1c60: 0e 03 02 1a 05 00 a0 70-30 10 06 0a 2b 06 01 04 .......p0...+...
1891 * 1c70: 01 82 37 02 01 0c 31 02-30 00 30 19 06 09 2a 86 ..7...1.0.0...*.
1892 * Set Of -^^ ^^- Empty Sequence.
1893 */
1894 RTEXITCODE rcExit = SignToolPkcs7_AuthAttribsAddSpcOpusInfo(pAuthAttribs, NULL /*pvInfo - none*/);
1895 if (rcExit != RTEXITCODE_SUCCESS)
1896 return rcExit;
1897
1898 /*
1899 * Add ContentType = Ms-SpcIndirectDataContext?
1900 * SEQ start -vv vv- Type ObjId
1901 * 1c70: 01 82 37 02 01 0c 31 02-30 00 30 19 06 09 2a 86 ..7...1.0.0...*.
1902 * 1c80: 48 86 f7 0d 01 09 03 31-0c 06 0a 2b 06 01 04 01 H......1...+....
1903 * 1c90: 82 37 02 01 04 ^^- ^^- ObjId
1904 * ^- Set Of
1905 */
1906 rcExit = SignToolPkcs7_AuthAttribsAddContentType(pAuthAttribs, pszContentTypeId);
1907 if (rcExit != RTEXITCODE_SUCCESS)
1908 return rcExit;
1909
1910 /*
1911 * Add Ms-SpcStatementType = Ms-SpcIndividualCodeSigning.
1912 * SEQ start -vv vv- Type ObjId
1913 * 1c90: 82 37 02 01 04 30 1c 06-0a 2b 06 01 04 01 82 37 .7...0...+.....7
1914 * 1ca0: 02 01 0b 31 0e 30 0c 06-0a 2b 06 01 04 01 82 37 ...1.0...+.....7
1915 * 1cb0: 02 01 15 ^^ ^^ ^^- ObjId
1916 * Set Of -^^ ^^- Sequence Of
1917 */
1918 rcExit = SignToolPkcs7_AuthAttribsAddMsStatementType(pAuthAttribs, RTCRSPC_STMT_TYPE_INDIVIDUAL_CODE_SIGNING);
1919 if (rcExit != RTEXITCODE_SUCCESS)
1920 return rcExit;
1921
1922 /*
1923 * Add signing time. We add this, even if signtool.exe, since OpenSSL will always do it otherwise.
1924 */
1925 if (!fNoSigningTime) /** @todo requires disabling the code in do_pkcs7_signed_attrib that adds it when absent */
1926 {
1927 rcExit = SignToolPkcs7_AuthAttribsAddSigningTime(pAuthAttribs, SigningTime);
1928 if (rcExit != RTEXITCODE_SUCCESS)
1929 return rcExit;
1930 }
1931
1932 /** @todo more? Some certificate stuff? */
1933
1934 return RTEXITCODE_SUCCESS;
1935}
1936
1937
1938static RTEXITCODE SignToolPkcs7_PrependCounterSignature(PRTCRPKCS7SIGNERINFO pSignerInfo,
1939 PCRTCRPKCS7SIGNERINFO pCounterSignerInfo, unsigned cVerbosity)
1940{
1941 /* Make sure the UnauthenticatedAttributes member is there. */
1942 RTEXITCODE rcExit = SignToolPkcs7_EnsureUnauthenticatedAttributesPresent(pSignerInfo);
1943 if (rcExit != RTEXITCODE_SUCCESS)
1944 return rcExit;
1945
1946 /* Append an entry to UnauthenticatedAttributes. */
1947 uint32_t iPos;
1948 int rc = RTCrPkcs7Attributes_InsertEx(&pSignerInfo->UnauthenticatedAttributes, 0 /*iPosition*/, NULL /*pToClone*/,
1949 &g_RTAsn1DefaultAllocator, &iPos);
1950 if (RT_FAILURE(rc))
1951 return RTMsgErrorExitFailure("RTCrPkcs7Attributes_Append failed: %Rrc", rc);
1952 Assert(iPos < pSignerInfo->UnauthenticatedAttributes.cItems); Assert(iPos == 0);
1953 PRTCRPKCS7ATTRIBUTE pAttr = pSignerInfo->UnauthenticatedAttributes.papItems[iPos];
1954
1955 if (cVerbosity >= 2)
1956 RTMsgInfo("Adding UnauthenticatedAttribute #%u...", iPos);
1957
1958 /* Create the attrib and its sub-set of counter signatures. */
1959 rc = RTCrPkcs7Attribute_SetCounterSignatures(pAttr, NULL, pAttr->Allocation.pAllocator);
1960 if (RT_FAILURE(rc))
1961 return RTMsgErrorExitFailure("RTCrPkcs7Attribute_SetCounterSignatures failed: %Rrc", rc);
1962
1963 /* Insert the counter signature. */
1964 rc = RTCrPkcs7SignerInfos_InsertEx(pAttr->uValues.pCounterSignatures, 0 /*iPosition*/, pCounterSignerInfo,
1965 pAttr->Allocation.pAllocator, NULL);
1966 if (RT_FAILURE(rc))
1967 return RTMsgErrorExitFailure("RTCrPkcs7SignerInfos_InsertEx failed: %Rrc", rc);
1968
1969 return RTEXITCODE_SUCCESS;
1970}
1971
1972
1973static RTEXITCODE SignToolPkcs7_AppendCertificate(PRTCRPKCS7SIGNEDDATA pSignedData, PCRTCRX509CERTIFICATE pCertToAppend)
1974{
1975 if (pSignedData->Certificates.cItems == 0 && !RTCrPkcs7SetOfCerts_IsPresent(&pSignedData->Certificates))
1976 return RTMsgErrorExitFailure("PKCS#7 signature includes no certificates! Didn't expect that");
1977
1978 /* Already there? */
1979 PCRTCRX509CERTIFICATE pExisting
1980 = RTCrPkcs7SetOfCerts_FindX509ByIssuerAndSerialNumber(&pSignedData->Certificates, &pCertToAppend->TbsCertificate.Issuer,
1981 &pCertToAppend->TbsCertificate.SerialNumber);
1982 if (!pExisting || RTCrX509Certificate_Compare(pExisting, pCertToAppend) != 0)
1983 {
1984 /* Prepend a RTCRPKCS7CERT entry. */
1985 uint32_t iPos;
1986 int rc = RTCrPkcs7SetOfCerts_InsertEx(&pSignedData->Certificates, 0 /*iPosition*/, NULL /*pToClone*/,
1987 &g_RTAsn1DefaultAllocator, &iPos);
1988 if (RT_FAILURE(rc))
1989 return RTMsgErrorExitFailure("RTCrPkcs7SetOfCerts_Append failed: %Rrc", rc);
1990 PRTCRPKCS7CERT pCertEntry = pSignedData->Certificates.papItems[iPos];
1991
1992 /* Set (clone) the certificate. */
1993 rc = RTCrPkcs7Cert_SetX509Cert(pCertEntry, pCertToAppend, pCertEntry->Allocation.pAllocator);
1994 if (RT_FAILURE(rc))
1995 return RTMsgErrorExitFailure("RTCrPkcs7Cert_X509Cert failed: %Rrc", rc);
1996 }
1997 return RTEXITCODE_SUCCESS;
1998}
1999
2000#ifdef RT_OS_WINDOWS
2001
2002static PCRTUTF16 GetBCryptNameFromCrDigest(RTCRDIGEST hDigest)
2003{
2004 switch (RTCrDigestGetType(hDigest))
2005 {
2006 case RTDIGESTTYPE_MD2: return BCRYPT_MD2_ALGORITHM;
2007 case RTDIGESTTYPE_MD4: return BCRYPT_MD4_ALGORITHM;
2008 case RTDIGESTTYPE_SHA1: return BCRYPT_SHA1_ALGORITHM;
2009 case RTDIGESTTYPE_SHA256: return BCRYPT_SHA256_ALGORITHM;
2010 case RTDIGESTTYPE_SHA384: return BCRYPT_SHA384_ALGORITHM;
2011 case RTDIGESTTYPE_SHA512: return BCRYPT_SHA512_ALGORITHM;
2012 default:
2013 RTMsgError("No BCrypt translation for %s/%d!", RTCrDigestGetAlgorithmOid(hDigest), RTCrDigestGetType(hDigest));
2014 return L"No BCrypt translation";
2015 }
2016}
2017
2018static RTEXITCODE
2019SignToolPkcs7_Pkcs7SignStuffAgainWithReal(const char *pszWhat, SignToolKeyPair *pCertKeyPair, unsigned cVerbosity,
2020 PRTCRPKCS7CONTENTINFO pContentInfo, void **ppvSigned, size_t *pcbSigned)
2021
2022{
2023 RT_NOREF(cVerbosity);
2024
2025 /*
2026 * First remove the fake certificate from the PKCS7 structure and insert the real one.
2027 */
2028 PRTCRPKCS7SIGNEDDATA pSignedData = pContentInfo->u.pSignedData;
2029 unsigned iCert = pSignedData->Certificates.cItems;
2030 unsigned cErased = 0;
2031 while (iCert-- > 0)
2032 {
2033 PCRTCRPKCS7CERT pCert = pSignedData->Certificates.papItems[iCert];
2034 if ( pCert->enmChoice == RTCRPKCS7CERTCHOICE_X509
2035 && RTCrX509Certificate_MatchIssuerAndSerialNumber(pCert->u.pX509Cert,
2036 &pCertKeyPair->pCertificate->TbsCertificate.Issuer,
2037 &pCertKeyPair->pCertificate->TbsCertificate.SerialNumber))
2038 {
2039 RTCrPkcs7SetOfCerts_Erase(&pSignedData->Certificates, iCert);
2040 cErased++;
2041 }
2042 }
2043 if (cErased == 0)
2044 return RTMsgErrorExitFailure("(%s) Failed to find temporary signing certificate in PKCS#7 from OpenSSL: %u certs",
2045 pszWhat, pSignedData->Certificates.cItems);
2046
2047 /* Then insert the real signing certificate. */
2048 PCRTCRX509CERTIFICATE const pRealCertificate = pCertKeyPair->getRealCertificate();
2049 RTEXITCODE rcExit = SignToolPkcs7_AppendCertificate(pSignedData, pRealCertificate);
2050 if (rcExit != RTEXITCODE_SUCCESS)
2051 return rcExit;
2052
2053 /*
2054 * Modify the signer info to reflect the real certificate.
2055 */
2056 PRTCRPKCS7SIGNERINFO pSignerInfo = pSignedData->SignerInfos.papItems[0];
2057 RTCrX509Name_Delete(&pSignerInfo->IssuerAndSerialNumber.Name);
2058 int rc = RTCrX509Name_Clone(&pSignerInfo->IssuerAndSerialNumber.Name,
2059 &pRealCertificate->TbsCertificate.Issuer, &g_RTAsn1DefaultAllocator);
2060 if (RT_FAILURE(rc))
2061 return RTMsgErrorExitFailure("(%s) RTCrX509Name_Clone failed: %Rrc", pszWhat, rc);
2062
2063 RTAsn1Integer_Delete(&pSignerInfo->IssuerAndSerialNumber.SerialNumber);
2064 rc = RTAsn1Integer_Clone(&pSignerInfo->IssuerAndSerialNumber.SerialNumber,
2065 &pRealCertificate->TbsCertificate.SerialNumber, &g_RTAsn1DefaultAllocator);
2066 if (RT_FAILURE(rc))
2067 return RTMsgErrorExitFailure("(%s) RTAsn1Integer_Clone failed: %Rrc", pszWhat, rc);
2068
2069 /* There shouldn't be anything in the authenticated attributes that
2070 we need to modify... */
2071
2072 /*
2073 * Now a create a new signature using the real key. Since we haven't modified
2074 * the authenticated attributes, we can just hash them as-is.
2075 */
2076 /* Create the hash to sign. */
2077 RTCRDIGEST hDigest;
2078 rc = RTCrDigestCreateByObjId(&hDigest, &pSignerInfo->DigestAlgorithm.Algorithm);
2079 if (RT_FAILURE(rc))
2080 return RTMsgErrorExitFailure("(%s) RTCrDigestCreateByObjId failed on '%s': %Rrc",
2081 pszWhat, pSignerInfo->DigestAlgorithm.Algorithm.szObjId, rc);
2082
2083 rcExit = RTEXITCODE_FAILURE;
2084 RTERRINFOSTATIC ErrInfo;
2085 rc = RTCrPkcs7Attributes_HashAttributes(&pSignerInfo->AuthenticatedAttributes, hDigest, RTErrInfoInitStatic(&ErrInfo));
2086 if (RT_SUCCESS(rc))
2087 {
2088 BCRYPT_PKCS1_PADDING_INFO PaddingInfo = { GetBCryptNameFromCrDigest(hDigest) };
2089 DWORD cbSignature = 0;
2090 SECURITY_STATUS rcNCrypt = NCryptSignHash(pCertKeyPair->hNCryptPrivateKey, &PaddingInfo,
2091 (PBYTE)RTCrDigestGetHash(hDigest), RTCrDigestGetHashSize(hDigest),
2092 NULL, 0, &cbSignature, NCRYPT_SILENT_FLAG | BCRYPT_PAD_PKCS1);
2093 if (rcNCrypt == ERROR_SUCCESS)
2094 {
2095 if (cVerbosity)
2096 RTMsgInfo("PaddingInfo: '%ls' cb=%#x, was %#zx\n",
2097 PaddingInfo.pszAlgId, cbSignature, pSignerInfo->EncryptedDigest.Asn1Core.cb);
2098
2099 rc = RTAsn1OctetString_AllocContent(&pSignerInfo->EncryptedDigest, NULL /*pvSrc*/, cbSignature,
2100 &g_RTAsn1DefaultAllocator);
2101 if (RT_SUCCESS(rc))
2102 {
2103 Assert(pSignerInfo->EncryptedDigest.Asn1Core.uData.pv);
2104 rcNCrypt = NCryptSignHash(pCertKeyPair->hNCryptPrivateKey, &PaddingInfo,
2105 (PBYTE)RTCrDigestGetHash(hDigest), RTCrDigestGetHashSize(hDigest),
2106 (PBYTE)pSignerInfo->EncryptedDigest.Asn1Core.uData.pv, cbSignature, &cbSignature,
2107 /*NCRYPT_SILENT_FLAG |*/ BCRYPT_PAD_PKCS1);
2108 if (rcNCrypt == ERROR_SUCCESS)
2109 {
2110 /*
2111 * Now we need to re-encode the whole thing and decode it again.
2112 */
2113 PRTASN1CORE pRoot = RTCrPkcs7ContentInfo_GetAsn1Core(pContentInfo);
2114 uint32_t cbRealSigned;
2115 rc = RTAsn1EncodePrepare(pRoot, RTASN1ENCODE_F_DER, &cbRealSigned, RTErrInfoInitStatic(&ErrInfo));
2116 if (RT_SUCCESS(rc))
2117 {
2118 void *pvRealSigned = RTMemAllocZ(cbRealSigned);
2119 if (pvRealSigned)
2120 {
2121 rc = RTAsn1EncodeToBuffer(pRoot, RTASN1ENCODE_F_DER, pvRealSigned, cbRealSigned,
2122 RTErrInfoInitStatic(&ErrInfo));
2123 if (RT_SUCCESS(rc))
2124 {
2125 /* Decode it */
2126 RTCrPkcs7ContentInfo_Delete(pContentInfo);
2127
2128 RTASN1CURSORPRIMARY PrimaryCursor;
2129 RTAsn1CursorInitPrimary(&PrimaryCursor, pvRealSigned, cbRealSigned, RTErrInfoInitStatic(&ErrInfo),
2130 &g_RTAsn1DefaultAllocator, 0, pszWhat);
2131 rc = RTCrPkcs7ContentInfo_DecodeAsn1(&PrimaryCursor.Cursor, 0, pContentInfo, "CI");
2132 if (RT_SUCCESS(rc))
2133 {
2134 Assert(RTCrPkcs7ContentInfo_IsSignedData(pContentInfo));
2135
2136 /* Almost done! Just replace output buffer. */
2137 RTMemFree(*ppvSigned);
2138 *ppvSigned = pvRealSigned;
2139 *pcbSigned = cbRealSigned;
2140 pvRealSigned = NULL;
2141 rcExit = RTEXITCODE_SUCCESS;
2142 }
2143 else
2144 RTMsgError("(%s) RTCrPkcs7ContentInfo_DecodeAsn1 failed: %Rrc%#RTeim",
2145 pszWhat, rc, &ErrInfo.Core);
2146 }
2147 else
2148 RTMsgError("(%s) RTAsn1EncodeToBuffer failed: %Rrc%#RTeim", pszWhat, rc, &ErrInfo.Core);
2149
2150 RTMemFree(pvRealSigned);
2151 }
2152 else
2153 RTMsgError("(%s) Failed to allocate %u bytes!", pszWhat, cbRealSigned);
2154 }
2155 else
2156 RTMsgError("(%s) RTAsn1EncodePrepare failed: %Rrc%#RTeim", pszWhat, rc, &ErrInfo.Core);
2157 }
2158 else
2159 RTMsgError("(%s) NCryptSignHash/2 failed: %Rwc %#x (%u)", pszWhat, rcNCrypt, rcNCrypt, rcNCrypt);
2160 }
2161 else
2162 RTMsgError("(%s) RTAsn1OctetString_AllocContent(,,%#x) failed: %Rrc", pszWhat, cbSignature, rc);
2163 }
2164 else
2165 RTMsgError("(%s) NCryptSignHash/1 failed: %Rwc %#x (%u)", pszWhat, rcNCrypt, rcNCrypt, rcNCrypt);
2166 }
2167 else
2168 RTMsgError("(%s) RTCrPkcs7Attributes_HashAttributes failed: %Rrc%#RTeim", pszWhat, rc, &ErrInfo.Core);
2169 RTCrDigestRelease(hDigest);
2170 return rcExit;
2171}
2172
2173#endif /* RT_OS_WINDOWS */
2174
2175static RTEXITCODE SignToolPkcs7_Pkcs7SignStuffInner(const char *pszWhat, const void *pvToDataToSign, size_t cbToDataToSign,
2176 PCRTCRPKCS7ATTRIBUTES pAuthAttribs, RTCRSTORE hAdditionalCerts,
2177 uint32_t fExtraFlags, RTDIGESTTYPE enmDigestType,
2178 SignToolKeyPair *pCertKeyPair, unsigned cVerbosity,
2179 void **ppvSigned, size_t *pcbSigned, PRTCRPKCS7CONTENTINFO pContentInfo,
2180 PRTCRPKCS7SIGNEDDATA *ppSignedData)
2181{
2182 *ppvSigned = NULL;
2183 if (pcbSigned)
2184 *pcbSigned = 0;
2185 if (ppSignedData)
2186 *ppSignedData = NULL;
2187
2188 /* Figure out how large the signature will be. */
2189 uint32_t const fSignFlags = RTCRPKCS7SIGN_SD_F_USE_V1 | RTCRPKCS7SIGN_SD_F_NO_SMIME_CAP | fExtraFlags;
2190 size_t cbSigned = 1024;
2191 RTERRINFOSTATIC ErrInfo;
2192 int rc = RTCrPkcs7SimpleSignSignedData(fSignFlags, pCertKeyPair->pCertificate, pCertKeyPair->hPrivateKey,
2193 pvToDataToSign, cbToDataToSign,enmDigestType, hAdditionalCerts, pAuthAttribs,
2194 NULL, &cbSigned, RTErrInfoInitStatic(&ErrInfo));
2195 if (rc != VERR_BUFFER_OVERFLOW)
2196 return RTMsgErrorExitFailure("(%s) RTCrPkcs7SimpleSignSignedData failed: %Rrc%#RTeim", pszWhat, rc, &ErrInfo.Core);
2197
2198 /* Allocate memory for it and do the actual signing. */
2199 void *pvSigned = RTMemAllocZ(cbSigned);
2200 if (!pvSigned)
2201 return RTMsgErrorExitFailure("(%s) Failed to allocate %#zx bytes for %s signature", pszWhat, cbSigned, pszWhat);
2202 rc = RTCrPkcs7SimpleSignSignedData(fSignFlags, pCertKeyPair->pCertificate, pCertKeyPair->hPrivateKey,
2203 pvToDataToSign, cbToDataToSign, enmDigestType, hAdditionalCerts, pAuthAttribs,
2204 pvSigned, &cbSigned, RTErrInfoInitStatic(&ErrInfo));
2205 if (RT_SUCCESS(rc))
2206 {
2207 if (cVerbosity > 2)
2208 RTMsgInfo("%s signature: %#zx bytes\n%.*Rhxd\n", pszWhat, cbSigned, cbSigned, pvSigned);
2209
2210 /*
2211 * Decode the signature and check that it is SignedData.
2212 */
2213 RTASN1CURSORPRIMARY PrimaryCursor;
2214 RTAsn1CursorInitPrimary(&PrimaryCursor, pvSigned, (uint32_t)cbSigned, RTErrInfoInitStatic(&ErrInfo),
2215 &g_RTAsn1DefaultAllocator, 0, pszWhat);
2216 rc = RTCrPkcs7ContentInfo_DecodeAsn1(&PrimaryCursor.Cursor, 0, pContentInfo, "CI");
2217 if (RT_SUCCESS(rc))
2218 {
2219 if (RTCrPkcs7ContentInfo_IsSignedData(pContentInfo))
2220 {
2221#ifdef RT_OS_WINDOWS
2222 /*
2223 * If we're using a fake key+cert, we now have to re-do the signing using the real
2224 * key+cert and the windows crypto API. This kludge is necessary because we can't
2225 * typically get that the encoded private key, so it isn't possible to feed it to
2226 * openssl.
2227 */
2228 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
2229 if (pCertKeyPair->pCertificateReal)
2230 rcExit = SignToolPkcs7_Pkcs7SignStuffAgainWithReal(pszWhat, pCertKeyPair, cVerbosity, pContentInfo,
2231 &pvSigned, &cbSigned);
2232 if (rcExit == RTEXITCODE_SUCCESS)
2233#endif
2234 {
2235 /*
2236 * Set returns and maybe display the result before returning.
2237 */
2238 *ppvSigned = pvSigned;
2239 if (pcbSigned)
2240 *pcbSigned = cbSigned;
2241 if (ppSignedData)
2242 *ppSignedData = pContentInfo->u.pSignedData;
2243
2244 if (cVerbosity)
2245 {
2246 SHOWEXEPKCS7 ShowExe;
2247 RT_ZERO(ShowExe);
2248 ShowExe.cVerbosity = cVerbosity;
2249 HandleShowExeWorkerPkcs7Display(&ShowExe, pContentInfo->u.pSignedData, 0, pContentInfo);
2250 }
2251 return RTEXITCODE_SUCCESS;
2252 }
2253 }
2254
2255 RTMsgError("(%s) RTCrPkcs7SimpleSignSignedData did not create SignedData: %s",
2256 pszWhat, pContentInfo->ContentType.szObjId);
2257 }
2258 else
2259 RTMsgError("(%s) RTCrPkcs7ContentInfo_DecodeAsn1 failed: %Rrc%#RTeim", pszWhat, rc, &ErrInfo.Core);
2260 RTCrPkcs7ContentInfo_Delete(pContentInfo);
2261 }
2262 RTMemFree(pvSigned);
2263 return RTEXITCODE_FAILURE;
2264}
2265
2266
2267static RTEXITCODE SignToolPkcs7_Pkcs7SignStuff(const char *pszWhat, const void *pvToDataToSign, size_t cbToDataToSign,
2268 PCRTCRPKCS7ATTRIBUTES pAuthAttribs, RTCRSTORE hAdditionalCerts,
2269 uint32_t fExtraFlags, RTDIGESTTYPE enmDigestType, SignToolKeyPair *pCertKeyPair,
2270 unsigned cVerbosity, void **ppvSigned, size_t *pcbSigned,
2271 PRTCRPKCS7CONTENTINFO pContentInfo, PRTCRPKCS7SIGNEDDATA *ppSignedData)
2272{
2273 /*
2274 * Gather all additional certificates before doing the actual work.
2275 */
2276 RTCRSTORE hAllAdditionalCerts = pCertKeyPair->assembleAllAdditionalCertificates(hAdditionalCerts);
2277 if (hAllAdditionalCerts == NIL_RTCRSTORE)
2278 return RTEXITCODE_FAILURE;
2279 RTEXITCODE rcExit = SignToolPkcs7_Pkcs7SignStuffInner(pszWhat, pvToDataToSign, cbToDataToSign, pAuthAttribs,
2280 hAllAdditionalCerts, fExtraFlags, enmDigestType, pCertKeyPair,
2281 cVerbosity, ppvSigned, pcbSigned, pContentInfo, ppSignedData);
2282 RTCrStoreRelease(hAllAdditionalCerts);
2283 return rcExit;
2284}
2285
2286
2287static RTEXITCODE SignToolPkcs7_AddTimestampSignatureEx(PRTCRPKCS7SIGNERINFO pSignerInfo, PRTCRPKCS7SIGNEDDATA pSignedData,
2288 unsigned cVerbosity, bool fReplaceExisting, bool fTimestampTypeOld,
2289 RTTIMESPEC SigningTime, SignToolKeyPair *pTimestampPair)
2290{
2291 AssertReturn(fTimestampTypeOld, RTMsgErrorExitFailure("New style signatures not supported yet"));
2292
2293 /*
2294 * Create a set of attributes we need to include in the AuthenticatedAttributes
2295 * of the timestamp signature.
2296 */
2297 RTCRPKCS7ATTRIBUTES AuthAttribs;
2298 int rc = RTCrPkcs7Attributes_Init(&AuthAttribs, &g_RTAsn1DefaultAllocator);
2299 if (RT_FAILURE(rc))
2300 return RTMsgErrorExitFailure("RTCrPkcs7SetOfAttributes_Init failed: %Rrc", rc);
2301
2302 RTEXITCODE rcExit = SignToolPkcs7_AddAuthAttribsForTimestamp(&AuthAttribs, fTimestampTypeOld, SigningTime,
2303 pTimestampPair->getRealCertificate());
2304 if (rcExit == RTEXITCODE_SUCCESS)
2305 {
2306 /*
2307 * Now create a PKCS#7 signature of the encrypted signature from the selected signer info.
2308 */
2309 void *pvSigned = NULL;
2310 PRTCRPKCS7SIGNEDDATA pTsSignedData = NULL;
2311 RTCRPKCS7CONTENTINFO TsContentInfo;
2312 rcExit = SignToolPkcs7_Pkcs7SignStuffInner("timestamp", pSignerInfo->EncryptedDigest.Asn1Core.uData.pv,
2313 pSignerInfo->EncryptedDigest.Asn1Core.cb, &AuthAttribs,
2314 NIL_RTCRSTORE /*hAdditionalCerts*/, RTCRPKCS7SIGN_SD_F_DEATCHED,
2315 RTDIGESTTYPE_SHA1, pTimestampPair, cVerbosity,
2316 &pvSigned, NULL /*pcbSigned*/, &TsContentInfo, &pTsSignedData);
2317 if (rcExit == RTEXITCODE_SUCCESS)
2318 {
2319
2320 /*
2321 * If we're replacing existing timestamp signatures, remove old ones now.
2322 */
2323 if ( fReplaceExisting
2324 && RTCrPkcs7Attributes_IsPresent(&pSignerInfo->UnauthenticatedAttributes))
2325 {
2326 uint32_t iItem = pSignerInfo->UnauthenticatedAttributes.cItems;
2327 while (iItem-- > 0)
2328 {
2329 PRTCRPKCS7ATTRIBUTE pAttr = pSignerInfo->UnauthenticatedAttributes.papItems[iItem];
2330 if (pAttr->enmType == RTCRPKCS7ATTRIBUTETYPE_COUNTER_SIGNATURES) /* ASSUMES all counter sigs are timstamps */
2331 {
2332 if (cVerbosity > 1)
2333 RTMsgInfo("Removing counter signature in attribute #%u\n", iItem);
2334 rc = RTCrPkcs7Attributes_Erase(&pSignerInfo->UnauthenticatedAttributes, iItem);
2335 if (RT_FAILURE(rc))
2336 rcExit = RTMsgErrorExitFailure("RTCrPkcs7Attributes_Erase failed on #%u: %Rrc", iItem, rc);
2337 }
2338 }
2339 }
2340
2341 /*
2342 * Add the new one.
2343 */
2344 if (rcExit == RTEXITCODE_SUCCESS)
2345 rcExit = SignToolPkcs7_PrependCounterSignature(pSignerInfo, pTsSignedData->SignerInfos.papItems[0], cVerbosity);
2346
2347 /*
2348 * Make sure the signing certificate is included.
2349 */
2350 if (rcExit == RTEXITCODE_SUCCESS)
2351 {
2352 rcExit = SignToolPkcs7_AppendCertificate(pSignedData, pTimestampPair->getRealCertificate());
2353
2354 PCRTCRCERTCTX pInterCaCtx = NULL;
2355 while ((pInterCaCtx = pTimestampPair->findNextIntermediateCert(pInterCaCtx)) != NULL)
2356 if (rcExit == RTEXITCODE_SUCCESS)
2357 rcExit = SignToolPkcs7_AppendCertificate(pSignedData, pInterCaCtx->pCert);
2358 }
2359
2360 /*
2361 * Clean up.
2362 */
2363 RTCrPkcs7ContentInfo_Delete(&TsContentInfo);
2364 RTMemFree(pvSigned);
2365 }
2366 }
2367 RTCrPkcs7Attributes_Delete(&AuthAttribs);
2368 return rcExit;
2369}
2370
2371
2372static RTEXITCODE SignToolPkcs7_AddTimestampSignature(SIGNTOOLPKCS7EXE *pThis, unsigned cVerbosity, unsigned iSignature,
2373 bool fReplaceExisting, bool fTimestampTypeOld, RTTIMESPEC SigningTime,
2374 SignToolKeyPair *pTimestampPair)
2375{
2376 AssertReturn(fTimestampTypeOld, RTMsgErrorExitFailure("New style signatures not supported yet"));
2377
2378 /*
2379 * Locate the signature specified by iSignature and add a timestamp to it.
2380 */
2381 PRTCRPKCS7SIGNEDDATA pSignedData = NULL;
2382 PRTCRPKCS7SIGNERINFO pSignerInfo = SignToolPkcs7_FindNestedSignatureByIndex(pThis, iSignature, &pSignedData);
2383 if (!pSignerInfo)
2384 return RTMsgErrorExitFailure("No signature #%u in %s", iSignature, pThis->pszFilename);
2385
2386 return SignToolPkcs7_AddTimestampSignatureEx(pSignerInfo, pSignedData, cVerbosity, fReplaceExisting, fTimestampTypeOld,
2387 SigningTime, pTimestampPair);
2388}
2389
2390
2391typedef enum SIGNDATATWEAK
2392{
2393 kSignDataTweak_NoTweak = 1,
2394 kSignDataTweak_RootIsParent
2395} SIGNDATATWEAK;
2396
2397static RTEXITCODE SignToolPkcs7_SignData(SIGNTOOLPKCS7 *pThis, PRTASN1CORE pToSignRoot, SIGNDATATWEAK enmTweak,
2398 const char *pszContentTypeId, unsigned cVerbosity, uint32_t fExtraFlags,
2399 RTDIGESTTYPE enmSigType, bool fReplaceExisting, bool fNoSigningTime,
2400 SignToolKeyPair *pSigningCertKey, RTCRSTORE hAddCerts,
2401 bool fTimestampTypeOld, RTTIMESPEC SigningTime, SignToolKeyPair *pTimestampCertKey)
2402{
2403 /*
2404 * Encode it.
2405 */
2406 RTERRINFOSTATIC ErrInfo;
2407 uint32_t cbEncoded = 0;
2408 int rc = RTAsn1EncodePrepare(pToSignRoot, RTASN1ENCODE_F_DER, &cbEncoded, RTErrInfoInitStatic(&ErrInfo));
2409 if (RT_FAILURE(rc))
2410 return RTMsgErrorExitFailure("RTAsn1EncodePrepare failed: %Rrc%RTeim", rc, &ErrInfo.Core);
2411
2412 if (cVerbosity >= 4)
2413 RTAsn1Dump(pToSignRoot, 0, 0, RTStrmDumpPrintfV, g_pStdOut);
2414
2415 uint8_t *pbEncoded = (uint8_t *)RTMemTmpAllocZ(cbEncoded );
2416 if (!pbEncoded)
2417 return RTMsgErrorExitFailure("Failed to allocate %#z bytes for encoding data we're signing (%s)",
2418 cbEncoded, pszContentTypeId);
2419
2420 RTEXITCODE rcExit = RTEXITCODE_FAILURE;
2421 rc = RTAsn1EncodeToBuffer(pToSignRoot, RTASN1ENCODE_F_DER, pbEncoded, cbEncoded, RTErrInfoInitStatic(&ErrInfo));
2422 if (RT_SUCCESS(rc))
2423 {
2424 size_t const cbToSign = cbEncoded - (enmTweak == kSignDataTweak_RootIsParent ? pToSignRoot->cbHdr : 0);
2425 void const *pvToSign = pbEncoded + (enmTweak == kSignDataTweak_RootIsParent ? pToSignRoot->cbHdr : 0);
2426
2427 /*
2428 * Create additional authenticated attributes.
2429 */
2430 RTCRPKCS7ATTRIBUTES AuthAttribs;
2431 rc = RTCrPkcs7Attributes_Init(&AuthAttribs, &g_RTAsn1DefaultAllocator);
2432 if (RT_SUCCESS(rc))
2433 {
2434 rcExit = SignToolPkcs7_AddAuthAttribsForImageOrCatSignature(&AuthAttribs, SigningTime, fNoSigningTime,
2435 pszContentTypeId);
2436 if (rcExit == RTEXITCODE_SUCCESS)
2437 {
2438 /*
2439 * Ditch the old signature if so desired.
2440 * (It is okay to do this in the CAT case too, as we've already
2441 * encoded the data and won't touch pToSignRoot any more.)
2442 */
2443 pToSignRoot = NULL; /* (may become invalid if replacing) */
2444 if (fReplaceExisting && pThis->pSignedData)
2445 {
2446 RTCrPkcs7ContentInfo_Delete(&pThis->ContentInfo);
2447 pThis->pSignedData = NULL;
2448 RTMemFree(pThis->pbBuf);
2449 pThis->pbBuf = NULL;
2450 pThis->cbBuf = 0;
2451 }
2452
2453 /*
2454 * Do the actual signing.
2455 */
2456 SIGNTOOLPKCS7 Src = { RTSIGNTOOLFILETYPE_DETECT, NULL, 0, NULL };
2457 PSIGNTOOLPKCS7 pSigDst = !pThis->pSignedData ? pThis : &Src;
2458 rcExit = SignToolPkcs7_Pkcs7SignStuff("image", pvToSign, cbToSign, &AuthAttribs, hAddCerts,
2459 fExtraFlags | RTCRPKCS7SIGN_SD_F_NO_DATA_ENCAP, enmSigType /** @todo ?? */,
2460 pSigningCertKey, cVerbosity,
2461 (void **)&pSigDst->pbBuf, &pSigDst->cbBuf,
2462 &pSigDst->ContentInfo, &pSigDst->pSignedData);
2463 if (rcExit == RTEXITCODE_SUCCESS)
2464 {
2465 /*
2466 * Add a timestamp signature if requested.
2467 */
2468 if (pTimestampCertKey->isComplete())
2469 rcExit = SignToolPkcs7_AddTimestampSignatureEx(pSigDst->pSignedData->SignerInfos.papItems[0],
2470 pSigDst->pSignedData,
2471 cVerbosity, false /*fReplaceExisting*/,
2472 fTimestampTypeOld, SigningTime, pTimestampCertKey);
2473
2474 /*
2475 * Append the signature to the existing one, if that's what we're doing.
2476 */
2477 if (rcExit == RTEXITCODE_SUCCESS && pSigDst == &Src)
2478 rcExit = SignToolPkcs7_AddNestedSignature(pThis, &Src, cVerbosity, true /*fPrepend*/); /** @todo prepend/append option */
2479
2480 /* cleanup */
2481 if (pSigDst == &Src)
2482 SignToolPkcs7_Delete(&Src);
2483 }
2484
2485 }
2486 RTCrPkcs7Attributes_Delete(&AuthAttribs);
2487 }
2488 else
2489 RTMsgError("RTCrPkcs7SetOfAttributes_Init failed: %Rrc", rc);
2490 }
2491 else
2492 RTMsgError("RTAsn1EncodeToBuffer failed: %Rrc", rc);
2493 RTMemTmpFree(pbEncoded);
2494 return rcExit;
2495}
2496
2497
2498static RTEXITCODE SignToolPkcs7_SpcCompleteWithoutPageHashes(RTCRSPCINDIRECTDATACONTENT *pSpcIndData)
2499{
2500 PCRTASN1ALLOCATORVTABLE const pAllocator = &g_RTAsn1DefaultAllocator;
2501 PRTCRSPCPEIMAGEDATA const pPeImage = pSpcIndData->Data.uValue.pPeImage;
2502 Assert(pPeImage);
2503
2504 /*
2505 * Set it to File with an empty name.
2506 * RTCRSPCPEIMAGEDATA::Flags -vv
2507 * RTCRSPCPEIMAGEDATA::SeqCore -vv T0 -vv vv- pT2/CtxTag2
2508 * 0040: 04 01 82 37 02 01 0f 30-09 03 01 00 a0 04 a2 02 ...7...0........
2509 * 0050: 80 00 30 21 30 09 06 05-2b 0e 03 02 1a 05 00 04 ..0!0...+.......
2510 * ^^- pUcs2 / empty string
2511 */
2512
2513 /* Create an empty BMP string. */
2514 RTASN1STRING EmptyStr;
2515 int rc = RTAsn1BmpString_Init(&EmptyStr, pAllocator);
2516 if (RT_FAILURE(rc))
2517 return RTMsgErrorExitFailure("RTAsn1BmpString_Init/Ucs2 failed: %Rrc", rc);
2518
2519 /* Create an SPC string and use the above empty string with the Ucs2 setter. */
2520 RTEXITCODE rcExit = RTEXITCODE_FAILURE;
2521 RTCRSPCSTRING SpcString;
2522 rc = RTCrSpcString_Init(&SpcString, pAllocator);
2523 if (RT_SUCCESS(rc))
2524 {
2525 rc = RTCrSpcString_SetUcs2(&SpcString, &EmptyStr, pAllocator);
2526 if (RT_SUCCESS(rc))
2527 {
2528 /* Create a temporary SpcLink with the empty SpcString. */
2529 RTCRSPCLINK SpcLink;
2530 rc = RTCrSpcLink_Init(&SpcLink, pAllocator);
2531 if (RT_SUCCESS(rc))
2532 {
2533 /* Use the setter on the SpcLink object to copy the SpcString to it. */
2534 rc = RTCrSpcLink_SetFile(&SpcLink, &SpcString, pAllocator);
2535 if (RT_SUCCESS(rc))
2536 {
2537 /* Use the setter to copy SpcLink to the PeImage structure. */
2538 rc = RTCrSpcPeImageData_SetFile(pPeImage, &SpcLink, pAllocator);
2539 if (RT_SUCCESS(rc))
2540 rcExit = RTEXITCODE_SUCCESS;
2541 else
2542 RTMsgError("RTCrSpcPeImageData_SetFile failed: %Rrc", rc);
2543 }
2544 else
2545 RTMsgError("RTCrSpcLink_SetFile failed: %Rrc", rc);
2546 RTCrSpcLink_Delete(&SpcLink);
2547 }
2548 else
2549 RTMsgError("RTCrSpcLink_Init failed: %Rrc", rc);
2550 }
2551 else
2552 RTMsgError("RTCrSpcString_SetUcs2 failed: %Rrc", rc);
2553 RTCrSpcString_Delete(&SpcString);
2554 }
2555 else
2556 RTMsgError("RTCrSpcString_Init failed: %Rrc", rc);
2557 RTAsn1BmpString_Delete(&EmptyStr);
2558 return rcExit;
2559}
2560
2561
2562static RTEXITCODE SignToolPkcs7_SpcAddImagePageHashes(SIGNTOOLPKCS7EXE *pThis, RTCRSPCINDIRECTDATACONTENT *pSpcIndData,
2563 RTDIGESTTYPE enmSigType)
2564{
2565 PCRTASN1ALLOCATORVTABLE const pAllocator = &g_RTAsn1DefaultAllocator;
2566 PRTCRSPCPEIMAGEDATA const pPeImage = pSpcIndData->Data.uValue.pPeImage;
2567 Assert(pPeImage);
2568
2569 /*
2570 * The hashes are stored in the 'Moniker' attribute.
2571 */
2572 /* Create a temporary SpcLink with a default moniker. */
2573 RTCRSPCLINK SpcLink;
2574 int rc = RTCrSpcLink_Init(&SpcLink, pAllocator);
2575 if (RT_FAILURE(rc))
2576 return RTMsgErrorExitFailure("RTCrSpcLink_Init failed: %Rrc", rc);
2577 rc = RTCrSpcLink_SetMoniker(&SpcLink, NULL, pAllocator);
2578 if (RT_SUCCESS(rc))
2579 {
2580 /* Use the setter to copy SpcLink to the PeImage structure. */
2581 rc = RTCrSpcPeImageData_SetFile(pPeImage, &SpcLink, pAllocator);
2582 if (RT_FAILURE(rc))
2583 RTMsgError("RTCrSpcLink_SetFile failed: %Rrc", rc);
2584 }
2585 else
2586 RTMsgError("RTCrSpcLink_SetMoniker failed: %Rrc", rc);
2587 RTCrSpcLink_Delete(&SpcLink);
2588 if (RT_FAILURE(rc))
2589 return RTEXITCODE_FAILURE;
2590
2591 /*
2592 * Now go to work on the moniker. It doesn't have any autogenerated
2593 * setters, so we must do stuff manually.
2594 */
2595 PRTCRSPCSERIALIZEDOBJECT pMoniker = pPeImage->T0.File.u.pMoniker;
2596 RTUUID Uuid;
2597 rc = RTUuidFromStr(&Uuid, RTCRSPCSERIALIZEDOBJECT_UUID_STR);
2598 if (RT_FAILURE(rc))
2599 return RTMsgErrorExitFailure("RTUuidFromStr failed: %Rrc", rc);
2600
2601 rc = RTAsn1OctetString_AllocContent(&pMoniker->Uuid, &Uuid, sizeof(Uuid), pAllocator);
2602 if (RT_FAILURE(rc))
2603 return RTMsgErrorExitFailure("RTAsn1String_InitWithValue/UUID failed: %Rrc", rc);
2604
2605 /* Create a new set of attributes and associate this with the SerializedData member. */
2606 PRTCRSPCSERIALIZEDOBJECTATTRIBUTES pSpcAttribs;
2607 rc = RTAsn1MemAllocZ(&pMoniker->SerializedData.EncapsulatedAllocation,
2608 (void **)&pSpcAttribs, sizeof(*pSpcAttribs));
2609 if (RT_FAILURE(rc))
2610 return RTMsgErrorExitFailure("RTAsn1MemAllocZ/pSpcAttribs failed: %Rrc", rc);
2611 pMoniker->SerializedData.pEncapsulated = RTCrSpcSerializedObjectAttributes_GetAsn1Core(pSpcAttribs);
2612 pMoniker->enmType = RTCRSPCSERIALIZEDOBJECTTYPE_ATTRIBUTES;
2613 pMoniker->u.pData = pSpcAttribs;
2614
2615 rc = RTCrSpcSerializedObjectAttributes_Init(pSpcAttribs, pAllocator);
2616 if (RT_FAILURE(rc))
2617 return RTMsgErrorExitFailure("RTCrSpcSerializedObjectAttributes_Init failed: %Rrc", rc);
2618
2619 /*
2620 * Add a single attribute to the set that we'll use for page hashes.
2621 */
2622 int32_t iPos = RTCrSpcSerializedObjectAttributes_Append(pSpcAttribs);
2623 if (iPos < 0)
2624 return RTMsgErrorExitFailure("RTCrSpcSerializedObjectAttributes_Append failed: %Rrc", iPos);
2625 PRTCRSPCSERIALIZEDOBJECTATTRIBUTE pSpcObjAttr = pSpcAttribs->papItems[iPos];
2626
2627 if (enmSigType == RTDIGESTTYPE_SHA1)
2628 rc = RTCrSpcSerializedObjectAttribute_SetV1Hashes(pSpcObjAttr, NULL, pAllocator);
2629 else if (enmSigType == RTDIGESTTYPE_SHA256)
2630 rc = RTCrSpcSerializedObjectAttribute_SetV2Hashes(pSpcObjAttr, NULL, pAllocator);
2631 else
2632 rc = VERR_CR_DIGEST_NOT_SUPPORTED;
2633 if (RT_FAILURE(rc))
2634 return RTMsgErrorExitFailure("RTCrSpcSerializedObjectAttribute_SetV1Hashes/SetV2Hashes failed: %Rrc", rc);
2635 PRTCRSPCSERIALIZEDPAGEHASHES pSpcPageHashes = pSpcObjAttr->u.pPageHashes;
2636 Assert(pSpcPageHashes);
2637
2638 /*
2639 * Now ask the loader for the number of pages in the page hash table
2640 * and calculate its size.
2641 */
2642 uint32_t cPages = 0;
2643 rc = RTLdrQueryPropEx(pThis->hLdrMod, RTLDRPROP_HASHABLE_PAGES, NULL, &cPages, sizeof(cPages), NULL);
2644 if (RT_FAILURE(rc))
2645 return RTMsgErrorExitFailure("RTLdrQueryPropEx/RTLDRPROP_HASHABLE_PAGES failed: %Rrc", rc);
2646
2647 uint32_t const cbHash = RTCrDigestTypeToHashSize(enmSigType);
2648 AssertReturn(cbHash > 0, RTMsgErrorExitFailure("Invalid value: enmSigType=%d", enmSigType));
2649 uint32_t const cbTable = (sizeof(uint32_t) + cbHash) * cPages;
2650
2651 /*
2652 * Allocate memory in the octect string.
2653 */
2654 rc = RTAsn1ContentAllocZ(&pSpcPageHashes->RawData.Asn1Core, cbTable, pAllocator);
2655 if (RT_FAILURE(rc))
2656 return RTMsgErrorExitFailure("RTAsn1ContentAllocZ failed to allocate %#x bytes for page hashes: %Rrc", cbTable, rc);
2657 pSpcPageHashes->pData = (PCRTCRSPCPEIMAGEPAGEHASHES)pSpcPageHashes->RawData.Asn1Core.uData.pu8;
2658
2659 RTLDRPROP enmLdrProp;
2660 switch (enmSigType)
2661 {
2662 case RTDIGESTTYPE_SHA1: enmLdrProp = RTLDRPROP_SHA1_PAGE_HASHES; break;
2663 case RTDIGESTTYPE_SHA256: enmLdrProp = RTLDRPROP_SHA256_PAGE_HASHES; break;
2664 default: AssertFailedReturn(RTMsgErrorExitFailure("Invalid value: enmSigType=%d", enmSigType));
2665
2666 }
2667 rc = RTLdrQueryPropEx(pThis->hLdrMod, enmLdrProp, NULL, (void *)pSpcPageHashes->RawData.Asn1Core.uData.pv, cbTable, NULL);
2668 if (RT_FAILURE(rc))
2669 return RTMsgErrorExitFailure("RTLdrQueryPropEx/RTLDRPROP_SHA?_PAGE_HASHES/%#x failed: %Rrc", cbTable, rc);
2670
2671 return RTEXITCODE_SUCCESS;
2672}
2673
2674
2675static RTEXITCODE SignToolPkcs7_SpcAddImageHash(SIGNTOOLPKCS7EXE *pThis, RTCRSPCINDIRECTDATACONTENT *pSpcIndData,
2676 RTDIGESTTYPE enmSigType)
2677{
2678 uint32_t const cbHash = RTCrDigestTypeToHashSize(enmSigType);
2679 const char * const pszAlgId = RTCrDigestTypeToAlgorithmOid(enmSigType);
2680
2681 /*
2682 * Ask the loader for the hash.
2683 */
2684 uint8_t abHash[RTSHA512_HASH_SIZE];
2685 int rc = RTLdrHashImage(pThis->hLdrMod, enmSigType, abHash, sizeof(abHash));
2686 if (RT_FAILURE(rc))
2687 return RTMsgErrorExitFailure("RTLdrHashImage/%s failed: %Rrc", RTCrDigestTypeToName(enmSigType), rc);
2688
2689 /*
2690 * Set it.
2691 */
2692 /** @todo no setter, this should be okay, though... */
2693 rc = RTAsn1ObjId_InitFromString(&pSpcIndData->DigestInfo.DigestAlgorithm.Algorithm, pszAlgId, &g_RTAsn1DefaultAllocator);
2694 if (RT_FAILURE(rc))
2695 return RTMsgErrorExitFailure("RTAsn1ObjId_InitFromString/%s failed: %Rrc", pszAlgId, rc);
2696 RTAsn1DynType_SetToNull(&pSpcIndData->DigestInfo.DigestAlgorithm.Parameters); /* ASSUMES RSA or similar */
2697
2698 rc = RTAsn1ContentDup(&pSpcIndData->DigestInfo.Digest.Asn1Core, abHash, cbHash, &g_RTAsn1DefaultAllocator);
2699 if (RT_FAILURE(rc))
2700 return RTMsgErrorExitFailure("RTAsn1ContentDup/%#x failed: %Rrc", cbHash, rc);
2701
2702 return RTEXITCODE_SUCCESS;
2703}
2704
2705
2706static RTEXITCODE SignToolPkcs7_AddOrReplaceSignature(SIGNTOOLPKCS7EXE *pThis, unsigned cVerbosity, RTDIGESTTYPE enmSigType,
2707 bool fReplaceExisting, bool fHashPages, bool fNoSigningTime,
2708 SignToolKeyPair *pSigningCertKey,
2709 RTCRSTORE hAddCerts, bool fTimestampTypeOld,
2710 RTTIMESPEC SigningTime, SignToolKeyPair *pTimestampCertKey)
2711{
2712 AssertReturn(fTimestampTypeOld || pTimestampCertKey->isNull(),
2713 RTMsgErrorExitFailure("New style signatures not supported yet"));
2714
2715 /*
2716 * We must construct the data to be packed into the PKCS#7 signature
2717 * and signed.
2718 */
2719 PCRTASN1ALLOCATORVTABLE const pAllocator = &g_RTAsn1DefaultAllocator;
2720 RTCRSPCINDIRECTDATACONTENT SpcIndData;
2721 int rc = RTCrSpcIndirectDataContent_Init(&SpcIndData, pAllocator);
2722 if (RT_FAILURE(rc))
2723 return RTMsgErrorExitFailure("RTCrSpcIndirectDataContent_Init failed: %Rrc", rc);
2724
2725 /* Set the data to PE image. */
2726 /** @todo Generalize the Type + enmType DYN stuff and generate setters. */
2727 Assert(SpcIndData.Data.enmType == RTCRSPCAAOVTYPE_NOT_PRESENT);
2728 Assert(SpcIndData.Data.uValue.pPeImage == NULL);
2729 RTEXITCODE rcExit;
2730 rc = RTAsn1ObjId_SetFromString(&SpcIndData.Data.Type, RTCRSPCPEIMAGEDATA_OID, pAllocator);
2731 if (RT_SUCCESS(rc))
2732 {
2733 SpcIndData.Data.enmType = RTCRSPCAAOVTYPE_PE_IMAGE_DATA;
2734 rc = RTAsn1MemAllocZ(&SpcIndData.Data.Allocation, (void **)&SpcIndData.Data.uValue.pPeImage,
2735 sizeof(*SpcIndData.Data.uValue.pPeImage));
2736 if (RT_SUCCESS(rc))
2737 {
2738 rc = RTCrSpcPeImageData_Init(SpcIndData.Data.uValue.pPeImage, pAllocator);
2739 if (RT_SUCCESS(rc))
2740 {
2741 /* Old (SHA1) signatures has a Flags member, it's zero bits, though. */
2742 if (enmSigType == RTDIGESTTYPE_SHA1)
2743 {
2744 uint8_t bFlags = 0;
2745 RTASN1BITSTRING Flags;
2746 rc = RTAsn1BitString_InitWithData(&Flags, &bFlags, 0, pAllocator);
2747 if (RT_SUCCESS(rc))
2748 {
2749 rc = RTCrSpcPeImageData_SetFlags(SpcIndData.Data.uValue.pPeImage, &Flags, pAllocator);
2750 RTAsn1BitString_Delete(&Flags);
2751 if (RT_FAILURE(rc))
2752 rcExit = RTMsgErrorExitFailure("RTCrSpcPeImageData_SetFlags failed: %Rrc", rc);
2753 }
2754 else
2755 rcExit = RTMsgErrorExitFailure("RTAsn1BitString_InitWithData failed: %Rrc", rc);
2756 }
2757
2758 /*
2759 * Add the hashes.
2760 */
2761 rcExit = SignToolPkcs7_SpcAddImageHash(pThis, &SpcIndData, enmSigType);
2762 if (rcExit == RTEXITCODE_SUCCESS)
2763 {
2764 if (fHashPages)
2765 rcExit = SignToolPkcs7_SpcAddImagePageHashes(pThis, &SpcIndData, enmSigType);
2766 else
2767 rcExit = SignToolPkcs7_SpcCompleteWithoutPageHashes(&SpcIndData);
2768
2769 /*
2770 * Encode and sign the SPC data, timestamp it, and line it up for adding to the executable.
2771 */
2772 if (rcExit == RTEXITCODE_SUCCESS)
2773 rcExit = SignToolPkcs7_SignData(pThis, RTCrSpcIndirectDataContent_GetAsn1Core(&SpcIndData),
2774 kSignDataTweak_NoTweak, RTCRSPCINDIRECTDATACONTENT_OID, cVerbosity, 0,
2775 enmSigType, fReplaceExisting, fNoSigningTime, pSigningCertKey, hAddCerts,
2776 fTimestampTypeOld, SigningTime, pTimestampCertKey);
2777 }
2778 }
2779 else
2780 rcExit = RTMsgErrorExitFailure("RTCrPkcs7SignerInfos_Init failed: %Rrc", rc);
2781 }
2782 else
2783 rcExit = RTMsgErrorExitFailure("RTAsn1MemAllocZ failed for RTCRSPCPEIMAGEDATA: %Rrc", rc);
2784 }
2785 else
2786 rcExit = RTMsgErrorExitFailure("RTAsn1ObjId_SetWithString/SpcPeImageData failed: %Rrc", rc);
2787
2788 RTCrSpcIndirectDataContent_Delete(&SpcIndData);
2789 return rcExit;
2790}
2791
2792
2793static RTEXITCODE SignToolPkcs7_AddOrReplaceCatSignature(SIGNTOOLPKCS7 *pThis, unsigned cVerbosity, RTDIGESTTYPE enmSigType,
2794 bool fReplaceExisting, bool fNoSigningTime,
2795 SignToolKeyPair *pSigningCertKey,
2796 RTCRSTORE hAddCerts, bool fTimestampTypeOld,
2797 RTTIMESPEC SigningTime, SignToolKeyPair *pTimestampCertKey)
2798{
2799 AssertReturn(fTimestampTypeOld || pTimestampCertKey->isNull(),
2800 RTMsgErrorExitFailure("New style signatures not supported yet"));
2801 AssertReturn(pThis->pSignedData, RTMsgErrorExitFailure("pSignedData is NULL!"));
2802
2803 /*
2804 * Figure out what to sign first.
2805 */
2806 uint32_t fExtraFlags = 0;
2807 PRTASN1CORE pToSign = &pThis->pSignedData->ContentInfo.Content.Asn1Core;
2808 const char *pszType = pThis->pSignedData->ContentInfo.ContentType.szObjId;
2809
2810 if (!fReplaceExisting && pThis->pSignedData->SignerInfos.cItems == 0)
2811 fReplaceExisting = true;
2812 if (!fReplaceExisting)
2813 {
2814 pszType = RTCR_PKCS7_DATA_OID;
2815 fExtraFlags |= RTCRPKCS7SIGN_SD_F_DEATCHED;
2816 }
2817
2818 /*
2819 * Do the signing.
2820 */
2821 RTEXITCODE rcExit = SignToolPkcs7_SignData(pThis, pToSign, kSignDataTweak_RootIsParent,
2822 pszType, cVerbosity, fExtraFlags, enmSigType, fReplaceExisting,
2823 fNoSigningTime, pSigningCertKey, hAddCerts,
2824 fTimestampTypeOld, SigningTime, pTimestampCertKey);
2825
2826 /* probably need to clean up stuff related to nested signatures here later... */
2827 return rcExit;
2828}
2829
2830#endif /* !IPRT_SIGNTOOL_NO_SIGNING */
2831
2832
2833/*********************************************************************************************************************************
2834* The 'extract-exe-signer-cert' command. *
2835*********************************************************************************************************************************/
2836
2837static RTEXITCODE HelpExtractExeSignerCert(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
2838{
2839 RT_NOREF_PV(enmLevel);
2840 RTStrmWrappedPrintf(pStrm, RTSTRMWRAPPED_F_HANGING_INDENT,
2841 "extract-exe-signer-cert [--ber|--cer|--der] [--signature-index|-i <num>] [--exe|-e] <exe> [--output|-o] <outfile.cer>\n");
2842 return RTEXITCODE_SUCCESS;
2843}
2844
2845static RTEXITCODE HandleExtractExeSignerCert(int cArgs, char **papszArgs)
2846{
2847 /*
2848 * Parse arguments.
2849 */
2850 static const RTGETOPTDEF s_aOptions[] =
2851 {
2852 { "--ber", 'b', RTGETOPT_REQ_NOTHING },
2853 { "--cer", 'c', RTGETOPT_REQ_NOTHING },
2854 { "--der", 'd', RTGETOPT_REQ_NOTHING },
2855 { "--exe", 'e', RTGETOPT_REQ_STRING },
2856 { "--output", 'o', RTGETOPT_REQ_STRING },
2857 { "--signature-index", 'i', RTGETOPT_REQ_UINT32 },
2858 { "--force", 'f', RTGETOPT_REQ_NOTHING },
2859 };
2860
2861 const char *pszExe = NULL;
2862 const char *pszOut = NULL;
2863 RTLDRARCH enmLdrArch = RTLDRARCH_WHATEVER;
2864 unsigned cVerbosity = 0;
2865 uint32_t fCursorFlags = RTASN1CURSOR_FLAGS_DER;
2866 uint32_t iSignature = 0;
2867 bool fForce = false;
2868
2869 RTGETOPTSTATE GetState;
2870 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
2871 AssertRCReturn(rc, RTEXITCODE_FAILURE);
2872 RTGETOPTUNION ValueUnion;
2873 int ch;
2874 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
2875 {
2876 switch (ch)
2877 {
2878 case 'e': pszExe = ValueUnion.psz; break;
2879 case 'o': pszOut = ValueUnion.psz; break;
2880 case 'b': fCursorFlags = 0; break;
2881 case 'c': fCursorFlags = RTASN1CURSOR_FLAGS_CER; break;
2882 case 'd': fCursorFlags = RTASN1CURSOR_FLAGS_DER; break;
2883 case 'f': fForce = true; break;
2884 case 'i': iSignature = ValueUnion.u32; break;
2885 case 'V': return HandleVersion(cArgs, papszArgs);
2886 case 'h': return HelpExtractExeSignerCert(g_pStdOut, RTSIGNTOOLHELP_FULL);
2887
2888 case VINF_GETOPT_NOT_OPTION:
2889 if (!pszExe)
2890 pszExe = ValueUnion.psz;
2891 else if (!pszOut)
2892 pszOut = ValueUnion.psz;
2893 else
2894 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Too many file arguments: %s", ValueUnion.psz);
2895 break;
2896
2897 default:
2898 return RTGetOptPrintError(ch, &ValueUnion);
2899 }
2900 }
2901 if (!pszExe)
2902 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No executable given.");
2903 if (!pszOut)
2904 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No output file given.");
2905 if (!fForce && RTPathExists(pszOut))
2906 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The output file '%s' exists.", pszOut);
2907
2908 /*
2909 * Do it.
2910 */
2911 /* Read & decode the PKCS#7 signature. */
2912 SIGNTOOLPKCS7EXE This;
2913 RTEXITCODE rcExit = SignToolPkcs7Exe_InitFromFile(&This, pszExe, cVerbosity, enmLdrArch);
2914 if (rcExit == RTEXITCODE_SUCCESS)
2915 {
2916 /* Find the signing certificate (ASSUMING that the certificate used is shipped in the set of certificates). */
2917 PRTCRPKCS7SIGNEDDATA pSignedData;
2918 PCRTCRPKCS7SIGNERINFO pSignerInfo = SignToolPkcs7_FindNestedSignatureByIndex(&This, iSignature, &pSignedData);
2919 rcExit = RTEXITCODE_FAILURE;
2920 if (pSignerInfo)
2921 {
2922 PCRTCRPKCS7ISSUERANDSERIALNUMBER pISN = &pSignedData->SignerInfos.papItems[0]->IssuerAndSerialNumber;
2923 PCRTCRX509CERTIFICATE pCert;
2924 pCert = RTCrPkcs7SetOfCerts_FindX509ByIssuerAndSerialNumber(&pSignedData->Certificates,
2925 &pISN->Name, &pISN->SerialNumber);
2926 if (pCert)
2927 {
2928 /*
2929 * Write it out.
2930 */
2931 RTFILE hFile;
2932 rc = RTFileOpen(&hFile, pszOut,
2933 RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | (fForce ? RTFILE_O_CREATE_REPLACE : RTFILE_O_CREATE));
2934 if (RT_SUCCESS(rc))
2935 {
2936 uint32_t cbCert = pCert->SeqCore.Asn1Core.cbHdr + pCert->SeqCore.Asn1Core.cb;
2937 rc = RTFileWrite(hFile, pCert->SeqCore.Asn1Core.uData.pu8 - pCert->SeqCore.Asn1Core.cbHdr,
2938 cbCert, NULL);
2939 if (RT_SUCCESS(rc))
2940 {
2941 rc = RTFileClose(hFile);
2942 if (RT_SUCCESS(rc))
2943 {
2944 hFile = NIL_RTFILE;
2945 rcExit = RTEXITCODE_SUCCESS;
2946 RTMsgInfo("Successfully wrote %u bytes to '%s'", cbCert, pszOut);
2947 }
2948 else
2949 RTMsgError("RTFileClose failed: %Rrc", rc);
2950 }
2951 else
2952 RTMsgError("RTFileWrite failed: %Rrc", rc);
2953 RTFileClose(hFile);
2954 }
2955 else
2956 RTMsgError("Error opening '%s' for writing: %Rrc", pszOut, rc);
2957 }
2958 else
2959 RTMsgError("Certificate not found.");
2960 }
2961 else
2962 RTMsgError("Could not locate signature #%u!", iSignature);
2963
2964 /* Delete the signature data. */
2965 SignToolPkcs7Exe_Delete(&This);
2966 }
2967 return rcExit;
2968}
2969
2970
2971/*********************************************************************************************************************************
2972* The 'extract-exe-signature' command. *
2973*********************************************************************************************************************************/
2974
2975static RTEXITCODE HelpExtractExeSignature(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
2976{
2977 RT_NOREF_PV(enmLevel);
2978 RTStrmWrappedPrintf(pStrm, RTSTRMWRAPPED_F_HANGING_INDENT,
2979 "extract-exe-signerature [--exe|-e] <exe> [--output|-o] <outfile.pkcs7>\n");
2980 return RTEXITCODE_SUCCESS;
2981}
2982
2983static RTEXITCODE HandleExtractExeSignature(int cArgs, char **papszArgs)
2984{
2985 /*
2986 * Parse arguments.
2987 */
2988 static const RTGETOPTDEF s_aOptions[] =
2989 {
2990 { "--exe", 'e', RTGETOPT_REQ_STRING },
2991 { "--output", 'o', RTGETOPT_REQ_STRING },
2992 { "--force", 'f', RTGETOPT_REQ_NOTHING },
2993 };
2994
2995 const char *pszExe = NULL;
2996 const char *pszOut = NULL;
2997 RTLDRARCH enmLdrArch = RTLDRARCH_WHATEVER;
2998 unsigned cVerbosity = 0;
2999 bool fForce = false;
3000
3001 RTGETOPTSTATE GetState;
3002 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
3003 AssertRCReturn(rc, RTEXITCODE_FAILURE);
3004 RTGETOPTUNION ValueUnion;
3005 int ch;
3006 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
3007 {
3008 switch (ch)
3009 {
3010 case 'e': pszExe = ValueUnion.psz; break;
3011 case 'o': pszOut = ValueUnion.psz; break;
3012 case 'f': fForce = true; break;
3013 case 'V': return HandleVersion(cArgs, papszArgs);
3014 case 'h': return HelpExtractExeSignerCert(g_pStdOut, RTSIGNTOOLHELP_FULL);
3015
3016 case VINF_GETOPT_NOT_OPTION:
3017 if (!pszExe)
3018 pszExe = ValueUnion.psz;
3019 else if (!pszOut)
3020 pszOut = ValueUnion.psz;
3021 else
3022 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Too many file arguments: %s", ValueUnion.psz);
3023 break;
3024
3025 default:
3026 return RTGetOptPrintError(ch, &ValueUnion);
3027 }
3028 }
3029 if (!pszExe)
3030 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No executable given.");
3031 if (!pszOut)
3032 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No output file given.");
3033 if (!fForce && RTPathExists(pszOut))
3034 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The output file '%s' exists.", pszOut);
3035
3036 /*
3037 * Do it.
3038 */
3039 /* Read & decode the PKCS#7 signature. */
3040 SIGNTOOLPKCS7EXE This;
3041 RTEXITCODE rcExit = SignToolPkcs7Exe_InitFromFile(&This, pszExe, cVerbosity, enmLdrArch);
3042 if (rcExit == RTEXITCODE_SUCCESS)
3043 {
3044 /*
3045 * Write out the PKCS#7 signature.
3046 */
3047 RTFILE hFile;
3048 rc = RTFileOpen(&hFile, pszOut,
3049 RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | (fForce ? RTFILE_O_CREATE_REPLACE : RTFILE_O_CREATE));
3050 if (RT_SUCCESS(rc))
3051 {
3052 rc = RTFileWrite(hFile, This.pbBuf, This.cbBuf, NULL);
3053 if (RT_SUCCESS(rc))
3054 {
3055 rc = RTFileClose(hFile);
3056 if (RT_SUCCESS(rc))
3057 {
3058 hFile = NIL_RTFILE;
3059 RTMsgInfo("Successfully wrote %u bytes to '%s'", This.cbBuf, pszOut);
3060 rcExit = RTEXITCODE_SUCCESS;
3061 }
3062 else
3063 RTMsgError("RTFileClose failed: %Rrc", rc);
3064 }
3065 else
3066 RTMsgError("RTFileWrite failed: %Rrc", rc);
3067 RTFileClose(hFile);
3068 }
3069 else
3070 RTMsgError("Error opening '%s' for writing: %Rrc", pszOut, rc);
3071
3072 /* Delete the signature data. */
3073 SignToolPkcs7Exe_Delete(&This);
3074 }
3075 return rcExit;
3076}
3077
3078
3079/*********************************************************************************************************************************
3080* The 'add-nested-exe-signature' command. *
3081*********************************************************************************************************************************/
3082
3083static RTEXITCODE HelpAddNestedExeSignature(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
3084{
3085 RT_NOREF_PV(enmLevel);
3086 RTStrmWrappedPrintf(pStrm, RTSTRMWRAPPED_F_HANGING_INDENT,
3087 "add-nested-exe-signature [-v|--verbose] [-d|--debug] [-p|--prepend] <destination-exe> <source-exe>\n");
3088 if (enmLevel == RTSIGNTOOLHELP_FULL)
3089 RTStrmWrappedPrintf(pStrm, 0,
3090 "\n"
3091 "The --debug option allows the source-exe to be omitted in order to test the "
3092 "encoding and PE file modification.\n"
3093 "\n"
3094 "The --prepend option puts the nested signature first rather than appending it "
3095 "to the end of of the nested signature set. Windows reads nested signatures in "
3096 "reverse order, so --prepend will logically putting it last.\n");
3097 return RTEXITCODE_SUCCESS;
3098}
3099
3100
3101static RTEXITCODE HandleAddNestedExeSignature(int cArgs, char **papszArgs)
3102{
3103 /*
3104 * Parse arguments.
3105 */
3106 static const RTGETOPTDEF s_aOptions[] =
3107 {
3108 { "--prepend", 'p', RTGETOPT_REQ_NOTHING },
3109 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
3110 { "--debug", 'd', RTGETOPT_REQ_NOTHING },
3111 };
3112
3113 const char *pszDst = NULL;
3114 const char *pszSrc = NULL;
3115 unsigned cVerbosity = 0;
3116 bool fDebug = false;
3117 bool fPrepend = false;
3118
3119 RTGETOPTSTATE GetState;
3120 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
3121 AssertRCReturn(rc, RTEXITCODE_FAILURE);
3122 RTGETOPTUNION ValueUnion;
3123 int ch;
3124 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
3125 {
3126 switch (ch)
3127 {
3128 case 'v': cVerbosity++; break;
3129 case 'd': fDebug = pszSrc == NULL; break;
3130 case 'p': fPrepend = true; break;
3131 case 'V': return HandleVersion(cArgs, papszArgs);
3132 case 'h': return HelpAddNestedExeSignature(g_pStdOut, RTSIGNTOOLHELP_FULL);
3133
3134 case VINF_GETOPT_NOT_OPTION:
3135 if (!pszDst)
3136 pszDst = ValueUnion.psz;
3137 else if (!pszSrc)
3138 {
3139 pszSrc = ValueUnion.psz;
3140 fDebug = false;
3141 }
3142 else
3143 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Too many file arguments: %s", ValueUnion.psz);
3144 break;
3145
3146 default:
3147 return RTGetOptPrintError(ch, &ValueUnion);
3148 }
3149 }
3150 if (!pszDst)
3151 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No destination executable given.");
3152 if (!pszSrc && !fDebug)
3153 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No source executable file given.");
3154
3155 /*
3156 * Do it.
3157 */
3158 /* Read & decode the source PKCS#7 signature. */
3159 SIGNTOOLPKCS7EXE Src;
3160 RTEXITCODE rcExit = pszSrc ? SignToolPkcs7Exe_InitFromFile(&Src, pszSrc, cVerbosity) : RTEXITCODE_SUCCESS;
3161 if (rcExit == RTEXITCODE_SUCCESS)
3162 {
3163 /* Ditto for the destination PKCS#7 signature. */
3164 SIGNTOOLPKCS7EXE Dst;
3165 rcExit = SignToolPkcs7Exe_InitFromFile(&Dst, pszDst, cVerbosity);
3166 if (rcExit == RTEXITCODE_SUCCESS)
3167 {
3168 /* Do the signature manipulation. */
3169 if (pszSrc)
3170 rcExit = SignToolPkcs7_AddNestedSignature(&Dst, &Src, cVerbosity, fPrepend);
3171 if (rcExit == RTEXITCODE_SUCCESS)
3172 rcExit = SignToolPkcs7_Encode(&Dst, cVerbosity);
3173
3174 /* Update the destination executable file. */
3175 if (rcExit == RTEXITCODE_SUCCESS)
3176 rcExit = SignToolPkcs7Exe_WriteSignatureToFile(&Dst, cVerbosity);
3177
3178 SignToolPkcs7Exe_Delete(&Dst);
3179 }
3180 if (pszSrc)
3181 SignToolPkcs7Exe_Delete(&Src);
3182 }
3183
3184 return rcExit;
3185}
3186
3187
3188/*********************************************************************************************************************************
3189* The 'add-nested-cat-signature' command. *
3190*********************************************************************************************************************************/
3191
3192static RTEXITCODE HelpAddNestedCatSignature(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
3193{
3194 RT_NOREF_PV(enmLevel);
3195 RTStrmWrappedPrintf(pStrm, RTSTRMWRAPPED_F_HANGING_INDENT,
3196 "add-nested-cat-signature [-v|--verbose] [-d|--debug] [-p|--prepend] <destination-cat> <source-cat>\n");
3197 if (enmLevel == RTSIGNTOOLHELP_FULL)
3198 RTStrmWrappedPrintf(pStrm, 0,
3199 "\n"
3200 "The --debug option allows the source-cat to be omitted in order to test the "
3201 "ASN.1 re-encoding of the destination catalog file.\n"
3202 "\n"
3203 "The --prepend option puts the nested signature first rather than appending it "
3204 "to the end of of the nested signature set. Windows reads nested signatures in "
3205 "reverse order, so --prepend will logically putting it last.\n");
3206 return RTEXITCODE_SUCCESS;
3207}
3208
3209
3210static RTEXITCODE HandleAddNestedCatSignature(int cArgs, char **papszArgs)
3211{
3212 /*
3213 * Parse arguments.
3214 */
3215 static const RTGETOPTDEF s_aOptions[] =
3216 {
3217 { "--prepend", 'p', RTGETOPT_REQ_NOTHING },
3218 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
3219 { "--debug", 'd', RTGETOPT_REQ_NOTHING },
3220 };
3221
3222 const char *pszDst = NULL;
3223 const char *pszSrc = NULL;
3224 unsigned cVerbosity = 0;
3225 bool fDebug = false;
3226 bool fPrepend = false;
3227
3228 RTGETOPTSTATE GetState;
3229 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
3230 AssertRCReturn(rc, RTEXITCODE_FAILURE);
3231 RTGETOPTUNION ValueUnion;
3232 int ch;
3233 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
3234 {
3235 switch (ch)
3236 {
3237 case 'v': cVerbosity++; break;
3238 case 'd': fDebug = pszSrc == NULL; break;
3239 case 'p': fPrepend = true; break;
3240 case 'V': return HandleVersion(cArgs, papszArgs);
3241 case 'h': return HelpAddNestedCatSignature(g_pStdOut, RTSIGNTOOLHELP_FULL);
3242
3243 case VINF_GETOPT_NOT_OPTION:
3244 if (!pszDst)
3245 pszDst = ValueUnion.psz;
3246 else if (!pszSrc)
3247 {
3248 pszSrc = ValueUnion.psz;
3249 fDebug = false;
3250 }
3251 else
3252 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Too many file arguments: %s", ValueUnion.psz);
3253 break;
3254
3255 default:
3256 return RTGetOptPrintError(ch, &ValueUnion);
3257 }
3258 }
3259 if (!pszDst)
3260 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No destination catalog file given.");
3261 if (!pszSrc && !fDebug)
3262 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No source catalog file given.");
3263
3264 /*
3265 * Do it.
3266 */
3267 /* Read & decode the source PKCS#7 signature. */
3268 SIGNTOOLPKCS7 Src;
3269 RTEXITCODE rcExit = pszSrc ? SignToolPkcs7_InitFromFile(&Src, pszSrc, cVerbosity) : RTEXITCODE_SUCCESS;
3270 if (rcExit == RTEXITCODE_SUCCESS)
3271 {
3272 /* Ditto for the destination PKCS#7 signature. */
3273 SIGNTOOLPKCS7EXE Dst;
3274 rcExit = SignToolPkcs7_InitFromFile(&Dst, pszDst, cVerbosity);
3275 if (rcExit == RTEXITCODE_SUCCESS)
3276 {
3277 /* Do the signature manipulation. */
3278 if (pszSrc)
3279 rcExit = SignToolPkcs7_AddNestedSignature(&Dst, &Src, cVerbosity, fPrepend);
3280 if (rcExit == RTEXITCODE_SUCCESS)
3281 rcExit = SignToolPkcs7_Encode(&Dst, cVerbosity);
3282
3283 /* Update the destination executable file. */
3284 if (rcExit == RTEXITCODE_SUCCESS)
3285 rcExit = SignToolPkcs7_WriteSignatureToFile(&Dst, pszDst, cVerbosity);
3286
3287 SignToolPkcs7_Delete(&Dst);
3288 }
3289 if (pszSrc)
3290 SignToolPkcs7_Delete(&Src);
3291 }
3292
3293 return rcExit;
3294}
3295
3296
3297/*********************************************************************************************************************************
3298* Option handlers shared by 'sign-exe', 'sign-cat', 'add-timestamp-exe-signature' and others. *
3299*********************************************************************************************************************************/
3300#ifndef IPRT_SIGNTOOL_NO_SIGNING
3301
3302static RTEXITCODE HandleOptAddCert(PRTCRSTORE phStore, const char *pszFile)
3303{
3304 if (*phStore == NIL_RTCRSTORE)
3305 {
3306 int rc = RTCrStoreCreateInMem(phStore, 2);
3307 if (RT_FAILURE(rc))
3308 return RTMsgErrorExitFailure("RTCrStoreCreateInMem(,2) failed: %Rrc", rc);
3309 }
3310 RTERRINFOSTATIC ErrInfo;
3311 int rc = RTCrStoreCertAddFromFile(*phStore, RTCRCERTCTX_F_ADD_IF_NOT_FOUND, pszFile, RTErrInfoInitStatic(&ErrInfo));
3312 if (RT_FAILURE(rc))
3313 return RTMsgErrorExitFailure("Error reading certificate from '%s': %Rrc%#RTeim", pszFile, rc, &ErrInfo.Core);
3314 return RTEXITCODE_SUCCESS;
3315}
3316
3317static RTEXITCODE HandleOptSignatureType(RTDIGESTTYPE *penmSigType, const char *pszType)
3318{
3319 if ( RTStrICmpAscii(pszType, "sha1") == 0
3320 || RTStrICmpAscii(pszType, "sha-1") == 0)
3321 *penmSigType = RTDIGESTTYPE_SHA1;
3322 else if ( RTStrICmpAscii(pszType, "sha256") == 0
3323 || RTStrICmpAscii(pszType, "sha-256") == 0)
3324 *penmSigType = RTDIGESTTYPE_SHA256;
3325 else
3326 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown signature type: %s (expected sha1 or sha256)", pszType);
3327 return RTEXITCODE_SUCCESS;
3328}
3329
3330
3331static RTEXITCODE HandleOptTimestampType(bool *pfOldType, const char *pszType)
3332{
3333 if (strcmp(pszType, "old") == 0)
3334 *pfOldType = true;
3335 else if (strcmp(pszType, "new") == 0)
3336 *pfOldType = false;
3337 else
3338 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown timestamp type: %s", pszType);
3339 return RTEXITCODE_SUCCESS;
3340}
3341
3342static RTEXITCODE HandleOptTimestampOverride(PRTTIMESPEC pSigningTime, const char *pszPartialTs)
3343{
3344 /*
3345 * First try use it as-is.
3346 */
3347 if (RTTimeSpecFromString(pSigningTime, pszPartialTs) != NULL)
3348 return RTEXITCODE_SUCCESS;
3349
3350 /* Check the input against a pattern, making sure we've got something that
3351 makes sense before trying to merge. */
3352 size_t const cchPartialTs = strlen(pszPartialTs);
3353 static char s_szPattern[] = "0000-00-00T00:00:";
3354 if (cchPartialTs > sizeof(s_szPattern) - 1) /* It is not a partial timestamp if we've got the seconds component. */
3355 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid timestamp: %s", pszPartialTs);
3356
3357 for (size_t off = 0; off < cchPartialTs; off++)
3358 switch (s_szPattern[off])
3359 {
3360 case '0':
3361 if (!RT_C_IS_DIGIT(pszPartialTs[off]))
3362 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid timestamp, expected digit at position %u: %s",
3363 off + 1, pszPartialTs);
3364 break;
3365 case '-':
3366 case ':':
3367 if (pszPartialTs[off] != s_szPattern[off])
3368 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid timestamp, expected '%c' at position %u: %s",
3369 s_szPattern[off], off + 1, pszPartialTs);
3370 break;
3371 case 'T':
3372 if ( pszPartialTs[off] != 'T'
3373 && pszPartialTs[off] != 't'
3374 && pszPartialTs[off] != ' ')
3375 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid timestamp, expected 'T' or space at position %u: %s",
3376 off + 1, pszPartialTs);
3377 break;
3378 default:
3379 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Internal error");
3380 }
3381
3382 if (RT_C_IS_DIGIT(s_szPattern[cchPartialTs]) && RT_C_IS_DIGIT(s_szPattern[cchPartialTs - 1]))
3383 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Incomplete timstamp component: %s", pszPartialTs);
3384
3385 /*
3386 * Take the current time and merge in the components from pszPartialTs.
3387 */
3388 char szSigningTime[RTTIME_STR_LEN];
3389 RTTIMESPEC Now;
3390 RTTimeSpecToString(RTTimeNow(&Now), szSigningTime, sizeof(szSigningTime));
3391 memcpy(szSigningTime, pszPartialTs, cchPartialTs);
3392 szSigningTime[4+1+2+1+2] = 'T';
3393
3394 /* Fix 29th for non-leap override: */
3395 if (memcmp(&szSigningTime[5], RT_STR_TUPLE("02-29")) == 0)
3396 {
3397 if (!RTTimeIsLeapYear(RTStrToUInt32(szSigningTime)))
3398 szSigningTime[9] = '8';
3399 }
3400 if (RTTimeSpecFromString(pSigningTime, szSigningTime) == NULL)
3401 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid timestamp: %s (%s)", pszPartialTs, szSigningTime);
3402
3403 return RTEXITCODE_SUCCESS;
3404}
3405
3406static RTEXITCODE HandleOptFileType(RTSIGNTOOLFILETYPE *penmFileType, const char *pszType)
3407{
3408 if (strcmp(pszType, "detect") == 0 || strcmp(pszType, "auto") == 0)
3409 *penmFileType = RTSIGNTOOLFILETYPE_DETECT;
3410 else if (strcmp(pszType, "exe") == 0)
3411 *penmFileType = RTSIGNTOOLFILETYPE_EXE;
3412 else if (strcmp(pszType, "cat") == 0)
3413 *penmFileType = RTSIGNTOOLFILETYPE_CAT;
3414 else
3415 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown forced file type: %s", pszType);
3416 return RTEXITCODE_SUCCESS;
3417}
3418
3419/**
3420 * Detects the type of files @a pszFile is (by reading from it).
3421 *
3422 * @returns The file type, or RTSIGNTOOLFILETYPE_UNKNOWN (error displayed).
3423 * @param enmForceFileType Usually set to RTSIGNTOOLFILETYPE_DETECT, but if
3424 * not we'll return this without probing the file.
3425 * @param pszFile The name of the file to detect the type of.
3426 */
3427static RTSIGNTOOLFILETYPE DetectFileType(RTSIGNTOOLFILETYPE enmForceFileType, const char *pszFile)
3428{
3429 /*
3430 * Forced?
3431 */
3432 if (enmForceFileType != RTSIGNTOOLFILETYPE_DETECT)
3433 return enmForceFileType;
3434
3435 /*
3436 * Read the start of the file.
3437 */
3438 RTFILE hFile = NIL_RTFILE;
3439 int rc = RTFileOpen(&hFile, pszFile, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
3440 if (RT_FAILURE(rc))
3441 {
3442 RTMsgError("Error opening '%s' for reading: %Rrc", pszFile, rc);
3443 return RTSIGNTOOLFILETYPE_UNKNOWN;
3444 }
3445
3446 union
3447 {
3448 uint8_t ab[256];
3449 uint16_t au16[256/2];
3450 uint32_t au32[256/4];
3451 } uBuf;
3452 RT_ZERO(uBuf);
3453
3454 size_t cbRead = 0;
3455 rc = RTFileRead(hFile, &uBuf, sizeof(uBuf), &cbRead);
3456 if (RT_FAILURE(rc))
3457 RTMsgError("Error reading from '%s': %Rrc", pszFile, rc);
3458
3459 uint64_t cbFile;
3460 int rcSize = RTFileQuerySize(hFile, &cbFile);
3461 if (RT_FAILURE(rcSize))
3462 RTMsgError("Error querying size of '%s': %Rrc", pszFile, rc);
3463
3464 RTFileClose(hFile);
3465 if (RT_FAILURE(rc) || RT_FAILURE(rcSize))
3466 return RTSIGNTOOLFILETYPE_UNKNOWN;
3467
3468 /*
3469 * Try guess the kind of file.
3470 */
3471 /* All the executable magics we know: */
3472 if ( uBuf.au16[0] == RT_H2LE_U16_C(IMAGE_DOS_SIGNATURE)
3473 || uBuf.au16[0] == RT_H2LE_U16_C(IMAGE_NE_SIGNATURE)
3474 || uBuf.au16[0] == RT_H2LE_U16_C(IMAGE_LX_SIGNATURE)
3475 || uBuf.au16[0] == RT_H2LE_U16_C(IMAGE_LE_SIGNATURE)
3476 || uBuf.au32[0] == RT_H2LE_U32_C(IMAGE_NT_SIGNATURE)
3477 || uBuf.au32[0] == RT_H2LE_U32_C(IMAGE_ELF_SIGNATURE)
3478 || uBuf.au32[0] == IMAGE_FAT_SIGNATURE
3479 || uBuf.au32[0] == IMAGE_FAT_SIGNATURE_OE
3480 || uBuf.au32[0] == IMAGE_MACHO32_SIGNATURE
3481 || uBuf.au32[0] == IMAGE_MACHO32_SIGNATURE_OE
3482 || uBuf.au32[0] == IMAGE_MACHO64_SIGNATURE
3483 || uBuf.au32[0] == IMAGE_MACHO64_SIGNATURE_OE)
3484 return RTSIGNTOOLFILETYPE_EXE;
3485
3486 /*
3487 * Catalog files are PKCS#7 SignedData and starts with a ContentInfo, i.e.:
3488 * SEQUENCE {
3489 * contentType OBJECT IDENTIFIER,
3490 * content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL
3491 * }
3492 *
3493 * We ASSUME that it's DER encoded and doesn't use an indefinite length form
3494 * at the start and that contentType is signedData (1.2.840.113549.1.7.2).
3495 *
3496 * Example of a 10353 (0x2871) byte long file:
3497 * vv-------- contentType -------vv
3498 * 00000000 30 82 28 6D 06 09 2A 86 48 86 F7 0D 01 07 02 A0
3499 * 00000010 82 28 5E 30 82 28 5A 02 01 01 31 0B 30 09 06 05
3500 */
3501 if ( uBuf.ab[0] == (ASN1_TAG_SEQUENCE | ASN1_TAGFLAG_CONSTRUCTED)
3502 && uBuf.ab[1] != 0x80 /* not indefinite form */
3503 && uBuf.ab[1] > 0x30)
3504 {
3505 size_t off = 1;
3506 uint32_t cbRec = uBuf.ab[1];
3507 if (cbRec & 0x80)
3508 {
3509 cbRec &= 0x7f;
3510 off += cbRec;
3511 switch (cbRec)
3512 {
3513 case 1: cbRec = uBuf.ab[2]; break;
3514 case 2: cbRec = RT_MAKE_U16( uBuf.ab[3], uBuf.ab[2]); break;
3515 case 3: cbRec = RT_MAKE_U32_FROM_U8(uBuf.ab[4], uBuf.ab[3], uBuf.ab[2], 0); break;
3516 case 4: cbRec = RT_MAKE_U32_FROM_U8(uBuf.ab[5], uBuf.ab[4], uBuf.ab[3], uBuf.ab[2]); break;
3517 default: cbRec = UINT32_MAX; break;
3518 }
3519 }
3520 if (off <= 5)
3521 {
3522 off++;
3523 if (off + cbRec == cbFile)
3524 {
3525 /* If the contentType is signedData we're going to treat it as a catalog file,
3526 we don't currently much care about the signed content of a cat file. */
3527 static const uint8_t s_abSignedDataOid[] =
3528 { ASN1_TAG_OID, 9 /*length*/, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02 };
3529 if (memcmp(&uBuf.ab[off], s_abSignedDataOid, sizeof(s_abSignedDataOid)) == 0)
3530 return RTSIGNTOOLFILETYPE_CAT;
3531 }
3532 }
3533 }
3534
3535 RTMsgError("Unable to detect type of '%s'", pszFile);
3536 return RTSIGNTOOLFILETYPE_UNKNOWN;
3537}
3538
3539#endif /* !IPRT_SIGNTOOL_NO_SIGNING */
3540
3541
3542/*********************************************************************************************************************************
3543* The 'add-timestamp-exe-signature' command. *
3544*********************************************************************************************************************************/
3545#ifndef IPRT_SIGNTOOL_NO_SIGNING
3546
3547static RTEXITCODE HelpAddTimestampExeSignature(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
3548{
3549 RT_NOREF_PV(enmLevel);
3550
3551 RTStrmWrappedPrintf(pStrm, RTSTRMWRAPPED_F_HANGING_INDENT,
3552 "add-timestamp-exe-signature [-v|--verbose] [--signature-index|-i <num>] "
3553 OPT_CERT_KEY_SYNOPSIS("--timestamp-")
3554 "[--timestamp-type old|new] "
3555 "[--timestamp-override <partial-isots>] "
3556 "[--replace-existing|-r] "
3557 "<exe>\n");
3558 if (enmLevel == RTSIGNTOOLHELP_FULL)
3559 RTStrmWrappedPrintf(pStrm, 0,
3560 "This is mainly to test timestamp code.\n"
3561 "\n"
3562 "The --timestamp-override option can take a partial or full ISO timestamp. It is merged "
3563 "with the current time if partial.\n"
3564 "\n");
3565 return RTEXITCODE_SUCCESS;
3566}
3567
3568static RTEXITCODE HandleAddTimestampExeSignature(int cArgs, char **papszArgs)
3569{
3570 /*
3571 * Parse arguments.
3572 */
3573 static const RTGETOPTDEF s_aOptions[] =
3574 {
3575 { "--signature-index", 'i', RTGETOPT_REQ_UINT32 },
3576 OPT_CERT_KEY_GETOPTDEF_ENTRIES("--timestamp-", 1000),
3577 { "--timestamp-type", OPT_TIMESTAMP_TYPE, RTGETOPT_REQ_STRING },
3578 { "--timestamp-override", OPT_TIMESTAMP_OVERRIDE, RTGETOPT_REQ_STRING },
3579 { "--replace-existing", 'r', RTGETOPT_REQ_NOTHING },
3580 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
3581 };
3582
3583 unsigned cVerbosity = 0;
3584 unsigned iSignature = 0;
3585 bool fReplaceExisting = false;
3586 bool fTimestampTypeOld = true;
3587 SignToolKeyPair TimestampCertKey("timestamp", true);
3588 RTTIMESPEC SigningTime;
3589 RTTimeNow(&SigningTime);
3590
3591 RTGETOPTSTATE GetState;
3592 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
3593 AssertRCReturn(rc, RTEXITCODE_FAILURE);
3594
3595 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
3596 RTGETOPTUNION ValueUnion;
3597 int ch;
3598 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
3599 {
3600 RTEXITCODE rcExit2 = RTEXITCODE_SUCCESS;
3601 switch (ch)
3602 {
3603 OPT_CERT_KEY_SWITCH_CASES(TimestampCertKey, 1000, ch, ValueUnion, rcExit2);
3604 case 'i': iSignature = ValueUnion.u32; break;
3605 case OPT_TIMESTAMP_TYPE: rcExit2 = HandleOptTimestampType(&fTimestampTypeOld, ValueUnion.psz); break;
3606 case OPT_TIMESTAMP_OVERRIDE: rcExit2 = HandleOptTimestampOverride(&SigningTime, ValueUnion.psz); break;
3607 case 'r': fReplaceExisting = true; break;
3608 case 'v': cVerbosity++; break;
3609 case 'V': return HandleVersion(cArgs, papszArgs);
3610 case 'h': return HelpAddTimestampExeSignature(g_pStdOut, RTSIGNTOOLHELP_FULL);
3611
3612 case VINF_GETOPT_NOT_OPTION:
3613 /* Do final certificate and key option processing (first file only). */
3614 rcExit2 = TimestampCertKey.finalizeOptions(cVerbosity);
3615 if (rcExit2 == RTEXITCODE_SUCCESS)
3616 {
3617 /* Do the work: */
3618 SIGNTOOLPKCS7EXE Exe;
3619 rcExit2 = SignToolPkcs7Exe_InitFromFile(&Exe, ValueUnion.psz, cVerbosity);
3620 if (rcExit2 == RTEXITCODE_SUCCESS)
3621 {
3622 rcExit2 = SignToolPkcs7_AddTimestampSignature(&Exe, cVerbosity, iSignature, fReplaceExisting,
3623 fTimestampTypeOld, SigningTime, &TimestampCertKey);
3624 if (rcExit2 == RTEXITCODE_SUCCESS)
3625 rcExit2 = SignToolPkcs7_Encode(&Exe, cVerbosity);
3626 if (rcExit2 == RTEXITCODE_SUCCESS)
3627 rcExit2 = SignToolPkcs7Exe_WriteSignatureToFile(&Exe, cVerbosity);
3628 SignToolPkcs7Exe_Delete(&Exe);
3629 }
3630 if (rcExit2 != RTEXITCODE_SUCCESS && rcExit == RTEXITCODE_SUCCESS)
3631 rcExit = rcExit2;
3632 rcExit2 = RTEXITCODE_SUCCESS;
3633 }
3634 break;
3635
3636 default:
3637 return RTGetOptPrintError(ch, &ValueUnion);
3638 }
3639
3640 if (rcExit2 != RTEXITCODE_SUCCESS)
3641 {
3642 rcExit = rcExit2;
3643 break;
3644 }
3645 }
3646 return rcExit;
3647}
3648
3649#endif /*!IPRT_SIGNTOOL_NO_SIGNING */
3650
3651
3652/*********************************************************************************************************************************
3653* The 'sign-exe' command. *
3654*********************************************************************************************************************************/
3655#ifndef IPRT_SIGNTOOL_NO_SIGNING
3656
3657static RTEXITCODE HelpSign(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
3658{
3659 RT_NOREF_PV(enmLevel);
3660
3661 RTStrmWrappedPrintf(pStrm, RTSTRMWRAPPED_F_HANGING_INDENT,
3662 "sign [-v|--verbose] "
3663 "[--file-type exe|cat] "
3664 "[--type|/fd sha1|sha256] "
3665 "[--hash-pages|/ph] "
3666 "[--no-hash-pages|/nph] "
3667 "[--append/as] "
3668 "[--no-signing-time] "
3669 "[--add-cert <file>] "
3670 "[--timestamp-type old|new] "
3671 "[--timestamp-override <partial-isots>] "
3672 "[--verbose|/debug|-v] "
3673 OPT_CERT_KEY_SYNOPSIS("--")
3674 OPT_CERT_KEY_SYNOPSIS("--timestamp-")
3675 "<exe>\n");
3676 if (enmLevel == RTSIGNTOOLHELP_FULL)
3677 RTStrmWrappedPrintf(pStrm, 0,
3678 "\n"
3679 "Create a new code signature for an executable or catalog.\n"
3680 "\n"
3681 "Options:\n"
3682 " --append, /as\n"
3683 " Append the signature if one already exists. The default is to replace any existing signature.\n"
3684 " --type sha1|sha256, /fd sha1|sha256\n"
3685 " Signature type, SHA-1 or SHA-256.\n"
3686 " --hash-pages, /ph, --no-page-hashes, /nph\n"
3687 " Enables or disables page hashing. Ignored for catalog files. Default: --no-page-hashes\n"
3688 " --add-cert <file>, /ac <file>\n"
3689 " Adds (first) certificate from the file to the signature. Both PEM and DER (binary) encodings "
3690 "are accepted. Repeat to add more certiifcates.\n"
3691 " --timestamp-override <partial-iso-timestamp>\n"
3692 " This specifies the signing time as a ISO timestamp. Partial timestamps are merged with the "
3693 "current time. This is applied to any timestamp signature as well as the signingTime attribute of "
3694 "main signature. Higher resolution than seconds is not supported. Default: Current time.\n"
3695 " --no-signing-time\n"
3696 " Don't set the signing time on the main signature, only on the timestamp one. Unfortunately, "
3697 "this doesn't work without modifying OpenSSL a little.\n"
3698 " --timestamp-type old|new\n"
3699 " Selects the timstamp type. 'old' is the old style /t <url> stuff from signtool.exe. "
3700 "'new' means a RTC-3161 timstamp - currently not implemented. Default: old\n"
3701 "\n"
3702 "Certificate and Key Options (--timestamp-cert-name etc for timestamps):\n"
3703 " --cert-subject <partial name>, /n <partial name>\n"
3704 " Locate the main signature signing certificate and key, unless anything else is given, "
3705 "by the given name substring. Overrides any previous --cert-sha1 and --cert-file options.\n"
3706 " --cert-sha1 <hex bytes>, /sha1 <hex bytes>\n"
3707 " Locate the main signature signing certificate and key, unless anything else is given, "
3708 "by the given thumbprint. The hex bytes can be space separated, colon separated, just "
3709 "bunched together, or a mix of these. This overrids any previous --cert-name and --cert-file "
3710 "options.\n"
3711 " --cert-store <name>, /s <store>\n"
3712 " Certificate store to search when using --cert-name or --cert-sha1. Default: MY\n"
3713 " --cert-machine-store, /sm\n"
3714 " Use the machine store rather the ones of the current user.\n"
3715 " --cert-file <file>, /f <file>\n"
3716 " Load the certificate and key, unless anything else is given, from given file. Both PEM and "
3717 "DER (binary) encodings are supported. Keys file can be RSA or PKCS#12 formatted.\n"
3718 " --key-file <file>\n"
3719 " Load the private key from the given file. Support RSA and PKCS#12 formatted files.\n"
3720 " --key-password <password>, /p <password>\n"
3721 " Password to use to decrypt a PKCS#12 password file.\n"
3722 " --key-password-file <file>|stdin\n"
3723 " Load password to decrypt the password file from the given file or from stdin.\n"
3724 " --key-name <name>, /kc <name>\n"
3725 " The private key container name. Not implemented.\n"
3726 " --key-provider <name>, /csp <name>\n"
3727 " The name of the crypto provider where the private key conatiner specified via --key-name "
3728 "can be found.\n"
3729 );
3730
3731 return RTEXITCODE_SUCCESS;
3732}
3733
3734
3735static RTEXITCODE HandleSign(int cArgs, char **papszArgs)
3736{
3737 /*
3738 * Parse arguments.
3739 */
3740 static const RTGETOPTDEF s_aOptions[] =
3741 {
3742 { "--append", 'A', RTGETOPT_REQ_NOTHING },
3743 { "/as", 'A', RTGETOPT_REQ_NOTHING },
3744 { "/a", OPT_IGNORED, RTGETOPT_REQ_NOTHING }, /* select best cert automatically */
3745 { "--type", 't', RTGETOPT_REQ_STRING },
3746 { "/fd", 't', RTGETOPT_REQ_STRING },
3747 { "--hash-pages", OPT_HASH_PAGES, RTGETOPT_REQ_NOTHING },
3748 { "/ph", OPT_HASH_PAGES, RTGETOPT_REQ_NOTHING },
3749 { "--no-hash-pages", OPT_NO_HASH_PAGES, RTGETOPT_REQ_NOTHING },
3750 { "/nph", OPT_NO_HASH_PAGES, RTGETOPT_REQ_NOTHING },
3751 { "--add-cert", OPT_ADD_CERT, RTGETOPT_REQ_STRING },
3752 { "/ac", OPT_ADD_CERT, RTGETOPT_REQ_STRING },
3753 { "--description", 'd', RTGETOPT_REQ_STRING },
3754 { "--desc", 'd', RTGETOPT_REQ_STRING },
3755 { "/d", 'd', RTGETOPT_REQ_STRING },
3756 { "--description-url", 'D', RTGETOPT_REQ_STRING },
3757 { "--desc-url", 'D', RTGETOPT_REQ_STRING },
3758 { "/du", 'D', RTGETOPT_REQ_STRING },
3759 { "--no-signing-time", OPT_NO_SIGNING_TIME, RTGETOPT_REQ_NOTHING },
3760 OPT_CERT_KEY_GETOPTDEF_ENTRIES("--", 1000),
3761 OPT_CERT_KEY_GETOPTDEF_COMPAT_ENTRIES( 1000),
3762 OPT_CERT_KEY_GETOPTDEF_ENTRIES("--timestamp-", 1020),
3763 { "--timestamp-type", OPT_TIMESTAMP_TYPE, RTGETOPT_REQ_STRING },
3764 { "--timestamp-override", OPT_TIMESTAMP_OVERRIDE, RTGETOPT_REQ_STRING },
3765 { "--file-type", OPT_FILE_TYPE, RTGETOPT_REQ_STRING },
3766 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
3767 { "/v", 'v', RTGETOPT_REQ_NOTHING },
3768 { "/debug", 'v', RTGETOPT_REQ_NOTHING },
3769 };
3770
3771 unsigned cVerbosity = 0;
3772 RTDIGESTTYPE enmSigType = RTDIGESTTYPE_SHA1;
3773 bool fReplaceExisting = true;
3774 bool fHashPages = false;
3775 bool fNoSigningTime = false;
3776 RTSIGNTOOLFILETYPE enmForceFileType = RTSIGNTOOLFILETYPE_DETECT;
3777 SignToolKeyPair SigningCertKey("signing", true);
3778 RTCRSTORE hAddCerts = NIL_RTCRSTORE; /* leaked if returning directly (--help, --version) */
3779 const char *pszDescription = NULL; /** @todo implement putting descriptions into the OpusInfo stuff. */
3780 const char *pszDescriptionUrl = NULL;
3781 bool fTimestampTypeOld = true;
3782 SignToolKeyPair TimestampCertKey("timestamp");
3783 RTTIMESPEC SigningTime;
3784 RTTimeNow(&SigningTime);
3785
3786 RTGETOPTSTATE GetState;
3787 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
3788 AssertRCReturn(rc, RTEXITCODE_FAILURE);
3789
3790 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
3791 RTGETOPTUNION ValueUnion;
3792 int ch;
3793 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
3794 {
3795 RTEXITCODE rcExit2 = RTEXITCODE_SUCCESS;
3796 switch (ch)
3797 {
3798 OPT_CERT_KEY_SWITCH_CASES(SigningCertKey, 1000, ch, ValueUnion, rcExit2);
3799 OPT_CERT_KEY_SWITCH_CASES(TimestampCertKey, 1020, ch, ValueUnion, rcExit2);
3800 case 't': rcExit2 = HandleOptSignatureType(&enmSigType, ValueUnion.psz); break;
3801 case 'A': fReplaceExisting = false; break;
3802 case 'd': pszDescription = ValueUnion.psz; break;
3803 case 'D': pszDescriptionUrl = ValueUnion.psz; break;
3804 case OPT_HASH_PAGES: fHashPages = true; break;
3805 case OPT_NO_HASH_PAGES: fHashPages = false; break;
3806 case OPT_NO_SIGNING_TIME: fNoSigningTime = true; break;
3807 case OPT_ADD_CERT: rcExit2 = HandleOptAddCert(&hAddCerts, ValueUnion.psz); break;
3808 case OPT_TIMESTAMP_TYPE: rcExit2 = HandleOptTimestampType(&fTimestampTypeOld, ValueUnion.psz); break;
3809 case OPT_TIMESTAMP_OVERRIDE: rcExit2 = HandleOptTimestampOverride(&SigningTime, ValueUnion.psz); break;
3810 case OPT_FILE_TYPE: rcExit2 = HandleOptFileType(&enmForceFileType, ValueUnion.psz); break;
3811 case OPT_IGNORED: break;
3812 case 'v': cVerbosity++; break;
3813 case 'V': return HandleVersion(cArgs, papszArgs);
3814 case 'h': return HelpSign(g_pStdOut, RTSIGNTOOLHELP_FULL);
3815
3816 case VINF_GETOPT_NOT_OPTION:
3817 /*
3818 * Do final certificate and key option processing (first file only).
3819 */
3820 rcExit2 = SigningCertKey.finalizeOptions(cVerbosity);
3821 if (rcExit2 == RTEXITCODE_SUCCESS)
3822 rcExit2 = TimestampCertKey.finalizeOptions(cVerbosity);
3823 if (rcExit2 == RTEXITCODE_SUCCESS)
3824 {
3825 /*
3826 * Detect file type.
3827 */
3828 RTSIGNTOOLFILETYPE enmFileType = DetectFileType(enmForceFileType, ValueUnion.psz);
3829 if (enmFileType == RTSIGNTOOLFILETYPE_EXE)
3830 {
3831 /*
3832 * Sign executable image.
3833 */
3834 SIGNTOOLPKCS7EXE Exe;
3835 rcExit2 = SignToolPkcs7Exe_InitFromFile(&Exe, ValueUnion.psz, cVerbosity,
3836 RTLDRARCH_WHATEVER, true /*fAllowUnsigned*/);
3837 if (rcExit2 == RTEXITCODE_SUCCESS)
3838 {
3839 rcExit2 = SignToolPkcs7_AddOrReplaceSignature(&Exe, cVerbosity, enmSigType, fReplaceExisting,
3840 fHashPages, fNoSigningTime, &SigningCertKey, hAddCerts,
3841 fTimestampTypeOld, SigningTime, &TimestampCertKey);
3842 if (rcExit2 == RTEXITCODE_SUCCESS)
3843 rcExit2 = SignToolPkcs7_Encode(&Exe, cVerbosity);
3844 if (rcExit2 == RTEXITCODE_SUCCESS)
3845 rcExit2 = SignToolPkcs7Exe_WriteSignatureToFile(&Exe, cVerbosity);
3846 SignToolPkcs7Exe_Delete(&Exe);
3847 }
3848 }
3849 else if (enmFileType == RTSIGNTOOLFILETYPE_CAT)
3850 {
3851 /*
3852 * Sign catalog file.
3853 */
3854 SIGNTOOLPKCS7 Cat;
3855 rcExit2 = SignToolPkcs7_InitFromFile(&Cat, ValueUnion.psz, cVerbosity);
3856 if (rcExit2 == RTEXITCODE_SUCCESS)
3857 {
3858 rcExit2 = SignToolPkcs7_AddOrReplaceCatSignature(&Cat, cVerbosity, enmSigType, fReplaceExisting,
3859 fNoSigningTime, &SigningCertKey, hAddCerts,
3860 fTimestampTypeOld, SigningTime, &TimestampCertKey);
3861 if (rcExit2 == RTEXITCODE_SUCCESS)
3862 rcExit2 = SignToolPkcs7_Encode(&Cat, cVerbosity);
3863 if (rcExit2 == RTEXITCODE_SUCCESS)
3864 rcExit2 = SignToolPkcs7_WriteSignatureToFile(&Cat, ValueUnion.psz, cVerbosity);
3865 SignToolPkcs7_Delete(&Cat);
3866 }
3867 }
3868 else
3869 rcExit2 = RTEXITCODE_FAILURE;
3870 if (rcExit2 != RTEXITCODE_SUCCESS && rcExit == RTEXITCODE_SUCCESS)
3871 rcExit = rcExit2;
3872 rcExit2 = RTEXITCODE_SUCCESS;
3873 }
3874 break;
3875
3876 default:
3877 return RTGetOptPrintError(ch, &ValueUnion);
3878 }
3879 if (rcExit2 != RTEXITCODE_SUCCESS)
3880 {
3881 rcExit = rcExit2;
3882 break;
3883 }
3884 }
3885
3886 if (hAddCerts != NIL_RTCRSTORE)
3887 RTCrStoreRelease(hAddCerts);
3888 return rcExit;
3889}
3890
3891#endif /*!IPRT_SIGNTOOL_NO_SIGNING */
3892
3893
3894/*********************************************************************************************************************************
3895* The 'verify-exe' command. *
3896*********************************************************************************************************************************/
3897#ifndef IPRT_IN_BUILD_TOOL
3898
3899static RTEXITCODE HelpVerifyExe(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
3900{
3901 RT_NOREF_PV(enmLevel);
3902 RTStrmWrappedPrintf(pStrm, RTSTRMWRAPPED_F_HANGING_INDENT,
3903 "verify-exe [--verbose|--quiet] [--kernel] [--root <root-cert.der>] [--additional <supp-cert.der>] "
3904 "[--type <win|osx>] <exe1> [exe2 [..]]\n");
3905 return RTEXITCODE_SUCCESS;
3906}
3907
3908typedef struct VERIFYEXESTATE
3909{
3910 RTCRSTORE hRootStore;
3911 RTCRSTORE hKernelRootStore;
3912 RTCRSTORE hAdditionalStore;
3913 bool fKernel;
3914 int cVerbose;
3915 enum { kSignType_Windows, kSignType_OSX } enmSignType;
3916 RTLDRARCH enmLdrArch;
3917 uint32_t cBad;
3918 uint32_t cOkay;
3919 const char *pszFilename;
3920 RTTIMESPEC ValidationTime;
3921} VERIFYEXESTATE;
3922
3923# ifdef VBOX
3924/** Certificate store load set.
3925 * Declared outside HandleVerifyExe because of braindead gcc visibility crap. */
3926struct STSTORESET
3927{
3928 RTCRSTORE hStore;
3929 PCSUPTAENTRY paTAs;
3930 unsigned cTAs;
3931};
3932# endif
3933
3934/**
3935 * @callback_method_impl{FNRTCRPKCS7VERIFYCERTCALLBACK,
3936 * Standard code signing. Use this for Microsoft SPC.}
3937 */
3938static DECLCALLBACK(int) VerifyExecCertVerifyCallback(PCRTCRX509CERTIFICATE pCert, RTCRX509CERTPATHS hCertPaths, uint32_t fFlags,
3939 void *pvUser, PRTERRINFO pErrInfo)
3940{
3941 VERIFYEXESTATE *pState = (VERIFYEXESTATE *)pvUser;
3942 uint32_t cPaths = RTCrX509CertPathsGetPathCount(hCertPaths);
3943
3944 /*
3945 * Dump all the paths.
3946 */
3947 if (pState->cVerbose > 0)
3948 {
3949 for (uint32_t iPath = 0; iPath < cPaths; iPath++)
3950 {
3951 RTPrintf("---\n");
3952 RTCrX509CertPathsDumpOne(hCertPaths, iPath, pState->cVerbose, RTStrmDumpPrintfV, g_pStdOut);
3953 *pErrInfo->pszMsg = '\0';
3954 }
3955 RTPrintf("---\n");
3956 }
3957
3958 /*
3959 * Test signing certificates normally doesn't have all the necessary
3960 * features required below. So, treat them as special cases.
3961 */
3962 if ( hCertPaths == NIL_RTCRX509CERTPATHS
3963 && RTCrX509Name_Compare(&pCert->TbsCertificate.Issuer, &pCert->TbsCertificate.Subject) == 0)
3964 {
3965 RTMsgInfo("Test signed.\n");
3966 return VINF_SUCCESS;
3967 }
3968
3969 if (hCertPaths == NIL_RTCRX509CERTPATHS)
3970 RTMsgInfo("Signed by trusted certificate.\n");
3971
3972 /*
3973 * Standard code signing capabilites required.
3974 */
3975 int rc = RTCrPkcs7VerifyCertCallbackCodeSigning(pCert, hCertPaths, fFlags, NULL, pErrInfo);
3976 if ( RT_SUCCESS(rc)
3977 && (fFlags & RTCRPKCS7VCC_F_SIGNED_DATA))
3978 {
3979 /*
3980 * If windows kernel signing, a valid certificate path must be anchored
3981 * by the microsoft kernel signing root certificate. The only
3982 * alternative is test signing.
3983 */
3984 if ( pState->fKernel
3985 && hCertPaths != NIL_RTCRX509CERTPATHS
3986 && pState->enmSignType == VERIFYEXESTATE::kSignType_Windows)
3987 {
3988 uint32_t cFound = 0;
3989 uint32_t cValid = 0;
3990 for (uint32_t iPath = 0; iPath < cPaths; iPath++)
3991 {
3992 bool fTrusted;
3993 PCRTCRX509NAME pSubject;
3994 PCRTCRX509SUBJECTPUBLICKEYINFO pPublicKeyInfo;
3995 int rcVerify;
3996 rc = RTCrX509CertPathsQueryPathInfo(hCertPaths, iPath, &fTrusted, NULL /*pcNodes*/, &pSubject, &pPublicKeyInfo,
3997 NULL, NULL /*pCertCtx*/, &rcVerify);
3998 AssertRCBreak(rc);
3999
4000 if (RT_SUCCESS(rcVerify))
4001 {
4002 Assert(fTrusted);
4003 cValid++;
4004
4005 /* Search the kernel signing root store for a matching anchor. */
4006 RTCRSTORECERTSEARCH Search;
4007 rc = RTCrStoreCertFindBySubjectOrAltSubjectByRfc5280(pState->hKernelRootStore, pSubject, &Search);
4008 AssertRCBreak(rc);
4009 PCRTCRCERTCTX pCertCtx;
4010 while ((pCertCtx = RTCrStoreCertSearchNext(pState->hKernelRootStore, &Search)) != NULL)
4011 {
4012 PCRTCRX509SUBJECTPUBLICKEYINFO pPubKeyInfo;
4013 if (pCertCtx->pCert)
4014 pPubKeyInfo = &pCertCtx->pCert->TbsCertificate.SubjectPublicKeyInfo;
4015 else if (pCertCtx->pTaInfo)
4016 pPubKeyInfo = &pCertCtx->pTaInfo->PubKey;
4017 else
4018 pPubKeyInfo = NULL;
4019 if (RTCrX509SubjectPublicKeyInfo_Compare(pPubKeyInfo, pPublicKeyInfo) == 0)
4020 cFound++;
4021 RTCrCertCtxRelease(pCertCtx);
4022 }
4023
4024 int rc2 = RTCrStoreCertSearchDestroy(pState->hKernelRootStore, &Search); AssertRC(rc2);
4025 }
4026 }
4027 if (RT_SUCCESS(rc) && cFound == 0)
4028 rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE, "Not valid kernel code signature.");
4029 if (RT_SUCCESS(rc) && cValid != 2)
4030 RTMsgWarning("%u valid paths, expected 2", cValid);
4031 }
4032 /*
4033 * For Mac OS X signing, check for special developer ID attributes.
4034 */
4035 else if (pState->enmSignType == VERIFYEXESTATE::kSignType_OSX)
4036 {
4037 uint32_t cDevIdApp = 0;
4038 uint32_t cDevIdKext = 0;
4039 uint32_t cDevIdMacDev = 0;
4040 for (uint32_t i = 0; i < pCert->TbsCertificate.T3.Extensions.cItems; i++)
4041 {
4042 PCRTCRX509EXTENSION pExt = pCert->TbsCertificate.T3.Extensions.papItems[i];
4043 if (RTAsn1ObjId_CompareWithString(&pExt->ExtnId, RTCR_APPLE_CS_DEVID_APPLICATION_OID) == 0)
4044 {
4045 cDevIdApp++;
4046 if (!pExt->Critical.fValue)
4047 rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
4048 "Dev ID Application certificate extension is not flagged critical");
4049 }
4050 else if (RTAsn1ObjId_CompareWithString(&pExt->ExtnId, RTCR_APPLE_CS_DEVID_KEXT_OID) == 0)
4051 {
4052 cDevIdKext++;
4053 if (!pExt->Critical.fValue)
4054 rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
4055 "Dev ID kext certificate extension is not flagged critical");
4056 }
4057 else if (RTAsn1ObjId_CompareWithString(&pExt->ExtnId, RTCR_APPLE_CS_DEVID_MAC_SW_DEV_OID) == 0)
4058 {
4059 cDevIdMacDev++;
4060 if (!pExt->Critical.fValue)
4061 rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
4062 "Dev ID Mac SW dev certificate extension is not flagged critical");
4063 }
4064 }
4065 if (cDevIdApp == 0)
4066 {
4067 if (cDevIdMacDev == 0)
4068 rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
4069 "Certificate is missing the 'Dev ID Application' extension");
4070 else
4071 RTMsgWarning("Mac SW dev certificate used to sign code.");
4072 }
4073 if (cDevIdKext == 0 && pState->fKernel)
4074 {
4075 if (cDevIdMacDev == 0)
4076 rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
4077 "Certificate is missing the 'Dev ID kext' extension");
4078 else
4079 RTMsgWarning("Mac SW dev certificate used to sign kernel code.");
4080 }
4081 }
4082 }
4083
4084 return rc;
4085}
4086
4087/** @callback_method_impl{FNRTLDRVALIDATESIGNEDDATA} */
4088static DECLCALLBACK(int) VerifyExeCallback(RTLDRMOD hLdrMod, PCRTLDRSIGNATUREINFO pInfo, PRTERRINFO pErrInfo, void *pvUser)
4089{
4090 VERIFYEXESTATE *pState = (VERIFYEXESTATE *)pvUser;
4091 RT_NOREF_PV(hLdrMod);
4092
4093 switch (pInfo->enmType)
4094 {
4095 case RTLDRSIGNATURETYPE_PKCS7_SIGNED_DATA:
4096 {
4097 PCRTCRPKCS7CONTENTINFO pContentInfo = (PCRTCRPKCS7CONTENTINFO)pInfo->pvSignature;
4098
4099 /*
4100 * Dump the signed data if so requested and it's the first one, assuming that
4101 * additional signatures in contained wihtin the same ContentInfo structure.
4102 */
4103 if (pState->cVerbose && pInfo->iSignature == 0)
4104 RTAsn1Dump(&pContentInfo->SeqCore.Asn1Core, 0, 0, RTStrmDumpPrintfV, g_pStdOut);
4105
4106 /*
4107 * We'll try different alternative timestamps here.
4108 */
4109 struct { RTTIMESPEC TimeSpec; const char *pszDesc; } aTimes[3];
4110 unsigned cTimes = 0;
4111
4112 /* The specified timestamp. */
4113 if (RTTimeSpecGetSeconds(&pState->ValidationTime) != 0)
4114 {
4115 aTimes[cTimes].TimeSpec = pState->ValidationTime;
4116 aTimes[cTimes].pszDesc = "validation time";
4117 cTimes++;
4118 }
4119
4120 /* Linking timestamp: */
4121 uint64_t uLinkingTime = 0;
4122 int rc = RTLdrQueryProp(hLdrMod, RTLDRPROP_TIMESTAMP_SECONDS, &uLinkingTime, sizeof(uLinkingTime));
4123 if (RT_SUCCESS(rc))
4124 {
4125 RTTimeSpecSetSeconds(&aTimes[cTimes].TimeSpec, uLinkingTime);
4126 aTimes[cTimes].pszDesc = "at link time";
4127 cTimes++;
4128 }
4129 else if (rc != VERR_NOT_FOUND)
4130 RTMsgError("RTLdrQueryProp/RTLDRPROP_TIMESTAMP_SECONDS failed on '%s': %Rrc\n", pState->pszFilename, rc);
4131
4132 /* Now: */
4133 RTTimeNow(&aTimes[cTimes].TimeSpec);
4134 aTimes[cTimes].pszDesc = "now";
4135 cTimes++;
4136
4137 /*
4138 * Do the actual verification.
4139 */
4140 for (unsigned iTime = 0; iTime < cTimes; iTime++)
4141 {
4142 if (pInfo->pvExternalData)
4143 rc = RTCrPkcs7VerifySignedDataWithExternalData(pContentInfo,
4144 RTCRPKCS7VERIFY_SD_F_COUNTER_SIGNATURE_SIGNING_TIME_ONLY
4145 | RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_SIGNING_TIME_IF_PRESENT
4146 | RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_MS_TIMESTAMP_IF_PRESENT
4147 | RTCRPKCS7VERIFY_SD_F_CHECK_TRUST_ANCHORS,
4148 pState->hAdditionalStore, pState->hRootStore,
4149 &aTimes[iTime].TimeSpec,
4150 VerifyExecCertVerifyCallback, pState,
4151 pInfo->pvExternalData, pInfo->cbExternalData, pErrInfo);
4152 else
4153 rc = RTCrPkcs7VerifySignedData(pContentInfo,
4154 RTCRPKCS7VERIFY_SD_F_COUNTER_SIGNATURE_SIGNING_TIME_ONLY
4155 | RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_SIGNING_TIME_IF_PRESENT
4156 | RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_MS_TIMESTAMP_IF_PRESENT
4157 | RTCRPKCS7VERIFY_SD_F_CHECK_TRUST_ANCHORS,
4158 pState->hAdditionalStore, pState->hRootStore,
4159 &aTimes[iTime].TimeSpec,
4160 VerifyExecCertVerifyCallback, pState, pErrInfo);
4161 if (RT_SUCCESS(rc))
4162 {
4163 Assert(rc == VINF_SUCCESS || rc == VINF_CR_DIGEST_DEPRECATED);
4164 const char *pszNote = rc == VINF_CR_DIGEST_DEPRECATED ? " (deprecated digest)" : "";
4165 if (pInfo->cSignatures == 1)
4166 RTMsgInfo("'%s' is valid %s%s.\n", pState->pszFilename, aTimes[iTime].pszDesc, pszNote);
4167 else
4168 RTMsgInfo("'%s' signature #%u is valid %s%s.\n",
4169 pState->pszFilename, pInfo->iSignature + 1, aTimes[iTime].pszDesc, pszNote);
4170 pState->cOkay++;
4171 return VINF_SUCCESS;
4172 }
4173 if (rc != VERR_CR_X509_CPV_NOT_VALID_AT_TIME)
4174 {
4175 if (pInfo->cSignatures == 1)
4176 RTMsgError("%s: Failed to verify signature: %Rrc%#RTeim\n", pState->pszFilename, rc, pErrInfo);
4177 else
4178 RTMsgError("%s: Failed to verify signature #%u: %Rrc%#RTeim\n",
4179 pState->pszFilename, pInfo->iSignature + 1, rc, pErrInfo);
4180 pState->cBad++;
4181 return VINF_SUCCESS;
4182 }
4183 }
4184
4185 if (pInfo->cSignatures == 1)
4186 RTMsgError("%s: Signature is not valid at present or link time.\n", pState->pszFilename);
4187 else
4188 RTMsgError("%s: Signature #%u is not valid at present or link time.\n",
4189 pState->pszFilename, pInfo->iSignature + 1);
4190 pState->cBad++;
4191 return VINF_SUCCESS;
4192 }
4193
4194 default:
4195 return RTErrInfoSetF(pErrInfo, VERR_NOT_SUPPORTED, "Unsupported signature type: %d", pInfo->enmType);
4196 }
4197}
4198
4199/**
4200 * Worker for HandleVerifyExe.
4201 */
4202static RTEXITCODE HandleVerifyExeWorker(VERIFYEXESTATE *pState, const char *pszFilename, PRTERRINFOSTATIC pStaticErrInfo)
4203{
4204 /*
4205 * Open the executable image and verify it.
4206 */
4207 RTLDRMOD hLdrMod;
4208 int rc = RTLdrOpen(pszFilename, RTLDR_O_FOR_VALIDATION, pState->enmLdrArch, &hLdrMod);
4209 if (RT_FAILURE(rc))
4210 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error opening executable image '%s': %Rrc", pszFilename, rc);
4211
4212 /* Reset the state. */
4213 pState->cBad = 0;
4214 pState->cOkay = 0;
4215 pState->pszFilename = pszFilename;
4216
4217 rc = RTLdrVerifySignature(hLdrMod, VerifyExeCallback, pState, RTErrInfoInitStatic(pStaticErrInfo));
4218 if (RT_FAILURE(rc))
4219 RTMsgError("RTLdrVerifySignature failed on '%s': %Rrc - %s\n", pszFilename, rc, pStaticErrInfo->szMsg);
4220
4221 int rc2 = RTLdrClose(hLdrMod);
4222 if (RT_FAILURE(rc2))
4223 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTLdrClose failed: %Rrc\n", rc2);
4224 if (RT_FAILURE(rc))
4225 return rc != VERR_LDRVI_NOT_SIGNED ? RTEXITCODE_FAILURE : RTEXITCODE_SKIPPED;
4226
4227 return pState->cOkay > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
4228}
4229
4230
4231static RTEXITCODE HandleVerifyExe(int cArgs, char **papszArgs)
4232{
4233 RTERRINFOSTATIC StaticErrInfo;
4234
4235 /* Note! This code does not try to clean up the crypto stores on failure.
4236 This is intentional as the code is only expected to be used in a
4237 one-command-per-process environment where we do exit() upon
4238 returning from this function. */
4239
4240 /*
4241 * Parse arguments.
4242 */
4243 static const RTGETOPTDEF s_aOptions[] =
4244 {
4245 { "--kernel", 'k', RTGETOPT_REQ_NOTHING },
4246 { "--root", 'r', RTGETOPT_REQ_STRING },
4247 { "--additional", 'a', RTGETOPT_REQ_STRING },
4248 { "--add", 'a', RTGETOPT_REQ_STRING },
4249 { "--type", 't', RTGETOPT_REQ_STRING },
4250 { "--validation-time", 'T', RTGETOPT_REQ_STRING },
4251 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
4252 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
4253 };
4254
4255 VERIFYEXESTATE State =
4256 {
4257 NIL_RTCRSTORE, NIL_RTCRSTORE, NIL_RTCRSTORE, false, 0,
4258 VERIFYEXESTATE::kSignType_Windows, RTLDRARCH_WHATEVER,
4259 0, 0, NULL
4260 };
4261 int rc = RTCrStoreCreateInMem(&State.hRootStore, 0);
4262 if (RT_SUCCESS(rc))
4263 rc = RTCrStoreCreateInMem(&State.hKernelRootStore, 0);
4264 if (RT_SUCCESS(rc))
4265 rc = RTCrStoreCreateInMem(&State.hAdditionalStore, 0);
4266 if (RT_FAILURE(rc))
4267 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error creating in-memory certificate store: %Rrc", rc);
4268 RTTimeSpecSetSeconds(&State.ValidationTime, 0);
4269
4270 RTGETOPTSTATE GetState;
4271 rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
4272 AssertRCReturn(rc, RTEXITCODE_FAILURE);
4273 RTGETOPTUNION ValueUnion;
4274 int ch;
4275 while ((ch = RTGetOpt(&GetState, &ValueUnion)) && ch != VINF_GETOPT_NOT_OPTION)
4276 {
4277 switch (ch)
4278 {
4279 case 'r': case 'a':
4280 rc = RTCrStoreCertAddFromFile(ch == 'r' ? State.hRootStore : State.hAdditionalStore,
4281 RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR,
4282 ValueUnion.psz, RTErrInfoInitStatic(&StaticErrInfo));
4283 if (RT_FAILURE(rc))
4284 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error loading certificate '%s': %Rrc - %s",
4285 ValueUnion.psz, rc, StaticErrInfo.szMsg);
4286 if (RTErrInfoIsSet(&StaticErrInfo.Core))
4287 RTMsgWarning("Warnings loading certificate '%s': %s", ValueUnion.psz, StaticErrInfo.szMsg);
4288 break;
4289
4290 case 't':
4291 if (!strcmp(ValueUnion.psz, "win") || !strcmp(ValueUnion.psz, "windows"))
4292 State.enmSignType = VERIFYEXESTATE::kSignType_Windows;
4293 else if (!strcmp(ValueUnion.psz, "osx") || !strcmp(ValueUnion.psz, "apple"))
4294 State.enmSignType = VERIFYEXESTATE::kSignType_OSX;
4295 else
4296 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown signing type: '%s'", ValueUnion.psz);
4297 break;
4298
4299 case 'T':
4300 if (!RTTimeSpecFromString(&State.ValidationTime, ValueUnion.psz))
4301 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid validation time (%s): %Rrc", ValueUnion.psz, rc);
4302 break;
4303
4304 case 'k': State.fKernel = true; break;
4305 case 'v': State.cVerbose++; break;
4306 case 'q': State.cVerbose = 0; break;
4307 case 'V': return HandleVersion(cArgs, papszArgs);
4308 case 'h': return HelpVerifyExe(g_pStdOut, RTSIGNTOOLHELP_FULL);
4309 default: return RTGetOptPrintError(ch, &ValueUnion);
4310 }
4311 }
4312 if (ch != VINF_GETOPT_NOT_OPTION)
4313 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No executable given.");
4314
4315 /*
4316 * Populate the certificate stores according to the signing type.
4317 */
4318# ifdef VBOX
4319 unsigned cSets = 0;
4320 struct STSTORESET aSets[6];
4321 switch (State.enmSignType)
4322 {
4323 case VERIFYEXESTATE::kSignType_Windows:
4324 aSets[cSets].hStore = State.hRootStore;
4325 aSets[cSets].paTAs = g_aSUPTimestampTAs;
4326 aSets[cSets].cTAs = g_cSUPTimestampTAs;
4327 cSets++;
4328 aSets[cSets].hStore = State.hRootStore;
4329 aSets[cSets].paTAs = g_aSUPSpcRootTAs;
4330 aSets[cSets].cTAs = g_cSUPSpcRootTAs;
4331 cSets++;
4332 aSets[cSets].hStore = State.hRootStore;
4333 aSets[cSets].paTAs = g_aSUPNtKernelRootTAs;
4334 aSets[cSets].cTAs = g_cSUPNtKernelRootTAs;
4335 cSets++;
4336 aSets[cSets].hStore = State.hKernelRootStore;
4337 aSets[cSets].paTAs = g_aSUPNtKernelRootTAs;
4338 aSets[cSets].cTAs = g_cSUPNtKernelRootTAs;
4339 cSets++;
4340 break;
4341
4342 case VERIFYEXESTATE::kSignType_OSX:
4343 aSets[cSets].hStore = State.hRootStore;
4344 aSets[cSets].paTAs = g_aSUPAppleRootTAs;
4345 aSets[cSets].cTAs = g_cSUPAppleRootTAs;
4346 cSets++;
4347 break;
4348 }
4349 for (unsigned i = 0; i < cSets; i++)
4350 for (unsigned j = 0; j < aSets[i].cTAs; j++)
4351 {
4352 rc = RTCrStoreCertAddEncoded(aSets[i].hStore, RTCRCERTCTX_F_ENC_TAF_DER, aSets[i].paTAs[j].pch,
4353 aSets[i].paTAs[j].cb, RTErrInfoInitStatic(&StaticErrInfo));
4354 if (RT_FAILURE(rc))
4355 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTCrStoreCertAddEncoded failed (%u/%u): %s",
4356 i, j, StaticErrInfo.szMsg);
4357 }
4358# endif /* VBOX */
4359
4360 /*
4361 * Do it.
4362 */
4363 RTEXITCODE rcExit;
4364 for (;;)
4365 {
4366 rcExit = HandleVerifyExeWorker(&State, ValueUnion.psz, &StaticErrInfo);
4367 if (rcExit != RTEXITCODE_SUCCESS)
4368 break;
4369
4370 /*
4371 * Next file
4372 */
4373 ch = RTGetOpt(&GetState, &ValueUnion);
4374 if (ch == 0)
4375 break;
4376 if (ch != VINF_GETOPT_NOT_OPTION)
4377 {
4378 rcExit = RTGetOptPrintError(ch, &ValueUnion);
4379 break;
4380 }
4381 }
4382
4383 /*
4384 * Clean up.
4385 */
4386 uint32_t cRefs;
4387 cRefs = RTCrStoreRelease(State.hRootStore); Assert(cRefs == 0);
4388 cRefs = RTCrStoreRelease(State.hKernelRootStore); Assert(cRefs == 0);
4389 cRefs = RTCrStoreRelease(State.hAdditionalStore); Assert(cRefs == 0);
4390
4391 return rcExit;
4392}
4393
4394#endif /* !IPRT_IN_BUILD_TOOL */
4395
4396/*
4397 * common code for show-exe and show-cat:
4398 */
4399
4400/**
4401 * Display an object ID.
4402 *
4403 * @returns IPRT status code.
4404 * @param pThis The show exe instance data.
4405 * @param pObjId The object ID to display.
4406 * @param pszLabel The field label (prefixed by szPrefix).
4407 * @param pszPost What to print after the ID (typically newline).
4408 */
4409static void HandleShowExeWorkerDisplayObjId(PSHOWEXEPKCS7 pThis, PCRTASN1OBJID pObjId, const char *pszLabel, const char *pszPost)
4410{
4411 int rc = RTAsn1QueryObjIdName(pObjId, pThis->szTmp, sizeof(pThis->szTmp));
4412 if (RT_SUCCESS(rc))
4413 {
4414 if (pThis->cVerbosity > 1)
4415 RTPrintf("%s%s%s (%s)%s", pThis->szPrefix, pszLabel, pThis->szTmp, pObjId->szObjId, pszPost);
4416 else
4417 RTPrintf("%s%s%s%s", pThis->szPrefix, pszLabel, pThis->szTmp, pszPost);
4418 }
4419 else
4420 RTPrintf("%s%s%s%s", pThis->szPrefix, pszLabel, pObjId->szObjId, pszPost);
4421}
4422
4423
4424/**
4425 * Display an object ID, without prefix and label
4426 *
4427 * @returns IPRT status code.
4428 * @param pThis The show exe instance data.
4429 * @param pObjId The object ID to display.
4430 * @param pszPost What to print after the ID (typically newline).
4431 */
4432static void HandleShowExeWorkerDisplayObjIdSimple(PSHOWEXEPKCS7 pThis, PCRTASN1OBJID pObjId, const char *pszPost)
4433{
4434 int rc = RTAsn1QueryObjIdName(pObjId, pThis->szTmp, sizeof(pThis->szTmp));
4435 if (RT_SUCCESS(rc))
4436 {
4437 if (pThis->cVerbosity > 1)
4438 RTPrintf("%s (%s)%s", pThis->szTmp, pObjId->szObjId, pszPost);
4439 else
4440 RTPrintf("%s%s", pThis->szTmp, pszPost);
4441 }
4442 else
4443 RTPrintf("%s%s", pObjId->szObjId, pszPost);
4444}
4445
4446
4447/**
4448 * Display a signer info attribute.
4449 *
4450 * @returns IPRT status code.
4451 * @param pThis The show exe instance data.
4452 * @param offPrefix The current prefix offset.
4453 * @param pAttr The attribute to display.
4454 */
4455static int HandleShowExeWorkerPkcs7DisplayAttrib(PSHOWEXEPKCS7 pThis, size_t offPrefix, PCRTCRPKCS7ATTRIBUTE pAttr)
4456{
4457 HandleShowExeWorkerDisplayObjId(pThis, &pAttr->Type, "", ":\n");
4458 if (pThis->cVerbosity > 4 && pAttr->SeqCore.Asn1Core.uData.pu8)
4459 RTPrintf("%s uData.pu8=%p cb=%#x\n", pThis->szPrefix, pAttr->SeqCore.Asn1Core.uData.pu8, pAttr->SeqCore.Asn1Core.cb);
4460
4461 int rc = VINF_SUCCESS;
4462 switch (pAttr->enmType)
4463 {
4464 case RTCRPKCS7ATTRIBUTETYPE_UNKNOWN:
4465 if (pAttr->uValues.pCores->cItems <= 1)
4466 RTPrintf("%s %u bytes\n", pThis->szPrefix,pAttr->uValues.pCores->SetCore.Asn1Core.cb);
4467 else
4468 RTPrintf("%s %u bytes divided by %u items\n", pThis->szPrefix, pAttr->uValues.pCores->SetCore.Asn1Core.cb, pAttr->uValues.pCores->cItems);
4469 break;
4470
4471 /* Object IDs, use pObjIds. */
4472 case RTCRPKCS7ATTRIBUTETYPE_OBJ_IDS:
4473 if (pAttr->uValues.pObjIds->cItems != 1)
4474 RTPrintf("%s%u object IDs:", pThis->szPrefix, pAttr->uValues.pObjIds->cItems);
4475 for (unsigned i = 0; i < pAttr->uValues.pObjIds->cItems; i++)
4476 {
4477 if (pAttr->uValues.pObjIds->cItems == 1)
4478 RTPrintf("%s ", pThis->szPrefix);
4479 else
4480 RTPrintf("%s ObjId[%u]: ", pThis->szPrefix, i);
4481 HandleShowExeWorkerDisplayObjIdSimple(pThis, pAttr->uValues.pObjIds->papItems[i], "\n");
4482 }
4483 break;
4484
4485 /* Sequence of object IDs, use pObjIdSeqs. */
4486 case RTCRPKCS7ATTRIBUTETYPE_MS_STATEMENT_TYPE:
4487 if (pAttr->uValues.pObjIdSeqs->cItems != 1)
4488 RTPrintf("%s%u object IDs:", pThis->szPrefix, pAttr->uValues.pObjIdSeqs->cItems);
4489 for (unsigned i = 0; i < pAttr->uValues.pObjIdSeqs->cItems; i++)
4490 {
4491 uint32_t const cObjIds = pAttr->uValues.pObjIdSeqs->papItems[i]->cItems;
4492 for (unsigned j = 0; j < cObjIds; j++)
4493 {
4494 if (pAttr->uValues.pObjIdSeqs->cItems == 1)
4495 RTPrintf("%s ", pThis->szPrefix);
4496 else
4497 RTPrintf("%s ObjIdSeq[%u]: ", pThis->szPrefix, i);
4498 if (cObjIds != 1)
4499 RTPrintf(" ObjId[%u]: ", j);
4500 HandleShowExeWorkerDisplayObjIdSimple(pThis, pAttr->uValues.pObjIdSeqs->papItems[i]