1 /* armor.c - Armor filters
2 * Copyright (C) 1998-2012 Free Software Foundation, Inc.
6 * This file is part of OpenCDK.
8 * The OpenCDK library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * as published by the Free Software Foundation; either version 3 of
11 * the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>
43 #define CRCINIT 0xB704CE
45 static u32 crc_table
[] = {
46 0x000000, 0x864CFB, 0x8AD50D, 0x0C99F6, 0x93E6E1, 0x15AA1A, 0x1933EC,
47 0x9F7F17, 0xA18139, 0x27CDC2, 0x2B5434, 0xAD18CF, 0x3267D8, 0xB42B23,
48 0xB8B2D5, 0x3EFE2E, 0xC54E89, 0x430272, 0x4F9B84, 0xC9D77F, 0x56A868,
49 0xD0E493, 0xDC7D65, 0x5A319E, 0x64CFB0, 0xE2834B, 0xEE1ABD, 0x685646,
50 0xF72951, 0x7165AA, 0x7DFC5C, 0xFBB0A7, 0x0CD1E9, 0x8A9D12, 0x8604E4,
51 0x00481F, 0x9F3708, 0x197BF3, 0x15E205, 0x93AEFE, 0xAD50D0, 0x2B1C2B,
52 0x2785DD, 0xA1C926, 0x3EB631, 0xB8FACA, 0xB4633C, 0x322FC7, 0xC99F60,
53 0x4FD39B, 0x434A6D, 0xC50696, 0x5A7981, 0xDC357A, 0xD0AC8C, 0x56E077,
54 0x681E59, 0xEE52A2, 0xE2CB54, 0x6487AF, 0xFBF8B8, 0x7DB443, 0x712DB5,
55 0xF7614E, 0x19A3D2, 0x9FEF29, 0x9376DF, 0x153A24, 0x8A4533, 0x0C09C8,
56 0x00903E, 0x86DCC5, 0xB822EB, 0x3E6E10, 0x32F7E6, 0xB4BB1D, 0x2BC40A,
57 0xAD88F1, 0xA11107, 0x275DFC, 0xDCED5B, 0x5AA1A0, 0x563856, 0xD074AD,
58 0x4F0BBA, 0xC94741, 0xC5DEB7, 0x43924C, 0x7D6C62, 0xFB2099, 0xF7B96F,
59 0x71F594, 0xEE8A83, 0x68C678, 0x645F8E, 0xE21375, 0x15723B, 0x933EC0,
60 0x9FA736, 0x19EBCD, 0x8694DA, 0x00D821, 0x0C41D7, 0x8A0D2C, 0xB4F302,
61 0x32BFF9, 0x3E260F, 0xB86AF4, 0x2715E3, 0xA15918, 0xADC0EE, 0x2B8C15,
62 0xD03CB2, 0x567049, 0x5AE9BF, 0xDCA544, 0x43DA53, 0xC596A8, 0xC90F5E,
63 0x4F43A5, 0x71BD8B, 0xF7F170, 0xFB6886, 0x7D247D, 0xE25B6A, 0x641791,
64 0x688E67, 0xEEC29C, 0x3347A4, 0xB50B5F, 0xB992A9, 0x3FDE52, 0xA0A145,
65 0x26EDBE, 0x2A7448, 0xAC38B3, 0x92C69D, 0x148A66, 0x181390, 0x9E5F6B,
66 0x01207C, 0x876C87, 0x8BF571, 0x0DB98A, 0xF6092D, 0x7045D6, 0x7CDC20,
67 0xFA90DB, 0x65EFCC, 0xE3A337, 0xEF3AC1, 0x69763A, 0x578814, 0xD1C4EF,
68 0xDD5D19, 0x5B11E2, 0xC46EF5, 0x42220E, 0x4EBBF8, 0xC8F703, 0x3F964D,
69 0xB9DAB6, 0xB54340, 0x330FBB, 0xAC70AC, 0x2A3C57, 0x26A5A1, 0xA0E95A,
70 0x9E1774, 0x185B8F, 0x14C279, 0x928E82, 0x0DF195, 0x8BBD6E, 0x872498,
71 0x016863, 0xFAD8C4, 0x7C943F, 0x700DC9, 0xF64132, 0x693E25, 0xEF72DE,
72 0xE3EB28, 0x65A7D3, 0x5B59FD, 0xDD1506, 0xD18CF0, 0x57C00B, 0xC8BF1C,
73 0x4EF3E7, 0x426A11, 0xC426EA, 0x2AE476, 0xACA88D, 0xA0317B, 0x267D80,
74 0xB90297, 0x3F4E6C, 0x33D79A, 0xB59B61, 0x8B654F, 0x0D29B4, 0x01B042,
75 0x87FCB9, 0x1883AE, 0x9ECF55, 0x9256A3, 0x141A58, 0xEFAAFF, 0x69E604,
76 0x657FF2, 0xE33309, 0x7C4C1E, 0xFA00E5, 0xF69913, 0x70D5E8, 0x4E2BC6,
77 0xC8673D, 0xC4FECB, 0x42B230, 0xDDCD27, 0x5B81DC, 0x57182A, 0xD154D1,
78 0x26359F, 0xA07964, 0xACE092, 0x2AAC69, 0xB5D37E, 0x339F85, 0x3F0673,
79 0xB94A88, 0x87B4A6, 0x01F85D, 0x0D61AB, 0x8B2D50, 0x145247, 0x921EBC,
80 0x9E874A, 0x18CBB1, 0xE37B16, 0x6537ED, 0x69AE1B, 0xEFE2E0, 0x709DF7,
81 0xF6D10C, 0xFA48FA, 0x7C0401, 0x42FA2F, 0xC4B6D4, 0xC82F22, 0x4E63D9,
82 0xD11CCE, 0x575035, 0x5BC9C3, 0xDD8538
85 static const char *armor_begin
[] = {
87 "BEGIN PGP PUBLIC KEY BLOCK",
88 "BEGIN PGP PRIVATE KEY BLOCK",
89 "BEGIN PGP SIGNATURE",
93 static const char *armor_end
[] = {
95 "END PGP PUBLIC KEY BLOCK",
96 "END PGP PRIVATE KEY BLOCK",
101 static const char *valid_headers
[] = {
110 static char b64chars
[] =
111 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
113 /* Return the compression algorithm in @r_zipalgo.
114 If the parameter is not set after execution,
115 the stream is not compressed. */
117 compress_get_algo (cdk_stream_t inp
, int *r_zipalgo
)
125 cdk_stream_seek (inp
, 0);
126 while (!cdk_stream_eof (inp
))
128 nread
= _cdk_stream_gets (inp
, buf
, DIM (buf
) - 1);
129 if (!nread
|| nread
== -1)
131 if (nread
== 1 && !cdk_stream_eof (inp
)
132 && (nread
= _cdk_stream_gets (inp
, buf
, DIM (buf
) - 1)) > 0)
134 plain_size
= sizeof(plain
);
135 base64_decode (buf
, nread
, (char*)plain
, &plain_size
);
136 if (!(*plain
& 0x80))
138 pkttype
= *plain
& 0x40 ? (*plain
& 0x3f) : ((*plain
>> 2) & 0xf);
139 if (pkttype
== CDK_PKT_COMPRESSED
&& r_zipalgo
)
141 _gnutls_buffers_log ("armor compressed (algo=%d)\n",
143 *r_zipalgo
= *(plain
+ 1);
153 check_armor (cdk_stream_t inp
, int *r_zipalgo
)
160 nread
= cdk_stream_read (inp
, buf
, DIM (buf
) - 1);
164 if (strstr (buf
, "-----BEGIN PGP"))
166 compress_get_algo (inp
, r_zipalgo
);
169 cdk_stream_seek (inp
, 0);
183 return 1; /* invalid packet: assume it is armored */
185 pkttype
= ctb
& 0x40 ? (ctb
& 0x3f) : ((ctb
>> 2) & 0xf);
189 case CDK_PKT_ONEPASS_SIG
:
190 case CDK_PKT_PUBLIC_KEY
:
191 case CDK_PKT_SECRET_KEY
:
192 case CDK_PKT_PUBKEY_ENC
:
193 case CDK_PKT_SIGNATURE
:
194 case CDK_PKT_LITERAL
:
195 case CDK_PKT_COMPRESSED
:
196 return 0; /* seems to be a regular packet: not armored */
203 update_crc (u32 crc
, const byte
* buf
, size_t buflen
)
210 for (j
= 0; j
< buflen
; j
++)
211 crc
= (crc
<< 8) ^ crc_table
[0xff & ((crc
>> 16) ^ buf
[j
])];
218 armor_encode (void *data
, FILE * in
, FILE * out
)
220 armor_filter_t
*afx
= data
;
222 char crcbuf
[5], buf
[128], raw
[49];
230 return CDK_Inv_Value
;
232 if (afx
->idx
< 0 || afx
->idx
> (int) DIM (armor_begin
) ||
233 afx
->idx2
< 0 || afx
->idx2
> (int) DIM (armor_end
))
236 return CDK_Inv_Value
;
239 _gnutls_buffers_log ("armor filter: encode\n");
241 memset (crcbuf
, 0, sizeof (crcbuf
));
243 lf
= afx
->le
? afx
->le
: LF
;
244 fprintf (out
, "-----%s-----%s", armor_begin
[afx
->idx
], lf
);
245 fprintf (out
, "Version: OpenPrivacy " PACKAGE_VERSION
"%s", lf
);
247 fwrite (afx
->hdrlines
, 1, strlen (afx
->hdrlines
), out
);
248 fprintf (out
, "%s", lf
);
250 if (fstat (fileno (in
), &statbuf
))
253 return CDK_General_Error
;
258 nread
= fread (raw
, 1, DIM (raw
) - 1, in
);
264 return CDK_File_Error
;
266 afx
->crc
= update_crc (afx
->crc
, (byte
*) raw
, nread
);
267 base64_encode (raw
, nread
, buf
, DIM (buf
) - 1);
268 fprintf (out
, "%s%s", buf
, lf
);
271 crcbuf2
[0] = afx
->crc
>> 16;
272 crcbuf2
[1] = afx
->crc
>> 8;
273 crcbuf2
[2] = afx
->crc
;
274 crcbuf
[0] = b64chars
[crcbuf2
[0] >> 2];
275 crcbuf
[1] = b64chars
[((crcbuf2
[0] << 4) & 0x30) | (crcbuf2
[1] >> 4)];
276 crcbuf
[2] = b64chars
[((crcbuf2
[1] << 2) & 0x3c) | (crcbuf2
[2] >> 6)];
277 crcbuf
[3] = b64chars
[crcbuf2
[2] & 0x3f];
278 fprintf (out
, "=%s%s", crcbuf
, lf
);
279 fprintf (out
, "-----%s-----%s", armor_end
[afx
->idx2
], lf
);
285 search_header (const char *buf
, const char **array
)
290 if (strlen (buf
) < 5 || strncmp (buf
, "-----", 5))
294 for (i
= 0; (s
= array
[i
]); i
++)
296 if (!strncmp (s
, buf
+ 5, strlen (s
)))
303 armor_decode (void *data
, FILE * in
, FILE * out
)
305 armor_filter_t
*afx
= data
;
308 byte raw
[128], crcbuf
[4];
314 size_t raw_size
, crcbuf_size
;
319 return CDK_Inv_Value
;
322 _gnutls_buffers_log ("armor filter: decode\n");
324 fseek (in
, 0, SEEK_SET
);
325 /* Search the begin of the message */
326 while (!feof (in
) && !pgp_data
)
328 s
= fgets (buf
, DIM (buf
) - 1, in
);
331 afx
->idx
= search_header (buf
, armor_begin
);
336 if (feof (in
) || !pgp_data
)
338 return CDK_Armor_Error
; /* no data found */
341 /* Parse header until the empty line is reached */
344 s
= fgets (buf
, DIM (buf
) - 1, in
);
347 if (strcmp (s
, LF
) == 0 || strcmp (s
, ALTLF
) == 0)
350 break; /* empty line */
352 /* From RFC2440: OpenPGP should consider improperly formatted Armor
353 Headers to be corruption of the ASCII Armor. A colon and a single
354 space separate the key and value. */
355 if (!strstr (buf
, ": "))
358 return CDK_Armor_Error
;
360 rc
= CDK_General_Error
;
361 for (i
= 0; (s
= valid_headers
[i
]); i
++)
363 if (!strncmp (s
, buf
, strlen (s
)))
368 /* From RFC2440: Unknown keys should be reported to the user,
369 but OpenPGP should continue to process the message. */
370 _cdk_log_info ("unknown header: `%s'\n", buf
);
375 /* Read the data body */
378 s
= fgets (buf
, DIM (buf
) - 1, in
);
384 if (len
> 0 && buf
[len
- 1] == '\n')
389 if (len
> 0 && buf
[len
- 1] == '\r')
394 if (buf
[0] == '=' && strlen (s
) == 5)
396 memset (crcbuf
, 0, sizeof (crcbuf
));
397 crcbuf_size
= sizeof(crcbuf
);
398 base64_decode (buf
+ 1, len
-1, (char*)crcbuf
, &crcbuf_size
);
399 crc2
= (crcbuf
[0] << 16) | (crcbuf
[1] << 8) | crcbuf
[2];
400 break; /* stop here */
404 raw_size
= sizeof(raw
);
405 nread
= base64_decode (buf
, len
, (char*)raw
, &raw_size
);
408 afx
->crc
= update_crc (afx
->crc
, raw
, raw_size
);
409 fwrite (raw
, 1, raw_size
, out
);
413 /* Search the tail of the message */
414 s
= fgets (buf
, DIM (buf
) - 1, in
);
417 int len
= strlen(buf
);
418 if (buf
[len
- 1] == '\n')
423 if (buf
[len
- 1] == '\r')
428 rc
= CDK_General_Error
;
429 afx
->idx2
= search_header (buf
, armor_end
);
434 /* This catches error when no tail was found or the header is
435 different then the tail line. */
436 if (rc
|| afx
->idx
!= afx
->idx2
)
437 rc
= CDK_Armor_Error
;
439 afx
->crc_okay
= (afx
->crc
== crc2
) ? 1 : 0;
440 if (!afx
->crc_okay
&& !rc
)
442 _gnutls_buffers_log ("file crc=%08X afx_crc=%08X\n",
443 (unsigned int) crc2
, (unsigned int) afx
->crc
);
444 rc
= CDK_Armor_CRC_Error
;
451 _cdk_filter_armor (void *data
, int ctl
, FILE * in
, FILE * out
)
453 if (ctl
== STREAMCTL_READ
)
454 return armor_decode (data
, in
, out
);
455 else if (ctl
== STREAMCTL_WRITE
)
456 return armor_encode (data
, in
, out
);
457 else if (ctl
== STREAMCTL_FREE
)
459 armor_filter_t
*afx
= data
;
462 _gnutls_buffers_log ("free armor filter\n");
463 afx
->idx
= afx
->idx2
= 0;
464 afx
->crc
= afx
->crc_okay
= 0;
475 * cdk_armor_encode_buffer:
476 * @inbuf: the raw input buffer
477 * @inlen: raw buffer len
478 * @outbuf: the destination buffer for the base64 output
479 * @outlen: destination buffer len
480 * @nwritten: actual length of the base64 data
481 * @type: the base64 file type.
483 * Encode the given buffer into base64 format. The base64
484 * string will be null terminated but the null will
485 * not be contained in the size.
488 cdk_armor_encode_buffer (const byte
* inbuf
, size_t inlen
,
489 char *outbuf
, size_t outlen
,
490 size_t * nwritten
, int type
)
492 const char *head
, *tail
, *le
;
495 size_t pos
, off
, len
, rest
;
497 if (!inbuf
|| !nwritten
)
500 return CDK_Inv_Value
;
502 if (type
> CDK_ARMOR_SIGNATURE
)
508 head
= armor_begin
[type
];
509 tail
= armor_end
[type
];
511 pos
= strlen (head
) + 10 + 2 + 2 + strlen (tail
) + 10 + 2 + 5 + 2 + 1;
512 /* The output data is 4/3 times larger, plus a line end for each line. */
513 pos
+= (4 * inlen
/ 3) + 2 * (4 * inlen
/ 3 / 64) + 1;
515 if (outbuf
&& outlen
< pos
)
519 return CDK_Too_Short
;
522 /* Only return the size of the output. */
529 memset (outbuf
, 0, outlen
);
530 memcpy (outbuf
, "-----", 5);
532 memcpy (outbuf
+ pos
, head
, strlen (head
));
533 pos
+= strlen (head
);
534 memcpy (outbuf
+ pos
, "-----", 5);
536 memcpy (outbuf
+ pos
, le
, strlen (le
));
538 memcpy (outbuf
+ pos
, le
, strlen (le
));
541 for (off
= 0; off
< inlen
;)
545 memcpy (tempbuf
, inbuf
+ off
, 48);
551 memcpy (tempbuf
, inbuf
+ off
, rest
);
556 base64_encode (tempbuf
, len
, tempout
, DIM (tempout
) - 1);
557 memcpy (outbuf
+ pos
, tempout
, strlen (tempout
));
558 pos
+= strlen (tempout
);
559 memcpy (outbuf
+ pos
, le
, strlen (le
));
563 memcpy (outbuf
+ pos
, "-----", 5);
565 memcpy (outbuf
+ pos
, tail
, strlen (tail
));
566 pos
+= strlen (tail
);
567 memcpy (outbuf
+ pos
, "-----", 5);
569 memcpy (outbuf
+ pos
, le
, strlen (le
));