VirtualBox

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

Last change on this file since 100594 was 100442, checked in by vboxsync, 17 months ago

IPRT,OpenSSL: Support ECDSA for verficiation purposes when IPRT links with OpenSSL. This required quite a bit of cleanups, so not entirely no-risk. bugref:10479 ticketref:21621

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