1 /* armor.c - Armor filters
2 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2007, 2008, 2010
3 * Free Software Foundation, Inc.
7 * This file is part of OpenCDK.
9 * The OpenCDK library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1 of
12 * the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
24 * ChangeLog for basic BASE64 code (base64_encode, base64_decode):
25 * Original author: Eric S. Raymond (Fetchmail)
26 * Heavily modified by Brendan Cully <brendan@kublai.com> (Mutt)
27 * Modify the code for generic use by Timo Schulz <twoaday@freakmail.de>
49 #define CRCINIT 0xB704CE
52 #define b64val(c) index64[(unsigned int)(c)]
54 static u32 crc_table
[] = {
55 0x000000, 0x864CFB, 0x8AD50D, 0x0C99F6, 0x93E6E1, 0x15AA1A, 0x1933EC,
57 0xA18139, 0x27CDC2, 0x2B5434, 0xAD18CF, 0x3267D8, 0xB42B23, 0xB8B2D5,
59 0xC54E89, 0x430272, 0x4F9B84, 0xC9D77F, 0x56A868, 0xD0E493, 0xDC7D65,
61 0x64CFB0, 0xE2834B, 0xEE1ABD, 0x685646, 0xF72951, 0x7165AA, 0x7DFC5C,
63 0x0CD1E9, 0x8A9D12, 0x8604E4, 0x00481F, 0x9F3708, 0x197BF3, 0x15E205,
65 0xAD50D0, 0x2B1C2B, 0x2785DD, 0xA1C926, 0x3EB631, 0xB8FACA, 0xB4633C,
67 0xC99F60, 0x4FD39B, 0x434A6D, 0xC50696, 0x5A7981, 0xDC357A, 0xD0AC8C,
69 0x681E59, 0xEE52A2, 0xE2CB54, 0x6487AF, 0xFBF8B8, 0x7DB443, 0x712DB5,
71 0x19A3D2, 0x9FEF29, 0x9376DF, 0x153A24, 0x8A4533, 0x0C09C8, 0x00903E,
73 0xB822EB, 0x3E6E10, 0x32F7E6, 0xB4BB1D, 0x2BC40A, 0xAD88F1, 0xA11107,
75 0xDCED5B, 0x5AA1A0, 0x563856, 0xD074AD, 0x4F0BBA, 0xC94741, 0xC5DEB7,
77 0x7D6C62, 0xFB2099, 0xF7B96F, 0x71F594, 0xEE8A83, 0x68C678, 0x645F8E,
79 0x15723B, 0x933EC0, 0x9FA736, 0x19EBCD, 0x8694DA, 0x00D821, 0x0C41D7,
81 0xB4F302, 0x32BFF9, 0x3E260F, 0xB86AF4, 0x2715E3, 0xA15918, 0xADC0EE,
83 0xD03CB2, 0x567049, 0x5AE9BF, 0xDCA544, 0x43DA53, 0xC596A8, 0xC90F5E,
85 0x71BD8B, 0xF7F170, 0xFB6886, 0x7D247D, 0xE25B6A, 0x641791, 0x688E67,
87 0x3347A4, 0xB50B5F, 0xB992A9, 0x3FDE52, 0xA0A145, 0x26EDBE, 0x2A7448,
89 0x92C69D, 0x148A66, 0x181390, 0x9E5F6B, 0x01207C, 0x876C87, 0x8BF571,
91 0xF6092D, 0x7045D6, 0x7CDC20, 0xFA90DB, 0x65EFCC, 0xE3A337, 0xEF3AC1,
93 0x578814, 0xD1C4EF, 0xDD5D19, 0x5B11E2, 0xC46EF5, 0x42220E, 0x4EBBF8,
95 0x3F964D, 0xB9DAB6, 0xB54340, 0x330FBB, 0xAC70AC, 0x2A3C57, 0x26A5A1,
97 0x9E1774, 0x185B8F, 0x14C279, 0x928E82, 0x0DF195, 0x8BBD6E, 0x872498,
99 0xFAD8C4, 0x7C943F, 0x700DC9, 0xF64132, 0x693E25, 0xEF72DE, 0xE3EB28,
101 0x5B59FD, 0xDD1506, 0xD18CF0, 0x57C00B, 0xC8BF1C, 0x4EF3E7, 0x426A11,
103 0x2AE476, 0xACA88D, 0xA0317B, 0x267D80, 0xB90297, 0x3F4E6C, 0x33D79A,
105 0x8B654F, 0x0D29B4, 0x01B042, 0x87FCB9, 0x1883AE, 0x9ECF55, 0x9256A3,
107 0xEFAAFF, 0x69E604, 0x657FF2, 0xE33309, 0x7C4C1E, 0xFA00E5, 0xF69913,
109 0x4E2BC6, 0xC8673D, 0xC4FECB, 0x42B230, 0xDDCD27, 0x5B81DC, 0x57182A,
111 0x26359F, 0xA07964, 0xACE092, 0x2AAC69, 0xB5D37E, 0x339F85, 0x3F0673,
113 0x87B4A6, 0x01F85D, 0x0D61AB, 0x8B2D50, 0x145247, 0x921EBC, 0x9E874A,
115 0xE37B16, 0x6537ED, 0x69AE1B, 0xEFE2E0, 0x709DF7, 0xF6D10C, 0xFA48FA,
117 0x42FA2F, 0xC4B6D4, 0xC82F22, 0x4E63D9, 0xD11CCE, 0x575035, 0x5BC9C3,
121 static const char *armor_begin
[] = {
123 "BEGIN PGP PUBLIC KEY BLOCK",
124 "BEGIN PGP PRIVATE KEY BLOCK",
125 "BEGIN PGP SIGNATURE",
129 static const char *armor_end
[] = {
131 "END PGP PUBLIC KEY BLOCK",
132 "END PGP PRIVATE KEY BLOCK",
137 static const char *valid_headers
[] = {
146 static char b64chars
[] =
147 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
149 static int index64
[128] = {
150 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
151 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
152 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
153 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
154 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
155 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
156 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
157 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1
161 /* encode a raw binary buffer to a null-terminated base64 strings */
163 base64_encode (char *out
, const byte
* in
, size_t len
, size_t olen
)
168 return CDK_Inv_Value
;
171 while (len
>= 3 && olen
> 10)
173 *out
++ = b64chars
[in
[0] >> 2];
174 *out
++ = b64chars
[((in
[0] << 4) & 0x30) | (in
[1] >> 4)];
175 *out
++ = b64chars
[((in
[1] << 2) & 0x3c) | (in
[2] >> 6)];
176 *out
++ = b64chars
[in
[2] & 0x3f];
182 /* clean up remainder */
183 if (len
> 0 && olen
> 4)
186 *out
++ = b64chars
[in
[0] >> 2];
187 fragment
= (in
[0] << 4) & 0x30;
189 fragment
|= in
[1] >> 4;
190 *out
++ = b64chars
[fragment
];
191 *out
++ = (len
< 2) ? '=' : b64chars
[(in
[1] << 2) & 0x3c];
199 /* Convert '\0'-terminated base64 string to raw byte buffer.
200 Returns length of returned buffer, or -1 on error. */
202 base64_decode (byte
* out
, const char *in
)
205 byte digit1
, digit2
, digit3
, digit4
;
217 if (digit1
> 127 || b64val (digit1
) == BAD
)
223 if (digit2
> 127 || b64val (digit2
) == BAD
)
229 if (digit3
> 127 || ((digit3
!= '=') && (b64val (digit3
) == BAD
)))
235 if (digit4
> 127 || ((digit4
!= '=') && (b64val (digit4
) == BAD
)))
242 /* digits are already sanity-checked */
243 *out
++ = (b64val (digit1
) << 2) | (b64val (digit2
) >> 4);
247 *out
++ = ((b64val (digit2
) << 4) & 0xf0) | (b64val (digit3
) >> 2);
251 *out
++ = ((b64val (digit3
) << 6) & 0xc0) | b64val (digit4
);
256 while (*in
&& digit4
!= '=');
262 /* Return the compression algorithm in @r_zipalgo.
263 If the parameter is not set after execution,
264 the stream is not compressed. */
266 compress_get_algo (cdk_stream_t inp
, int *r_zipalgo
)
273 cdk_stream_seek (inp
, 0);
274 while (!cdk_stream_eof (inp
))
276 nread
= _cdk_stream_gets (inp
, buf
, DIM (buf
) - 1);
277 if (!nread
|| nread
== -1)
279 if (nread
== 1 && !cdk_stream_eof (inp
)
280 && (nread
= _cdk_stream_gets (inp
, buf
, DIM (buf
) - 1)) > 0)
282 base64_decode (plain
, buf
);
283 if (!(*plain
& 0x80))
285 pkttype
= *plain
& 0x40 ? (*plain
& 0x3f) : ((*plain
>> 2) & 0xf);
286 if (pkttype
== CDK_PKT_COMPRESSED
&& r_zipalgo
)
288 _gnutls_buffers_log ("armor compressed (algo=%d)\n",
290 *r_zipalgo
= *(plain
+ 1);
300 check_armor (cdk_stream_t inp
, int *r_zipalgo
)
307 nread
= cdk_stream_read (inp
, buf
, DIM (buf
) - 1);
311 if (strstr (buf
, "-----BEGIN PGP"))
313 compress_get_algo (inp
, r_zipalgo
);
316 cdk_stream_seek (inp
, 0);
330 return 1; /* invalid packet: assume it is armored */
332 pkttype
= ctb
& 0x40 ? (ctb
& 0x3f) : ((ctb
>> 2) & 0xf);
336 case CDK_PKT_ONEPASS_SIG
:
337 case CDK_PKT_PUBLIC_KEY
:
338 case CDK_PKT_SECRET_KEY
:
339 case CDK_PKT_PUBKEY_ENC
:
340 case CDK_PKT_SIGNATURE
:
341 case CDK_PKT_LITERAL
:
342 case CDK_PKT_COMPRESSED
:
343 return 0; /* seems to be a regular packet: not armored */
350 update_crc (u32 crc
, const byte
* buf
, size_t buflen
)
357 for (j
= 0; j
< buflen
; j
++)
358 crc
= (crc
<< 8) ^ crc_table
[0xff & ((crc
>> 16) ^ buf
[j
])];
365 armor_encode (void *data
, FILE * in
, FILE * out
)
367 armor_filter_t
*afx
= data
;
369 char crcbuf
[5], buf
[128], raw
[49];
377 return CDK_Inv_Value
;
379 if (afx
->idx
< 0 || afx
->idx
> (int) DIM (armor_begin
) ||
380 afx
->idx2
< 0 || afx
->idx2
> (int) DIM (armor_end
))
383 return CDK_Inv_Value
;
386 _gnutls_buffers_log ("armor filter: encode\n");
388 memset (crcbuf
, 0, sizeof (crcbuf
));
390 lf
= afx
->le
? afx
->le
: LF
;
391 fprintf (out
, "-----%s-----%s", armor_begin
[afx
->idx
], lf
);
392 fprintf (out
, "Version: OpenPrivacy " PACKAGE_VERSION
"%s", lf
);
394 fwrite (afx
->hdrlines
, 1, strlen (afx
->hdrlines
), out
);
395 fprintf (out
, "%s", lf
);
397 if (fstat (fileno (in
), &statbuf
))
400 return CDK_General_Error
;
405 nread
= fread (raw
, 1, DIM (raw
) - 1, in
);
411 return CDK_File_Error
;
413 afx
->crc
= update_crc (afx
->crc
, (byte
*) raw
, nread
);
414 base64_encode (buf
, (byte
*) raw
, nread
, DIM (buf
) - 1);
415 fprintf (out
, "%s%s", buf
, lf
);
418 crcbuf2
[0] = afx
->crc
>> 16;
419 crcbuf2
[1] = afx
->crc
>> 8;
420 crcbuf2
[2] = afx
->crc
;
421 crcbuf
[0] = b64chars
[crcbuf2
[0] >> 2];
422 crcbuf
[1] = b64chars
[((crcbuf2
[0] << 4) & 0x30) | (crcbuf2
[1] >> 4)];
423 crcbuf
[2] = b64chars
[((crcbuf2
[1] << 2) & 0x3c) | (crcbuf2
[2] >> 6)];
424 crcbuf
[3] = b64chars
[crcbuf2
[2] & 0x3f];
425 fprintf (out
, "=%s%s", crcbuf
, lf
);
426 fprintf (out
, "-----%s-----%s", armor_end
[afx
->idx2
], lf
);
433 * cdk_armor_filter_use:
434 * @inp: the stream to check
436 * Check if the stream contains armored data.
439 cdk_armor_filter_use (cdk_stream_t inp
)
445 c
= cdk_stream_getc (inp
);
447 return 0; /* EOF, doesn't matter whether armored or not */
448 cdk_stream_seek (inp
, 0);
449 check
= is_armored (c
);
452 check
= check_armor (inp
, &zipalgo
);
454 _cdk_stream_set_compress_algo (inp
, zipalgo
);
461 search_header (const char *buf
, const char **array
)
466 if (strlen (buf
) < 5 || strncmp (buf
, "-----", 5))
471 for (i
= 0; (s
= array
[i
]); i
++)
473 if (!strncmp (s
, buf
+ 5, strlen (s
)))
481 _cdk_armor_get_lineend (void)
488 armor_decode (void *data
, FILE * in
, FILE * out
)
490 armor_filter_t
*afx
= data
;
493 byte raw
[128], crcbuf
[4];
503 return CDK_Inv_Value
;
506 _gnutls_buffers_log ("armor filter: decode\n");
508 fseek (in
, 0, SEEK_SET
);
509 /* Search the begin of the message */
510 while (!feof (in
) && !pgp_data
)
512 s
= fgets (buf
, DIM (buf
) - 1, in
);
515 afx
->idx
= search_header (buf
, armor_begin
);
520 if (feof (in
) || !pgp_data
)
523 return CDK_Armor_Error
; /* no data found */
526 /* Parse header until the empty line is reached */
529 s
= fgets (buf
, DIM (buf
) - 1, in
);
532 if (strcmp (s
, LF
) == 0 || strcmp (s
, ALTLF
) == 0)
535 break; /* empty line */
537 /* From RFC2440: OpenPGP should consider improperly formatted Armor
538 Headers to be corruption of the ASCII Armor. A colon and a single
539 space separate the key and value. */
540 if (!strstr (buf
, ": "))
543 return CDK_Armor_Error
;
545 rc
= CDK_General_Error
;
546 for (i
= 0; (s
= valid_headers
[i
]); i
++)
548 if (!strncmp (s
, buf
, strlen (s
)))
553 /* From RFC2440: Unknown keys should be reported to the user,
554 but OpenPGP should continue to process the message. */
555 _cdk_log_info ("unknown header: `%s'\n", buf
);
560 /* Read the data body */
563 s
= fgets (buf
, DIM (buf
) - 1, in
);
569 if (buf
[len
- 1] == '\n')
571 if (buf
[len
- 1] == '\r')
573 if (buf
[0] == '=' && strlen (s
) == 5)
575 memset (crcbuf
, 0, sizeof (crcbuf
));
576 base64_decode (crcbuf
, buf
+ 1);
577 crc2
= (crcbuf
[0] << 16) | (crcbuf
[1] << 8) | crcbuf
[2];
578 break; /* stop here */
582 nread
= base64_decode (raw
, buf
);
583 if (nread
== -1 || nread
== 0)
585 afx
->crc
= update_crc (afx
->crc
, raw
, nread
);
586 fwrite (raw
, 1, nread
, out
);
590 /* Search the tail of the message */
591 s
= fgets (buf
, DIM (buf
) - 1, in
);
594 int len
= strlen(buf
);
595 if (buf
[len
- 1] == '\n')
597 if (buf
[len
- 1] == '\r')
599 rc
= CDK_General_Error
;
600 afx
->idx2
= search_header (buf
, armor_end
);
605 /* This catches error when no tail was found or the header is
606 different then the tail line. */
607 if (rc
|| afx
->idx
!= afx
->idx2
)
608 rc
= CDK_Armor_Error
;
610 afx
->crc_okay
= (afx
->crc
== crc2
) ? 1 : 0;
611 if (!afx
->crc_okay
&& !rc
)
613 _gnutls_buffers_log ("file crc=%08X afx_crc=%08X\n",
614 (unsigned int) crc2
, (unsigned int) afx
->crc
);
615 rc
= CDK_Armor_CRC_Error
;
625 * @file: Name of the file to protect.
626 * @output: Output filename.
628 * Protect a file with ASCII armor.
631 cdk_file_armor (cdk_ctx_t hd
, const char *file
, const char *output
)
633 cdk_stream_t inp
, out
;
636 rc
= _cdk_check_args (hd
->opt
.overwrite
, file
, output
);
640 rc
= cdk_stream_open (file
, &inp
);
647 rc
= cdk_stream_new (output
, &out
);
650 cdk_stream_close (inp
);
655 cdk_stream_set_armor_flag (out
, CDK_ARMOR_MESSAGE
);
656 if (hd
->opt
.compress
)
657 rc
= cdk_stream_set_compress_flag (out
, hd
->compress
.algo
,
660 rc
= cdk_stream_set_literal_flag (out
, 0, file
);
662 rc
= cdk_stream_kick_off (inp
, out
);
664 rc
= _cdk_stream_get_errno (out
);
666 cdk_stream_close (out
);
667 cdk_stream_close (inp
);
674 * @file: Name of the file to unprotect.
675 * @output: Output filename.
677 * Remove ASCII armor from a file.
680 cdk_file_dearmor (const char *file
, const char *output
)
682 cdk_stream_t inp
, out
;
686 rc
= _cdk_check_args (1, file
, output
);
693 rc
= cdk_stream_open (file
, &inp
);
700 rc
= cdk_stream_create (output
, &out
);
703 cdk_stream_close (inp
);
708 if (cdk_armor_filter_use (inp
))
710 rc
= cdk_stream_set_literal_flag (inp
, 0, NULL
);
711 zipalgo
= cdk_stream_is_compressed (inp
);
713 rc
= cdk_stream_set_compress_flag (inp
, zipalgo
, 0);
715 rc
= cdk_stream_set_armor_flag (inp
, 0);
717 rc
= cdk_stream_kick_off (inp
, out
);
719 rc
= _cdk_stream_get_errno (inp
);
722 cdk_stream_close (inp
);
723 cdk_stream_close (out
);
730 _cdk_filter_armor (void *data
, int ctl
, FILE * in
, FILE * out
)
732 if (ctl
== STREAMCTL_READ
)
733 return armor_decode (data
, in
, out
);
734 else if (ctl
== STREAMCTL_WRITE
)
735 return armor_encode (data
, in
, out
);
736 else if (ctl
== STREAMCTL_FREE
)
738 armor_filter_t
*afx
= data
;
741 _gnutls_buffers_log ("free armor filter\n");
742 afx
->idx
= afx
->idx2
= 0;
743 afx
->crc
= afx
->crc_okay
= 0;
754 * cdk_armor_encode_buffer:
755 * @inbuf: the raw input buffer
756 * @inlen: raw buffer len
757 * @outbuf: the destination buffer for the base64 output
758 * @outlen: destination buffer len
759 * @nwritten: actual length of the base64 data
760 * @type: the base64 file type.
762 * Encode the given buffer into base64 format. The base64
763 * string will be null terminated but the null will
764 * not be contained in the size.
767 cdk_armor_encode_buffer (const byte
* inbuf
, size_t inlen
,
768 char *outbuf
, size_t outlen
,
769 size_t * nwritten
, int type
)
771 const char *head
, *tail
, *le
;
774 size_t pos
, off
, len
, rest
;
776 if (!inbuf
|| !nwritten
)
779 return CDK_Inv_Value
;
781 if (type
> CDK_ARMOR_SIGNATURE
)
787 head
= armor_begin
[type
];
788 tail
= armor_end
[type
];
789 le
= _cdk_armor_get_lineend ();
790 pos
= strlen (head
) + 10 + 2 + 2 + strlen (tail
) + 10 + 2 + 5 + 2 + 1;
791 /* The output data is 4/3 times larger, plus a line end for each line. */
792 pos
+= (4 * inlen
/ 3) + 2 * (4 * inlen
/ 3 / 64) + 1;
794 if (outbuf
&& outlen
< pos
)
798 return CDK_Too_Short
;
801 /* Only return the size of the output. */
809 memset (outbuf
, 0, outlen
);
810 memcpy (outbuf
+ pos
, "-----", 5);
812 memcpy (outbuf
+ pos
, head
, strlen (head
));
813 pos
+= strlen (head
);
814 memcpy (outbuf
+ pos
, "-----", 5);
816 memcpy (outbuf
+ pos
, le
, strlen (le
));
818 memcpy (outbuf
+ pos
, le
, strlen (le
));
821 for (off
= 0; off
< inlen
;)
825 memcpy (tempbuf
, inbuf
+ off
, 48);
831 memcpy (tempbuf
, inbuf
+ off
, rest
);
836 base64_encode (tempout
, tempbuf
, len
, DIM (tempout
) - 1);
837 memcpy (outbuf
+ pos
, tempout
, strlen (tempout
));
838 pos
+= strlen (tempout
);
839 memcpy (outbuf
+ pos
, le
, strlen (le
));
843 memcpy (outbuf
+ pos
, "-----", 5);
845 memcpy (outbuf
+ pos
, tail
, strlen (tail
));
846 pos
+= strlen (tail
);
847 memcpy (outbuf
+ pos
, "-----", 5);
849 memcpy (outbuf
+ pos
, le
, strlen (le
));