VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/crypto/pemfile-write.cpp@ 98859

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

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 9.3 KB
Line 
1/* $Id: pemfile-write.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * IPRT - Crypto - PEM file writer.
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 "internal/iprt.h"
42#include <iprt/crypto/pem.h>
43
44#include <iprt/asn1.h>
45#include <iprt/base64.h>
46#include <iprt/errcore.h>
47#include <iprt/string.h>
48#include <iprt/vfs.h>
49
50
51/*********************************************************************************************************************************
52* Structures and Typedefs *
53*********************************************************************************************************************************/
54/**
55 * Used by rtCrPemWriteAsn1Callback to buffer data before outputting it as
56 * BASE64.
57 *
58 * An encoded line is 64 characters long plus a newline, covering 48 bytes
59 * of binary data. We want about 4KB of output:
60 * 4096 / 65 = 63.015384615384615384615384615385
61 * 64 * 65 + 1 = 4161 (0x1041)
62 */
63typedef struct PEMOUTPUTASN1
64{
65 size_t cbPending;
66 PFNRTSTROUTPUT pfnOutput;
67 void *pvUser;
68 size_t cchRet;
69 uint8_t abBlock[0x0c00];
70 char szBlock[0x1060];
71} PEMOUTPUTASN1;
72typedef PEMOUTPUTASN1 *PPEMOUTPUTASN1;
73
74
75
76RTDECL(size_t) RTCrPemWriteBlob(PFNRTSTROUTPUT pfnOutput, void *pvUser,
77 const void *pvContent, size_t cbContent, const char *pszMarker)
78{
79 /*
80 * -----BEGIN XXXXX-----
81 */
82 size_t cchRet = pfnOutput(pvUser, RT_STR_TUPLE("-----BEGIN "));
83 size_t const cchMarker = strlen(pszMarker);
84 cchRet += pfnOutput(pvUser, pszMarker, cchMarker);
85 cchRet += pfnOutput(pvUser, RT_STR_TUPLE("-----\n"));
86
87 /*
88 * base64 - in reasonably sized stack blocks.
89 * An encoded line is 64 characters long plus a newline, covering 48 bytes
90 * of binary data. We want about 4KB of output:
91 * 4096 / 65 = 63.015384615384615384615384615385
92 * 64 * 65 + 1 = 4161 (0x1041)
93 */
94 const size_t cbMaxBlock = 64 * 48;
95 while (cbContent > 0)
96 {
97 char szBlock[0x1060];
98 size_t cbBlock = RT_MIN(cbContent, cbMaxBlock);
99 size_t cchBlock = 0;
100 int rc = RTBase64EncodeEx(pvContent, cbBlock, RTBASE64_FLAGS_EOL_LF,
101 szBlock, sizeof(szBlock), &cchBlock);
102 AssertRC(rc);
103 szBlock[cchBlock++] = '\n';
104 szBlock[cchBlock] = '\0';
105
106 cchRet += pfnOutput(pvUser, szBlock, cchBlock);
107
108 pvContent = (uint8_t const *)pvContent + cbBlock;
109 cbContent -= cbBlock;
110 }
111
112 /*
113 * -----END XXXXX-----
114 */
115 cchRet += pfnOutput(pvUser, RT_STR_TUPLE("-----END "));
116 cchRet += pfnOutput(pvUser, pszMarker, cchMarker);
117 cchRet += pfnOutput(pvUser, RT_STR_TUPLE("-----\n"));
118
119 /* termination call */
120 cchRet += pfnOutput(pvUser, NULL, 0);
121
122 return cchRet;
123}
124
125
126RTDECL(ssize_t) RTCrPemWriteBlobToVfsIoStrm(RTVFSIOSTREAM hVfsIos, const void *pvContent, size_t cbContent, const char *pszMarker)
127{
128 VFSIOSTRMOUTBUF Buf;
129 VFSIOSTRMOUTBUF_INIT(&Buf, hVfsIos);
130 size_t cchRet = RTCrPemWriteBlob(RTVfsIoStrmStrOutputCallback, &Buf, pvContent, cbContent, pszMarker);
131 Assert(Buf.offBuf == 0);
132 return RT_SUCCESS(Buf.rc) ? (ssize_t)cchRet : Buf.rc;
133}
134
135
136RTDECL(ssize_t) RTCrPemWriteBlobToVfsFile(RTVFSFILE hVfsFile, const void *pvContent, size_t cbContent, const char *pszMarker)
137{
138 RTVFSIOSTREAM hVfsIos = RTVfsFileToIoStream(hVfsFile);
139 AssertReturn(hVfsIos != NIL_RTVFSIOSTREAM, VERR_INVALID_HANDLE);
140 ssize_t cchRet = RTCrPemWriteBlobToVfsIoStrm(hVfsIos, pvContent, cbContent, pszMarker);
141 RTVfsIoStrmRelease(hVfsIos);
142 return cchRet;
143}
144
145
146/** @callback_method_impl{FNRTASN1ENCODEWRITER} */
147static DECLCALLBACK(int) rtCrPemWriteAsn1Callback(const void *pvBuf, size_t cbToWrite, void *pvUser, PRTERRINFO pErrInfo)
148{
149 PPEMOUTPUTASN1 pThis = (PPEMOUTPUTASN1)pvUser;
150 AssertCompile((sizeof(pThis->abBlock) % 48) == 0);
151
152 while (cbToWrite > 0)
153 {
154 size_t offDst = pThis->cbPending;
155 AssertStmt(offDst <= sizeof(pThis->abBlock), offDst = sizeof(pThis->abBlock));
156 size_t cbDst = sizeof(pThis->abBlock) - offDst;
157 if (cbToWrite < cbDst)
158 {
159 /* Buffer not full: Append and return. */
160 memcpy(&pThis->abBlock[offDst], pvBuf, cbToWrite);
161 pThis->cbPending = offDst + cbToWrite;
162 break;
163 }
164
165 /* Fill the buffer and flush it: */
166 memcpy(&pThis->abBlock[offDst], pvBuf, cbDst);
167 Assert(offDst + cbDst == sizeof(pThis->abBlock));
168
169 size_t cchBlock = 0;
170 int rc = RTBase64EncodeEx(pThis->abBlock, sizeof(pThis->abBlock), RTBASE64_FLAGS_EOL_LF,
171 pThis->szBlock, sizeof(pThis->szBlock), &cchBlock);
172 AssertRC(rc);
173 pThis->szBlock[cchBlock++] = '\n';
174 pThis->szBlock[cchBlock] = '\0';
175
176 pThis->cchRet += pThis->pfnOutput(pThis->pvUser, pThis->szBlock, cchBlock);
177 pThis->cbPending = 0;
178
179 /* Advance. */
180 pvBuf = (uint8_t const *)pvBuf + cbDst;
181 cbToWrite -= cbDst;
182 }
183
184 RT_NOREF(pErrInfo);
185 return VINF_SUCCESS;
186}
187
188
189RTDECL(ssize_t) RTCrPemWriteAsn1(PFNRTSTROUTPUT pfnOutput, void *pvUser, PRTASN1CORE pRoot,
190 uint32_t fFlags, const char *pszMarker, PRTERRINFO pErrInfo)
191{
192 AssertReturn(!fFlags, VERR_INVALID_FLAGS);
193
194 /*
195 * Prepare the ASN.1 data for DER encoding.
196 */
197 int rc = RTAsn1EncodePrepare(pRoot, RTASN1ENCODE_F_DER, NULL /*pcbEncoded*/, pErrInfo);
198 AssertRCReturn(rc, rc);
199
200 /*
201 * -----BEGIN XXXXX-----
202 */
203 size_t cchRet = pfnOutput(pvUser, RT_STR_TUPLE("-----BEGIN "));
204 size_t const cchMarker = strlen(pszMarker);
205 cchRet += pfnOutput(pvUser, pszMarker, cchMarker);
206 cchRet += pfnOutput(pvUser, RT_STR_TUPLE("-----\n"));
207
208 /*
209 * BASE64
210 */
211 PEMOUTPUTASN1 This;
212 This.pfnOutput = pfnOutput;
213 This.pvUser = pvUser;
214 This.cchRet = 0;
215 This.cbPending = 0;
216 rc = RTAsn1EncodeWrite(pRoot, RTASN1ENCODE_F_DER, rtCrPemWriteAsn1Callback, &This, pErrInfo);
217 AssertRCReturn(rc, rc);
218 cchRet += This.cchRet;
219
220 Assert(This.cbPending <= sizeof(This.abBlock));
221 if (This.cbPending)
222 {
223 size_t cchBlock = 0;
224 rc = RTBase64EncodeEx(This.abBlock, This.cbPending, RTBASE64_FLAGS_EOL_LF,
225 This.szBlock, sizeof(This.szBlock), &cchBlock);
226 AssertRC(rc);
227 This.szBlock[cchBlock++] = '\n';
228 This.szBlock[cchBlock] = '\0';
229
230 cchRet += pfnOutput(pvUser, This.szBlock, cchBlock);
231 }
232
233 /*
234 * -----END XXXXX-----
235 */
236 cchRet += pfnOutput(pvUser, RT_STR_TUPLE("-----END "));
237 cchRet += pfnOutput(pvUser, pszMarker, cchMarker);
238 cchRet += pfnOutput(pvUser, RT_STR_TUPLE("-----\n"));
239
240 /* termination call */
241 cchRet += pfnOutput(pvUser, NULL, 0);
242
243 return cchRet;
244}
245
246
247RTDECL(ssize_t) RTCrPemWriteAsn1ToVfsIoStrm(RTVFSIOSTREAM hVfsIos, PRTASN1CORE pRoot,
248 uint32_t fFlags, const char *pszMarker, PRTERRINFO pErrInfo)
249{
250 VFSIOSTRMOUTBUF Buf;
251 VFSIOSTRMOUTBUF_INIT(&Buf, hVfsIos);
252 ssize_t cchRet = RTCrPemWriteAsn1(RTVfsIoStrmStrOutputCallback, &Buf, pRoot, fFlags, pszMarker, pErrInfo);
253 Assert(Buf.offBuf == 0);
254 return RT_SUCCESS(Buf.rc) ? (ssize_t)cchRet : Buf.rc;
255}
256
257
258RTDECL(ssize_t) RTCrPemWriteAsn1ToVfsFile(RTVFSFILE hVfsFile, PRTASN1CORE pRoot,
259 uint32_t fFlags, const char *pszMarker, PRTERRINFO pErrInfo)
260{
261 RTVFSIOSTREAM hVfsIos = RTVfsFileToIoStream(hVfsFile);
262 AssertReturn(hVfsIos != NIL_RTVFSIOSTREAM, VERR_INVALID_HANDLE);
263 ssize_t cchRet = RTCrPemWriteAsn1ToVfsIoStrm(hVfsIos, pRoot, fFlags, pszMarker, pErrInfo);
264 RTVfsIoStrmRelease(hVfsIos);
265 return cchRet;
266}
Note: See TracBrowser for help on using the repository browser.

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