Skip to content

Commit 0bfa7d0

Browse files
committed
add base64_encode_pem()
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
1 parent 1aac5b3 commit 0bfa7d0

3 files changed

Lines changed: 82 additions & 11 deletions

File tree

src/headers/tomcrypt_private.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,17 @@ void ocb3_int_xor_blocks(unsigned char *out, const unsigned char *block_a, const
202202

203203
/* tomcrypt_misc.h */
204204

205+
typedef enum {
206+
/** Use `\r\n` as line separator */
207+
BASE64_PEM_CRLF = 1,
208+
/** Create output with 72 chars line length */
209+
BASE64_PEM_SSH = 2,
210+
} base64_pem_flags;
211+
212+
int base64_encode_pem(const unsigned char *in, unsigned long inlen,
213+
char *out, unsigned long *outlen,
214+
unsigned int flags);
215+
205216
void copy_or_zeromem(const unsigned char* src, unsigned char* dest, unsigned long len, int coz);
206217

207218
int pbes_decrypt(const pbes_arg *arg, unsigned char *dec_data, unsigned long *dec_size);

src/misc/base64/base64_encode.c

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,35 @@ static const char * const codes_base64url =
2121
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
2222
#endif /* LTC_BASE64_URL */
2323

24-
static int s_base64_encode_internal(const unsigned char *in, unsigned long inlen,
25-
char *out, unsigned long *outlen,
26-
const char *codes, int pad)
24+
enum mode {
25+
nopad = 0,
26+
pad = 1,
27+
lf = 2,
28+
cr = 4,
29+
ssh = 8,
30+
crlf = lf | cr,
31+
};
32+
33+
static int s_base64_encode_internal(const unsigned char *in, unsigned long inlen,
34+
char *out, unsigned long *outlen,
35+
const char *codes, unsigned int mode)
2736
{
28-
unsigned long i, len2, leven;
37+
unsigned long i, len2, leven, linelen;
2938
char *p;
3039

3140
LTC_ARGCHK(in != NULL);
3241
LTC_ARGCHK(out != NULL);
3342
LTC_ARGCHK(outlen != NULL);
3443

44+
linelen = (mode & ssh) ? 72 : 64;
45+
3546
/* valid output size ? */
3647
len2 = 4 * ((inlen + 2) / 3);
48+
if ((mode & crlf) == lf) {
49+
len2 += len2 / linelen;
50+
} else if ((mode & crlf) == crlf) {
51+
len2 += (len2 / linelen) * 2;
52+
}
3753
if (*outlen < len2 + 1) {
3854
*outlen = len2 + 1;
3955
return CRYPT_BUFFER_OVERFLOW;
@@ -46,6 +62,10 @@ static int s_base64_encode_internal(const unsigned char *in, unsigned long inle
4662
*p++ = codes[(((in[1] & 0xf) << 2) + (in[2] >> 6)) & 0x3F];
4763
*p++ = codes[in[2] & 0x3F];
4864
in += 3;
65+
if ((p - out) % linelen == 0) {
66+
if (mode & cr) *p++ = '\r';
67+
if (mode & lf) *p++ = '\n';
68+
}
4969
}
5070
/* Pad it if necessary... */
5171
if (i < inlen) {
@@ -54,7 +74,7 @@ static int s_base64_encode_internal(const unsigned char *in, unsigned long inle
5474

5575
*p++ = codes[(a >> 2) & 0x3F];
5676
*p++ = codes[(((a & 3) << 4) + (b >> 4)) & 0x3F];
57-
if (pad) {
77+
if (mode & pad) {
5878
*p++ = (i+1 < inlen) ? codes[(((b & 0xf) << 2)) & 0x3F] : '=';
5979
*p++ = '=';
6080
}
@@ -83,7 +103,26 @@ static int s_base64_encode_internal(const unsigned char *in, unsigned long inle
83103
int base64_encode(const unsigned char *in, unsigned long inlen,
84104
char *out, unsigned long *outlen)
85105
{
86-
return s_base64_encode_internal(in, inlen, out, outlen, codes_base64, 1);
106+
return s_base64_encode_internal(in, inlen, out, outlen, codes_base64, pad);
107+
}
108+
109+
/**
110+
base64 Encode a buffer for PEM output
111+
(NUL terminated with line-break at 64 chars)
112+
@param in The input buffer to encode
113+
@param inlen The length of the input buffer
114+
@param out [out] The destination of the base64 encoded data
115+
@param outlen [in/out] The max size and resulting size
116+
@param flags \ref base64_pem_flags
117+
@return CRYPT_OK if successful
118+
*/
119+
int base64_encode_pem(const unsigned char *in, unsigned long inlen,
120+
char *out, unsigned long *outlen,
121+
unsigned int flags)
122+
{
123+
int use_crlf = flags & BASE64_PEM_CRLF ? pad | crlf : pad | lf;
124+
int ssh_style = flags & BASE64_PEM_SSH ? ssh : 0;
125+
return s_base64_encode_internal(in, inlen, out, outlen, codes_base64, ssh_style | use_crlf);
87126
}
88127
#endif /* LTC_BASE64 */
89128

@@ -100,13 +139,13 @@ int base64_encode(const unsigned char *in, unsigned long inlen,
100139
int base64url_encode(const unsigned char *in, unsigned long inlen,
101140
char *out, unsigned long *outlen)
102141
{
103-
return s_base64_encode_internal(in, inlen, out, outlen, codes_base64url, 0);
142+
return s_base64_encode_internal(in, inlen, out, outlen, codes_base64url, nopad);
104143
}
105144

106145
int base64url_strict_encode(const unsigned char *in, unsigned long inlen,
107146
char *out, unsigned long *outlen)
108147
{
109-
return s_base64_encode_internal(in, inlen, out, outlen, codes_base64url, 1);
148+
return s_base64_encode_internal(in, inlen, out, outlen, codes_base64url, pad);
110149
}
111150
#endif /* LTC_BASE64_URL */
112151

tests/base64_test.c

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ int base64_test(void)
1515
0xbe, 0xe8, 0x92, 0x3c, 0xa2, 0x25, 0xf0, 0xf8,
1616
0x91, 0xe4, 0xef, 0xab, 0x0b, 0x8c, 0xfd, 0xff,
1717
0x14, 0xd0, 0x29, 0x9d, 0x00 };
18+
/* 3 A's are encoded as QUFB */
19+
const char *As_lf = "QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFB\nQUFB";
20+
const char *As_crlf = "QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFB\r\nQUFB";
21+
const char *As_ssh_lf = "QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFB\nQUFB";
22+
const char *As_ssh_crlf = "QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFB\r\nQUFB";
1823

1924
#if defined(LTC_BASE64)
2025
/*
@@ -140,16 +145,32 @@ int base64_test(void)
140145

141146
out[10] = 0;
142147
DO(base64_decode(out, l1, tmp, &l2));
143-
DO(compare_testvector(tmp, l2, in, l2, "insane base64 decoding (NUL)", -1));
148+
DO(compare_testvector(tmp, l2, in, l2, "insane base64 decoding (NUL)", 0));
144149
DO(base64_sane_decode(out, l1, tmp, &l2) == CRYPT_INVALID_PACKET ? CRYPT_OK : CRYPT_INVALID_PACKET);
145150
DO(base64_strict_decode(out, l1, tmp, &l2) == CRYPT_INVALID_PACKET ? CRYPT_OK : CRYPT_INVALID_PACKET);
146151

147152
out[10] = 9; /* tab */
148153
DO(base64_decode(out, l1, tmp, &l2));
149-
DO(compare_testvector(tmp, l2, in, l2, "insane base64 decoding (TAB)", -1));
154+
DO(compare_testvector(tmp, l2, in, l2, "insane base64 decoding (TAB)", 0));
150155
DO(base64_sane_decode(out, l1, tmp, &l2));
151-
DO(compare_testvector(tmp, l2, in, l2, "relaxed base64 decoding (TAB)", -1));
156+
DO(compare_testvector(tmp, l2, in, l2, "relaxed base64 decoding (TAB)", 0));
152157
DO(base64_strict_decode(out, l1, tmp, &l2) == CRYPT_INVALID_PACKET ? CRYPT_OK : CRYPT_INVALID_PACKET);
158+
159+
memset(in, 'A', sizeof(in));
160+
l1 = strlen(As_lf);
161+
SHOULD_FAIL(base64_encode_pem(in, 51, out, &l1, 0));
162+
l1++;
163+
DO(base64_encode_pem(in, 51, out, &l1, 0));
164+
DO(compare_testvector(out, l1, As_lf, strlen(As_lf), "PEM output with \\n", 0));
165+
l1 = strlen(As_crlf) + 1;
166+
DO(base64_encode_pem(in, 51, out, &l1, BASE64_PEM_CRLF));
167+
DO(compare_testvector(out, l1, As_crlf, strlen(As_crlf), "PEM output with \\r\\n", 0));
168+
l1 = strlen(As_ssh_lf) + 1;
169+
DO(base64_encode_pem(in, 57, out, &l1, BASE64_PEM_SSH));
170+
DO(compare_testvector(out, l1, As_ssh_lf, strlen(As_ssh_lf), "PEM SSH-style output with \\n", 0));
171+
l1 = strlen(As_ssh_crlf) + 1;
172+
DO(base64_encode_pem(in, 57, out, &l1, BASE64_PEM_SSH | BASE64_PEM_CRLF));
173+
DO(compare_testvector(out, l1, As_ssh_crlf, strlen(As_ssh_crlf), "PEM SSH-style output with \\r\\n", 0));
153174
#endif
154175

155176
return 0;

0 commit comments

Comments
 (0)