Index: creddy/attribute.c
===================================================================
--- creddy/attribute.c	(revision 085f15950e1245032702e366285b3848d7715cae)
+++ creddy/attribute.c	(revision 085f15950e1245032702e366285b3848d7715cae)
@@ -0,0 +1,127 @@
+#include <credentials/keys/private_key.h>
+
+#include "creddy.h"
+
+#define ROLE_SEPARATOR " <- "
+
+void attribute_main(options_t *opts) {
+    if (
+        opts->issuer == NULL ||
+        opts->key == NULL ||
+        opts->role == NULL ||
+        opts->subject == NULL ||
+        opts->out == NULL
+    )
+        usage(opts);
+
+    if (!clean_name(opts->role)) {
+        printf("bad role name\n");
+        usage(opts);
+    }
+
+    // verify the subject role name if present
+    if (opts->subject_role) {
+        char *role = opts->subject_role;
+        char *start[3];
+        int name_parts = 0, i;
+
+        start[name_parts++] = role;
+
+        for (i = 0; role[i] != '\0'; ++i)
+            if (role[i] == '.') {
+                if (name_parts == 3) {
+                    printf("bad subject role name (too many dots)\n");
+                    usage(opts);
+                }
+                start[name_parts++] = &role[i+1];
+                role[i] = 0;
+            }
+
+        for (i = 0; i < name_parts; ++i)
+            if (!clean_name(start[i])) {
+                printf("bad subject role name\n");
+                usage(opts);
+            }
+
+        for (i = 1; i < name_parts; ++i)
+            *(start[i]-1) = '.'; // replace the dot
+
+        printf("good: %s\n", opts->subject_role);
+    }
+
+    if (opts->validity < 0) {
+        printf("Validity must be >= 1 day\n");
+        usage(opts);
+    }
+
+    // load signer key
+    private_key_t *key = lib->creds->create(lib->creds,
+        CRED_PRIVATE_KEY, KEY_RSA,
+        BUILD_FROM_FILE, opts->key,
+        BUILD_END
+    );
+    if (key == NULL)
+        errx(1, "can't open private key file %s", opts->key);
+
+    // get the keyids
+    certificate_t *issuer = cert_from_file(opts->issuer);
+    char *issuer_id = cert_keyid(issuer);
+
+    certificate_t *subject = cert_from_file(opts->subject);
+    char *subject_id = cert_keyid(subject);
+
+    // build the role encoding
+    int role_encoding_len = 1; // for nul terminator
+    role_encoding_len += strlen(issuer_id) + 1 + strlen(opts->role);
+    role_encoding_len += sizeof(ROLE_SEPARATOR) - 1;
+    role_encoding_len += strlen(subject_id);
+    if (opts->subject_role)
+        role_encoding_len += 1 + strlen(opts->subject_role);
+
+    char *role_encoding = xmalloc(role_encoding_len);
+    role_encoding[0] = '\0';
+
+    sprintf(role_encoding, "%s.%s" ROLE_SEPARATOR "%s", issuer_id, opts->role, subject_id);
+    if (opts->subject_role) {
+        strcat(role_encoding, ".");
+        strcat(role_encoding, opts->subject_role);
+    }
+
+    free(issuer_id);
+    free(subject_id);
+
+    // create attribute cert
+    time_t not_before = time(NULL);
+    time_t not_after = not_before + opts->validity * 3600 * 60 * 60;
+    chunk_t serial = generate_serial();
+
+    certificate_t *attr_cert = lib->creds->create(lib->creds,
+        CRED_CERTIFICATE, CERT_X509_AC,
+        BUILD_CERT, issuer,
+        BUILD_NOT_BEFORE_TIME, not_before,
+        BUILD_NOT_AFTER_TIME, not_after,
+        BUILD_SERIAL, serial,
+        BUILD_IETF_GROUP_ATTR, role_encoding,
+        BUILD_SIGNING_CERT, issuer,
+        BUILD_SIGNING_KEY, key,
+        BUILD_END
+    );
+    if (attr_cert == NULL)
+        errx(1, "Couldn't build attribute cert");
+
+    // write to file
+    chunk_t encoding = attr_cert->get_encoding(attr_cert);
+
+    FILE *out = fopen(opts->out, "w");
+    if (out == NULL)
+        err(1, "Can't open attribute cert output file %s", opts->out);
+    fwrite(encoding.ptr, encoding.len, 1, out);
+    fclose(out);
+
+    free(role_encoding);
+    free(serial.ptr);
+    DESTROY_IF(attr_cert);
+    DESTROY_IF(issuer);
+    DESTROY_IF(subject);
+    DESTROY_IF(key);
+}
