Index: creddy/creddy.c
===================================================================
--- creddy/creddy.c	(revision abd7c251aeeda06b2bd96c350cfd6ed57fd6b477)
+++ creddy/creddy.c	(revision abd7c251aeeda06b2bd96c350cfd6ed57fd6b477)
@@ -0,0 +1,169 @@
+#include <assert.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <library.h>
+
+#include "creddy.h"
+
+#define OPT_CN              1
+#define OPT_VALIDITY        2
+
+int main(int argc, char **argv) {
+    int ret;
+    options_t options = { 0, };
+
+    options.validity = 1080;
+
+    struct option getopts[] = {
+        { "help",       0, &options.help, 1 },
+        { "generate",   0, &options.mode, MODE_GENERATE },
+        { "verify",     0, &options.mode, MODE_VERIFY },
+        { "keyid",      0, &options.mode, MODE_KEYID },
+        { "attribute",  0, &options.mode, MODE_ATTRIBUTE },
+        { "roles",      0, &options.mode, MODE_ROLES },
+
+        // generate options
+        { "cn",         1, 0, OPT_CN },
+        { "validity",   1, 0, OPT_VALIDITY },
+
+        { NULL },
+    };
+
+    for ( ; ; ) {
+        int c = getopt_long(argc, argv, "", getopts, NULL);
+        if (c < 0)
+            break;
+
+        switch (c) {
+            // set the option from the value in the getopts struct
+            case 0:
+                continue;
+
+            // generate options
+            case OPT_CN:
+                options.cn = xstrdup(optarg);
+                break;
+            case OPT_VALIDITY:
+                options.validity = atoi(optarg);
+                break;
+
+            case '?':
+                break;
+
+            default:
+                printf("wat\n");
+                return 45;
+        }
+    }
+
+    if (options.help || optind < argc) {
+        if (optind > 0 && optind < argc)
+            printf("I don't understand %s\n", argv[optind]);
+        usage(&options);
+    }
+
+    // init libstrongswan
+    atexit(library_deinit);
+    ret = library_init(NULL);
+    if (!ret)
+        exit(SS_RC_LIBSTRONGSWAN_INTEGRITY);
+    ret = lib->plugins->load(
+        lib->plugins,
+        NULL,
+        lib->settings->get_str(lib->settings, "", PLUGINS)
+    );
+    if (!ret)
+        exit(SS_RC_LIBSTRONGSWAN_INTEGRITY);
+
+    switch (options.mode) {
+        case MODE_GENERATE:
+            generate_main(&options);
+            break;
+
+        default:
+            usage(&options);
+    }
+
+    return 0;
+}
+
+void usage(options_t *opts) {
+    if (opts->mode == MODE_GENERATE)
+        printf(
+            "Usage: creddy --generate --cn <name> [ --validity <day> ]\n"
+            "    cert will be in ${name}_ID.der, private key in ${name}_private.der\n"
+            "    default validity: 1080 days\n"
+        );
+
+    else if (opts->mode == MODE_VERIFY)
+        printf(
+            "Usage: identity --verify --cert <cert> [ --attrcert <cert> ]\n"
+            "    if attrcert is provided, verify that it was issued by cert\n"
+        );
+
+    else if (opts->mode == MODE_KEYID)
+        printf(
+            "Usage: identity --keyid --cert <cert>\n"
+        );
+
+    else if (opts->mode == MODE_ATTRIBUTE)
+        printf(
+            "Usage: identity --attribute \\\n"
+            "                --issuer <cert> --key <key> --role <role> \\\n"
+            "                --subject <cert> [ --subject-role <role> ] \\\n"
+            "                [ --validity <days> ] --out <file>\n"
+            "    default validity: 365 days\n"
+        );
+
+    else if (opts->mode == MODE_ROLES)
+        printf(
+            "Usage: identity --roles --cert <cert>\n"
+        );
+
+    else
+        printf(
+            "Usage: creddy [--<mode>] [--help]\n"
+            "    --generate:  generate X.509 identity cert and private key\n"
+            "    --verify:    check validity of X.509 ID or attribute cert\n"
+            "    --keyid:     get fingerprint from X.509 ID cert\n"
+            "    --attribute: generate an X.509 attribute cert\n"
+            "    --roles:     list roles from an X.509 attribute cert\n"
+        );
+
+
+    exit(1);
+}
+
+void *xmalloc(size_t len) {
+    void *ret = malloc(len);
+    if (ret == NULL)
+        err(1, "couldn't malloc %d bytes\n", len);
+    return ret;
+}
+
+char *xstrdup(char *string) {
+    char *dup = strdup(string);
+    if (dup == NULL)
+        err(1, "Can't dup %s", string);
+    return dup;
+}
+
+int clean_name(char *string) {
+    int i;
+
+    assert(string != NULL);
+
+    // must start with a letter
+    if (!isalpha(string[0]))
+        return 0;
+
+    // rest must be alphanumeric
+    for (i = 1; string[i] != '\0'; ++i)
+        if (!isalnum(string[i]))
+            return 0;
+
+    return 1;
+}
Index: creddy/creddy.h
===================================================================
--- creddy/creddy.h	(revision abd7c251aeeda06b2bd96c350cfd6ed57fd6b477)
+++ creddy/creddy.h	(revision abd7c251aeeda06b2bd96c350cfd6ed57fd6b477)
@@ -0,0 +1,29 @@
+#ifndef __CREDDY_H__
+#define __CREDDY_H__
+
+typedef struct _options_t {
+    int help;
+    int mode;
+
+    // generate options
+    char *cn;
+    int validity;
+} options_t;
+
+#define MODE_GENERATE   1
+#define MODE_VERIFY     2
+#define MODE_KEYID      3
+#define MODE_ATTRIBUTE  4
+#define MODE_ROLES      5
+
+// returns true if a name starts with a letter and is otherwise alphanumeric
+int clean_name(char *string);
+
+void usage(options_t *opts);
+void *xmalloc(size_t len);
+char *xstrdup(char *string);
+
+// sub programs
+void generate_main(options_t *opts);
+
+#endif /* __CREDDY_H__ */
Index: creddy/generate.c
===================================================================
--- creddy/generate.c	(revision abd7c251aeeda06b2bd96c350cfd6ed57fd6b477)
+++ creddy/generate.c	(revision abd7c251aeeda06b2bd96c350cfd6ed57fd6b477)
@@ -0,0 +1,186 @@
+#include <fcntl.h>
+
+#include <library.h>
+#include <credentials/keys/private_key.h>
+#include <credentials/certificates/certificate.h>
+#include <credentials/certificates/x509.h>
+
+
+#include "creddy.h"
+
+#define KEY_SUFFIX  "_private.dem"
+#define CERT_SUFFIX "_ID.pem"
+
+static private_key_t *_generate_key(char *filename);
+static void _generate_cert(char *filename, private_key_t *private, char *cn, int validity);
+
+void generate_main(options_t *opts) {
+    int ret;
+    chunk_t encoding;
+    char *cn = opts->cn, *filename;
+    int cn_len;
+    private_key_t *key;
+
+    if (cn == NULL)
+        usage(opts);
+
+    if (!clean_name(cn)) {
+        printf("Invalid CN: must start with a letter and be alphanumeric\n");
+        usage(opts);
+    }
+    if (opts->validity <= 0) {
+        printf("Validity must be >= 1 day\n");
+        usage(opts);
+    }
+
+    cn_len = strlen(cn);
+
+    // key output filename: "${cn}_private.der"
+    filename = xmalloc(cn_len + sizeof(KEY_SUFFIX));
+    memcpy(filename, cn, cn_len);
+
+    memcpy(filename + cn_len, KEY_SUFFIX, sizeof(KEY_SUFFIX));
+    key = _generate_key(filename);
+
+    // cert filename: ${cn}_ID.pem
+    memcpy(filename +cn_len, CERT_SUFFIX, sizeof(CERT_SUFFIX));
+    _generate_cert(filename, key, cn, opts->validity);
+
+    free(filename);
+    DESTROY_IF(key);
+}
+
+static private_key_t *_generate_key(char *filename) {
+    private_key_t *key;
+    int ret;
+    chunk_t encoding;
+
+    printf("Generating key, this will take a while. Create entropy!\n");
+    printf("    - move the mouse\n");
+    printf("    - generate disk system activity (run find)\n");
+
+    // generate the key
+    key = lib->creds->create(
+        lib->creds,
+        CRED_PRIVATE_KEY, KEY_RSA,
+        BUILD_KEY_SIZE, 2048,
+        BUILD_END
+    );
+    if (key == NULL)
+        errx(1, "Key generation failed");
+
+    // get the key encoding
+    ret = key->get_encoding(key, KEY_PRIV_ASN1_DER, &encoding);
+    if (!ret)
+        errx(1, "Couldn't encode private key");
+
+    // write it to a file (with mode 600)
+    int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+    if (fd < 0)
+        err(1, "Couldn't open %s for writing private key", filename);
+
+    ret = write(fd, encoding.ptr, encoding.len);
+    if (ret < encoding.len)
+        errx(1, "Couldn't write all the bytes of the private key");
+
+    close(fd);
+
+    free(encoding.ptr);
+
+    return key;
+}
+
+static char *_create_dn(char *cn) {
+
+#define DN "cn="
+
+    char *dn = xmalloc(sizeof(DN) + strlen(cn));
+    memcpy(dn, DN, sizeof(DN));
+    strcat(dn, cn);
+
+    return dn;
+}
+
+#define BYTES_PER_LINE 64
+
+// thx libstrongswan
+static void _encode_base64(char *filename, chunk_t encoding) {
+    int start;
+
+    chunk_t b64 = chunk_to_base64(encoding, NULL);
+
+    FILE *out = fopen(filename, "w");
+    if (out == NULL)
+        err(1, "couldn't open %s for writing", filename);
+
+    fprintf(out, "-----BEGIN CERTIFICATE-----\n");
+
+    for (start = 0; start < b64.len; start += BYTES_PER_LINE) {
+        int left = b64.len - start;
+        int len = left < BYTES_PER_LINE ? left : BYTES_PER_LINE;
+        fwrite(b64.ptr + start, len, 1, out);
+        fprintf(out, "\n");
+    }
+
+    fprintf(out, "-----END CERTIFICATE-----\n");
+    fclose(out);
+
+    free(b64.ptr);
+}
+
+static void _generate_cert(char *filename, private_key_t *private, char *cn, int validity) {
+    // build the DN
+    char *dn_string = _create_dn(cn);
+    identification_t *id = identification_create_from_string(dn_string);
+    if (id == NULL)
+        errx(1, "couldn't create ID from DN %s", dn_string);
+    free(dn_string);
+
+    // get the public key
+    public_key_t *public = private->get_public_key(private);
+    if (public == NULL)
+        errx(1, "couldn't get public key from private key");
+
+    // create a serial (stolen from strongswan pki)
+    rng_t *rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
+    if (!rng)
+        errx(1, "no random number generator");
+
+    chunk_t serial = chunk_empty;
+    rng->allocate_bytes(rng, 8, &serial);
+    while (serial.ptr[0] == 0)
+        // don't get leading 0's
+        rng->get_bytes(rng, 1, serial.ptr);
+    rng->destroy(rng);
+
+    // validity period
+    time_t not_before = time(NULL);
+    time_t not_after = not_before + validity * 24 * 60 * 60;
+
+    // create!
+    certificate_t *cert = lib->creds->create(lib->creds,
+        CRED_CERTIFICATE, CERT_X509,
+        BUILD_SIGNING_KEY, private,
+        BUILD_PUBLIC_KEY, public,
+        BUILD_SUBJECT, id,
+        BUILD_NOT_BEFORE_TIME, not_before,
+        BUILD_NOT_AFTER_TIME, not_after,
+        BUILD_SERIAL, serial,
+        BUILD_DIGEST_ALG, HASH_SHA1,
+        BUILD_X509_FLAG, X509_CA,
+        BUILD_PATHLEN, X509_NO_PATH_LEN_CONSTRAINT,
+        BUILD_END
+    );
+    if (cert == NULL)
+        errx(1, "couldn't build cert :(");
+
+    chunk_t encoding = cert->get_encoding(cert);
+
+    // sigh
+    _encode_base64(filename, encoding);
+
+    DESTROY_IF(id);
+    DESTROY_IF(cert);
+    DESTROY_IF(public);
+    free(encoding.ptr);
+}
