Browse Source

add `base64_encode_pem()`

Signed-off-by: Steffen Jaeckel <[email protected]>
Steffen Jaeckel 3 years ago
parent
commit
0bfa7d0e1e
3 changed files with 82 additions and 11 deletions
  1. 11 0
      src/headers/tomcrypt_private.h
  2. 47 8
      src/misc/base64/base64_encode.c
  3. 24 3
      tests/base64_test.c

+ 11 - 0
src/headers/tomcrypt_private.h

@@ -202,6 +202,17 @@ void ocb3_int_xor_blocks(unsigned char *out, const unsigned char *block_a, const
 
 /* tomcrypt_misc.h */
 
+typedef enum {
+   /** Use `\r\n` as line separator */
+   BASE64_PEM_CRLF = 1,
+   /** Create output with 72 chars line length */
+   BASE64_PEM_SSH = 2,
+} base64_pem_flags;
+
+int base64_encode_pem(const unsigned char *in,  unsigned long inlen,
+                                     char *out, unsigned long *outlen,
+                            unsigned int  flags);
+
 void copy_or_zeromem(const unsigned char* src, unsigned char* dest, unsigned long len, int coz);
 
 int pbes_decrypt(const pbes_arg  *arg, unsigned char *dec_data, unsigned long *dec_size);

+ 47 - 8
src/misc/base64/base64_encode.c

@@ -21,19 +21,35 @@ static const char * const codes_base64url =
 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
 #endif /* LTC_BASE64_URL */
 
-static int s_base64_encode_internal(const unsigned char *in,  unsigned long inlen,
-                                 char *out, unsigned long *outlen,
-                                 const char *codes, int pad)
+enum mode {
+   nopad = 0,
+   pad = 1,
+   lf = 2,
+   cr = 4,
+   ssh = 8,
+   crlf = lf | cr,
+};
+
+static int s_base64_encode_internal(const unsigned char *in,    unsigned long inlen,
+                                                   char *out,   unsigned long *outlen,
+                                    const          char *codes, unsigned int  mode)
 {
-   unsigned long i, len2, leven;
+   unsigned long i, len2, leven, linelen;
    char *p;
 
    LTC_ARGCHK(in     != NULL);
    LTC_ARGCHK(out    != NULL);
    LTC_ARGCHK(outlen != NULL);
 
+   linelen = (mode & ssh) ? 72 : 64;
+
    /* valid output size ? */
    len2 = 4 * ((inlen + 2) / 3);
+   if ((mode & crlf) == lf) {
+      len2 += len2 / linelen;
+   } else if ((mode & crlf) == crlf) {
+      len2 += (len2 / linelen) * 2;
+   }
    if (*outlen < len2 + 1) {
       *outlen = len2 + 1;
       return CRYPT_BUFFER_OVERFLOW;
@@ -46,6 +62,10 @@ static int s_base64_encode_internal(const unsigned char *in,  unsigned long inle
        *p++ = codes[(((in[1] & 0xf) << 2) + (in[2] >> 6)) & 0x3F];
        *p++ = codes[in[2] & 0x3F];
        in += 3;
+       if ((p - out) % linelen == 0) {
+          if (mode & cr) *p++ = '\r';
+          if (mode & lf) *p++ = '\n';
+       }
    }
    /* Pad it if necessary...  */
    if (i < inlen) {
@@ -54,7 +74,7 @@ static int s_base64_encode_internal(const unsigned char *in,  unsigned long inle
 
        *p++ = codes[(a >> 2) & 0x3F];
        *p++ = codes[(((a & 3) << 4) + (b >> 4)) & 0x3F];
-       if (pad) {
+       if (mode & pad) {
          *p++ = (i+1 < inlen) ? codes[(((b & 0xf) << 2)) & 0x3F] : '=';
          *p++ = '=';
        }
@@ -83,7 +103,26 @@ static int s_base64_encode_internal(const unsigned char *in,  unsigned long inle
 int base64_encode(const unsigned char *in,  unsigned long inlen,
                                  char *out, unsigned long *outlen)
 {
-    return s_base64_encode_internal(in, inlen, out, outlen, codes_base64, 1);
+    return s_base64_encode_internal(in, inlen, out, outlen, codes_base64, pad);
+}
+
+/**
+   base64 Encode a buffer for PEM output
+     (NUL terminated with line-break at 64 chars)
+   @param in       The input buffer to encode
+   @param inlen    The length of the input buffer
+   @param out      [out] The destination of the base64 encoded data
+   @param outlen   [in/out] The max size and resulting size
+   @param flags    \ref base64_pem_flags
+   @return CRYPT_OK if successful
+*/
+int base64_encode_pem(const unsigned char *in,  unsigned long inlen,
+                                     char *out, unsigned long *outlen,
+                            unsigned int  flags)
+{
+    int use_crlf = flags & BASE64_PEM_CRLF ? pad | crlf : pad | lf;
+    int ssh_style = flags & BASE64_PEM_SSH ? ssh : 0;
+    return s_base64_encode_internal(in, inlen, out, outlen, codes_base64, ssh_style | use_crlf);
 }
 #endif /* LTC_BASE64 */
 
@@ -100,13 +139,13 @@ int base64_encode(const unsigned char *in,  unsigned long inlen,
 int base64url_encode(const unsigned char *in,  unsigned long inlen,
                                     char *out, unsigned long *outlen)
 {
-    return s_base64_encode_internal(in, inlen, out, outlen, codes_base64url, 0);
+    return s_base64_encode_internal(in, inlen, out, outlen, codes_base64url, nopad);
 }
 
 int base64url_strict_encode(const unsigned char *in,  unsigned long inlen,
                                            char *out, unsigned long *outlen)
 {
-    return s_base64_encode_internal(in, inlen, out, outlen, codes_base64url, 1);
+    return s_base64_encode_internal(in, inlen, out, outlen, codes_base64url, pad);
 }
 #endif /* LTC_BASE64_URL */
 

+ 24 - 3
tests/base64_test.c

@@ -15,6 +15,11 @@ int base64_test(void)
          0xbe, 0xe8, 0x92, 0x3c, 0xa2, 0x25, 0xf0, 0xf8,
          0x91, 0xe4, 0xef, 0xab, 0x0b, 0x8c, 0xfd, 0xff,
          0x14, 0xd0, 0x29, 0x9d, 0x00 };
+   /* 3 A's are encoded as QUFB */
+   const char *As_lf = "QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFB\nQUFB";
+   const char *As_crlf = "QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFB\r\nQUFB";
+   const char *As_ssh_lf = "QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFB\nQUFB";
+   const char *As_ssh_crlf = "QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFB\r\nQUFB";
 
 #if defined(LTC_BASE64)
    /*
@@ -140,16 +145,32 @@ int base64_test(void)
 
    out[10] = 0;
    DO(base64_decode(out, l1, tmp, &l2));
-   DO(compare_testvector(tmp, l2, in, l2, "insane base64 decoding (NUL)", -1));
+   DO(compare_testvector(tmp, l2, in, l2, "insane base64 decoding (NUL)", 0));
    DO(base64_sane_decode(out, l1, tmp, &l2) == CRYPT_INVALID_PACKET ? CRYPT_OK : CRYPT_INVALID_PACKET);
    DO(base64_strict_decode(out, l1, tmp, &l2) == CRYPT_INVALID_PACKET ? CRYPT_OK : CRYPT_INVALID_PACKET);
 
    out[10] = 9; /* tab */
    DO(base64_decode(out, l1, tmp, &l2));
-   DO(compare_testvector(tmp, l2, in, l2, "insane base64 decoding (TAB)", -1));
+   DO(compare_testvector(tmp, l2, in, l2, "insane base64 decoding (TAB)", 0));
    DO(base64_sane_decode(out, l1, tmp, &l2));
-   DO(compare_testvector(tmp, l2, in, l2, "relaxed base64 decoding (TAB)", -1));
+   DO(compare_testvector(tmp, l2, in, l2, "relaxed base64 decoding (TAB)", 0));
    DO(base64_strict_decode(out, l1, tmp, &l2) == CRYPT_INVALID_PACKET ? CRYPT_OK : CRYPT_INVALID_PACKET);
+
+   memset(in, 'A', sizeof(in));
+   l1 = strlen(As_lf);
+   SHOULD_FAIL(base64_encode_pem(in, 51, out, &l1, 0));
+   l1++;
+   DO(base64_encode_pem(in, 51, out, &l1, 0));
+   DO(compare_testvector(out, l1, As_lf, strlen(As_lf), "PEM output with \\n", 0));
+   l1 = strlen(As_crlf) + 1;
+   DO(base64_encode_pem(in, 51, out, &l1, BASE64_PEM_CRLF));
+   DO(compare_testvector(out, l1, As_crlf, strlen(As_crlf), "PEM output with \\r\\n", 0));
+   l1 = strlen(As_ssh_lf) + 1;
+   DO(base64_encode_pem(in, 57, out, &l1, BASE64_PEM_SSH));
+   DO(compare_testvector(out, l1, As_ssh_lf, strlen(As_ssh_lf), "PEM SSH-style output with \\n", 0));
+   l1 = strlen(As_ssh_crlf) + 1;
+   DO(base64_encode_pem(in, 57, out, &l1, BASE64_PEM_SSH | BASE64_PEM_CRLF));
+   DO(compare_testvector(out, l1, As_ssh_crlf, strlen(As_ssh_crlf), "PEM SSH-style output with \\r\\n", 0));
 #endif
 
    return 0;