diff options
author | Stef Walter <stefw@gnome.org> | 2013-01-23 12:15:27 +0100 |
---|---|---|
committer | Stef Walter <stefw@gnome.org> | 2013-02-05 15:00:25 +0100 |
commit | 5df24bf0fb8532e0ebdf5f2366834848fdf6097d (patch) | |
tree | b858952507533e8708650fd61a128eb41aa6819d | |
parent | 722efb88cf12261d705e2a6dfb4aceab9ff7b76f (diff) |
Implement code for writing PEM
* Based on the gcr code
* Bring in base64 output code from BSD
* Make sure to output base64 lines of 64 character length since
this is what OpenSSL expects
-rw-r--r-- | common/base64.c | 62 | ||||
-rw-r--r-- | common/base64.h | 6 | ||||
-rw-r--r-- | common/pem.c | 54 | ||||
-rw-r--r-- | common/pem.h | 5 | ||||
-rw-r--r-- | common/tests/test-pem.c | 114 |
5 files changed, 237 insertions, 4 deletions
diff --git a/common/base64.c b/common/base64.c index 7e66933..3f51c8d 100644 --- a/common/base64.c +++ b/common/base64.c @@ -190,3 +190,65 @@ p11_b64_pton (const char *src, return (tarindex); } + +int +p11_b64_ntop (const unsigned char *src, + size_t srclength, + char *target, + size_t targsize, + int breakl) +{ + size_t len = 0; + unsigned char input[3]; + unsigned char output[4]; + size_t i; + + while (srclength > 0) { + if (2 < srclength) { + input[0] = *src++; + input[1] = *src++; + input[2] = *src++; + srclength -= 3; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + output[3] = input[2] & 0x3f; + + } else if (0 != srclength) { + /* Get what's left. */ + input[0] = input[1] = input[2] = '\0'; + for (i = 0; i < srclength; i++) + input[i] = *src++; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + if (srclength == 1) + output[2] = 255; + else + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + output[3] = 255; + + srclength = 0; + } + + for (i = 0; i < 4; i++) { + if (breakl && len % (breakl + 1) == 0) { + assert (len + 1 < targsize); + target[len++] = '\n'; + } + + assert(output[i] == 255 || output[i] < 64); + assert (len + 1 < targsize); + + if (output[i] == 255) + target[len++] = Pad64; + else + target[len++] = Base64[output[i]]; + } + } + + assert (len < targsize); + target[len] = '\0'; /* Returned value doesn't count \0. */ + return len; +} diff --git a/common/base64.h b/common/base64.h index 4a2d1e7..cc27afd 100644 --- a/common/base64.h +++ b/common/base64.h @@ -50,4 +50,10 @@ int p11_b64_pton (const char *src, unsigned char *target, size_t targsize); +int p11_b64_ntop (const unsigned char *src, + size_t srclength, + char *target, + size_t targsize, + int breakl); + #endif /* P11_BASE64_H_ */ diff --git a/common/pem.c b/common/pem.c index 3d3d284..b3c6acd 100644 --- a/common/pem.c +++ b/common/pem.c @@ -36,6 +36,7 @@ #include "compat.h" #include "base64.h" +#include "buffer.h" #include "debug.h" #include "pem.h" @@ -119,7 +120,7 @@ pem_find_end (const char *data, data += n_type; /* Next comes the suffix */ - if (ARMOR_SUFF_L > n_data && strncmp ((char *)data, ARMOR_SUFF, ARMOR_SUFF_L) != 0) + if (ARMOR_SUFF_L > n_data || strncmp ((char *)data, ARMOR_SUFF, ARMOR_SUFF_L) != 0) return NULL; /* The end of the data */ @@ -239,3 +240,54 @@ p11_pem_parse (const char *data, return nfound; } + +char * +p11_pem_write (const unsigned char *contents, + size_t length, + const char *type, + size_t *pem_len) +{ + p11_buffer buffer; + size_t estimate; + size_t prefix; + char *target; + int len; + + return_val_if_fail (contents || !length, NULL); + return_val_if_fail (type, NULL); + return_val_if_fail (pem_len, NULL); + + /* Estimate from base64 data. Algorithm from Glib reference */ + estimate = length * 4 / 3 + 7; + estimate += estimate / 64 + 1; + + if (!p11_buffer_init_null (&buffer, estimate + 128)) + return_val_if_reached (NULL); + + p11_buffer_add (&buffer, ARMOR_PREF_BEGIN, ARMOR_PREF_BEGIN_L); + p11_buffer_add (&buffer, type, -1); + p11_buffer_add (&buffer, ARMOR_SUFF, ARMOR_SUFF_L); + + prefix = buffer.len; + target = p11_buffer_append (&buffer, estimate); + return_val_if_fail (target != NULL, NULL); + + /* + * OpenSSL is absolutely certain that it wants its PEM base64 + * lines to be 64 characters in len. + */ + + len = p11_b64_ntop (contents, length, target, estimate, 64); + + assert (len > 0); + assert (len <= estimate); + buffer.len = prefix + len; + + p11_buffer_add (&buffer, "\n", 1); + p11_buffer_add (&buffer, ARMOR_PREF_END, ARMOR_PREF_END_L); + p11_buffer_add (&buffer, type, -1); + p11_buffer_add (&buffer, ARMOR_SUFF, ARMOR_SUFF_L); + p11_buffer_add (&buffer, "\n", 1); + + return p11_buffer_steal (&buffer, pem_len); +} diff --git a/common/pem.h b/common/pem.h index 1e88f1f..d84f418 100644 --- a/common/pem.h +++ b/common/pem.h @@ -47,4 +47,9 @@ unsigned int p11_pem_parse (const char *input, p11_pem_sink sink, void *user_data); +char * p11_pem_write (const unsigned char *contents, + size_t length, + const char *type, + size_t *pem_len); + #endif /* P11_PEM_H_ */ diff --git a/common/tests/test-pem.c b/common/tests/test-pem.c index 65a78d8..54a59d6 100644 --- a/common/tests/test-pem.c +++ b/common/tests/test-pem.c @@ -129,7 +129,7 @@ typedef struct { int input_index; int output_index; int parsed; -} SuccessClosure; +} Closure; static void on_parse_pem_success (const char *type, @@ -137,7 +137,7 @@ on_parse_pem_success (const char *type, size_t length, void *user_data) { - SuccessClosure *cl = user_data; + Closure *cl = user_data; CuAssertIntEquals (cl->cu, success_fixtures[cl->input_index].output[cl->output_index].length, length); CuAssertTrue (cl->cu, memcmp (success_fixtures[cl->input_index].output[cl->output_index].data, contents, @@ -150,7 +150,7 @@ on_parse_pem_success (const char *type, static void test_pem_success (CuTest *cu) { - SuccessClosure cl; + Closure cl; int ret; int i; int j; @@ -232,6 +232,113 @@ test_pem_failure (CuTest *cu) } } +typedef struct { + const char *input; + size_t length; + const char *type; + const char *output; +} WriteFixture; + +typedef struct { + CuTest *cu; + WriteFixture *fixture; +} WriteClosure; + +static WriteFixture write_fixtures[] = { + { + "\x69\x83\x4d\x5e\xab\x21\x95\x5c\x42\x76\x8f\x10\x7c\xa7\x97\x87" + "\x71\x94\xcd\xdf\xf2\x9f\x82\xd8\x21\x58\x10\xaf\x1e\x1a", + 30, "BLOCK1", + "-----BEGIN BLOCK1-----\n" + "aYNNXqshlVxCdo8QfKeXh3GUzd/yn4LYIVgQrx4a\n" + "-----END BLOCK1-----\n", + }, + { + "\x50\x31\x31\x2d\x4b\x49\x54\x0a\x0a\x50\x72\x6f\x76\x69\x64\x65" + "\x73\x20\x61\x20\x77\x61\x79\x20\x74\x6f\x20\x6c\x6f\x61\x64\x20" + "\x61\x6e\x64\x20\x65\x6e\x75\x6d\x65\x72\x61\x74\x65\x20\x50\x4b" + "\x43\x53\x23\x31\x31\x20\x6d\x6f\x64\x75\x6c\x65\x73\x2e\x20\x50" + "\x72\x6f\x76\x69\x64\x65\x73\x20\x61\x20\x73\x74\x61\x6e\x64\x61" + "\x72\x64\x0a\x63\x6f\x6e\x66\x69\x67\x75\x72\x61\x74\x69\x6f\x6e" + "\x20\x73\x65\x74\x75\x70\x20\x66\x6f\x72\x20\x69\x6e\x73\x74\x61" + "\x6c\x6c\x69\x6e\x67\x20\x50\x4b\x43\x53\x23\x31\x31\x20\x6d\x6f" + "\x64\x75\x6c\x65\x73\x20\x69\x6e\x20\x73\x75\x63\x68\x20\x61\x20" + "\x77\x61\x79\x20\x74\x68\x61\x74\x20\x74\x68\x65\x79\x27\x72\x65" + "\x0a\x64\x69\x73\x63\x6f\x76\x65\x72\x61\x62\x6c\x65\x2e\x0a\x0a" + "\x41\x6c\x73\x6f\x20\x73\x6f\x6c\x76\x65\x73\x20\x70\x72\x6f\x62" + "\x6c\x65\x6d\x73\x20\x77\x69\x74\x68\x20\x63\x6f\x6f\x72\x64\x69" + "\x6e\x61\x74\x69\x6e\x67\x20\x74\x68\x65\x20\x75\x73\x65\x20\x6f" + "\x66\x20\x50\x4b\x43\x53\x23\x31\x31\x20\x62\x79\x20\x64\x69\x66" + "\x66\x65\x72\x65\x6e\x74\x0a\x63\x6f\x6d\x70\x6f\x6e\x65\x6e\x74" + "\x73\x20\x6f\x72\x20\x6c\x69\x62\x72\x61\x72\x69\x65\x73\x20\x6c" + "\x69\x76\x69\x6e\x67\x20\x69\x6e\x20\x74\x68\x65\x20\x73\x61\x6d" + "\x65\x20\x70\x72\x6f\x63\x65\x73\x73\x2e\x0a", + 299, "LONG TYPE WITH SPACES", + "-----BEGIN LONG TYPE WITH SPACES-----\n" + "UDExLUtJVAoKUHJvdmlkZXMgYSB3YXkgdG8gbG9hZCBhbmQgZW51bWVyYXRlIFBL\n" + "Q1MjMTEgbW9kdWxlcy4gUHJvdmlkZXMgYSBzdGFuZGFyZApjb25maWd1cmF0aW9u\n" + "IHNldHVwIGZvciBpbnN0YWxsaW5nIFBLQ1MjMTEgbW9kdWxlcyBpbiBzdWNoIGEg\n" + "d2F5IHRoYXQgdGhleSdyZQpkaXNjb3ZlcmFibGUuCgpBbHNvIHNvbHZlcyBwcm9i\n" + "bGVtcyB3aXRoIGNvb3JkaW5hdGluZyB0aGUgdXNlIG9mIFBLQ1MjMTEgYnkgZGlm\n" + "ZmVyZW50CmNvbXBvbmVudHMgb3IgbGlicmFyaWVzIGxpdmluZyBpbiB0aGUgc2Ft\n" + "ZSBwcm9jZXNzLgo=\n" + "-----END LONG TYPE WITH SPACES-----\n" + }, + { + "\x69\x83\x4d\x5e\xab\x21\x95\x5c\x42\x76\x8f\x10\x7c\xa7\x97\x87" + "\x71\x94\xcd\xdf\xf2\x9f\x82\xd8\x21\x58\x10\xaf", + 28, "BLOCK1", + "-----BEGIN BLOCK1-----\n" + "aYNNXqshlVxCdo8QfKeXh3GUzd/yn4LYIVgQrw==\n" + "-----END BLOCK1-----\n", + }, + { + NULL, + } +}; + +static void +on_parse_written (const char *type, + const unsigned char *contents, + size_t length, + void *user_data) +{ + WriteClosure *cl = user_data; + + CuAssertStrEquals (cl->cu, cl->fixture->type, type); + CuAssertIntEquals (cl->cu, cl->fixture->length, length); + CuAssertTrue (cl->cu, memcmp (contents, cl->fixture->input, length) == 0); +} + +static void +test_pem_write (CuTest *cu) +{ + WriteFixture *fixture; + WriteClosure cl; + size_t length; + char *output; + unsigned int count; + int i; + + for (i = 0; write_fixtures[i].input != NULL; i++) { + fixture = write_fixtures + i; + + output = p11_pem_write ((unsigned char *)fixture->input, + fixture->length, + fixture->type, &length); + CuAssertStrEquals (cu, fixture->output, output); + CuAssertIntEquals (cu, strlen (fixture->output), length); + + cl.fixture = fixture; + cl.cu = cu; + + count = p11_pem_parse (output, length, on_parse_written, &cl); + CuAssertIntEquals (cu, 1, count); + + free (output); + } +} + int main (void) { @@ -241,6 +348,7 @@ main (void) SUITE_ADD_TEST (suite, test_pem_success); SUITE_ADD_TEST (suite, test_pem_failure); + SUITE_ADD_TEST (suite, test_pem_write); CuSuiteRun (suite); CuSuiteSummary (suite, output); |