Index: creddy/attribute.c
===================================================================
--- creddy/attribute.c	(revision 413e62850b7ae5c839f53309156d8de20230b672)
+++ creddy/attribute.c	(revision 980a7b6c556281c8ae33130f5b4b5c91fcadf24d)
@@ -7,4 +7,5 @@
 
 #define ROLE_SEPARATOR " <- "
+#define INTERSECTION_SEP " & "
 #define SHA1_LENGTH 40
 
@@ -86,8 +87,7 @@
 }
 
-
 void attribute_main(options_t *opts) {
-    char *subject_id = NULL;
     struct cb_opts c_opts = { 1, 0, 3, "Key password:" };
+    int i, role_len = 1;
 
     if (
@@ -99,64 +99,76 @@
         usage(opts);
 
-    // make sure we have exactly one of --subject-cert / --subject-id
-    if (opts->subject_cert == NULL && opts->subject_id == NULL)
+    if (!clean_name(opts->role)) {
+        printf("bad role name %s\n", opts->role);
         usage(opts);
-    if (opts->subject_cert && opts->subject_id) {
-        printf("Need exactly one of --subject-cert / --subject-id");
-        usage(opts);
-    }
-
-    // if we have an ID, make sure it's SHA1 and lowercase
-    if (opts->subject_id) {
-        int i;
-        subject_id = opts->subject_id;
-
-        // hex chars (also convert to lowercase)
-        for (i = 0; subject_id[i]; ++i) {
-            subject_id[i] = tolower(subject_id[i]);
-            if (!isxdigit(subject_id[i])) {
-                printf("Invalid subject ID: must be SHA1\n");
+    }
+
+    role_len += SHA1_LENGTH + 1 + strlen(opts->role);
+    role_len += strlen(ROLE_SEPARATOR);
+
+    for (i = 0; i < opts->num_subjects; ++i) {
+        subject_t *cur = &opts->subjects[i];
+
+        // if we have an ID, make sure it's SHA1 and lowercase
+        if (cur->id) {
+            int j;
+            char *subject_id = cur->id;
+
+            // hex chars (also convert to lowercase)
+            for (j = 0; subject_id[j]; ++j) {
+                subject_id[j] = tolower(subject_id[j]);
+                if (!isxdigit(subject_id[j])) {
+                    printf("Invalid subject ID %s: must be SHA1\n", subject_id);
+                    usage(opts);
+                }
+            }
+
+            // correct length
+            if (j != SHA1_LENGTH) {
+                printf("Invalid subject ID %s: must be SHA1 (wrong length)\n", subject_id);
                 usage(opts);
             }
         }
 
-        // correct length
-        if (i != SHA1_LENGTH) {
-            printf("Invalid subject ID: must be SHA1 (too long)\n");
-            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");
+        // otherwise we have a cert
+        else {
+            certificate_t *subject = cert_from_file(cur->cert);
+            cur->id = cert_keyid(subject);
+            DESTROY_IF(subject);
+        }
+
+        role_len += SHA1_LENGTH;
+
+        // verify the subject role name if present
+        if (cur->role) {
+            char *role = cur->role;
+            char *start[3];
+            int name_parts = 0, j;
+
+            start[name_parts++] = role;
+
+            for (j = 0; role[j] != '\0'; ++j)
+                if (role[j] == '.') {
+                    if (name_parts == 3) {
+                        printf("bad subject role name (too many dots)\n");
+                        usage(opts);
+                    }
+                    start[name_parts++] = &role[j+1];
+                    role[j] = 0;
+                }
+
+            for (j = 0; j < name_parts; ++j)
+                if (!clean_name(start[j])) {
+                    printf("bad subject role name\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
+
+            for (j = 1; j < name_parts; ++j)
+                *(start[j]-1) = '.'; // replace the dot
+
+            role_len += strlen(cur->role) + 1;
+        }
+
+        role_len += sizeof(INTERSECTION_SEP) - 1;
     }
 
@@ -182,33 +194,26 @@
     char *issuer_id = cert_keyid(issuer);
 
-    // subject keyid from cert if it wasn't passed in
-    if (opts->subject_cert) {
-        certificate_t *subject = cert_from_file(opts->subject_cert);
-        subject_id = cert_keyid(subject);
-        DESTROY_IF(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);
+    char *role_encoding = xmalloc(role_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);
+    // role
+    sprintf(role_encoding, "%s.%s" ROLE_SEPARATOR, issuer_id, opts->role);
+
+    // subject(s)
+    for (i = 0; i < opts->num_subjects; ++i) {
+        subject_t *cur = &opts->subjects[i];
+
+        strcat(role_encoding, cur->id);
+        if (cur->role) {
+            strcat(role_encoding, ".");
+            strcat(role_encoding, cur->role);
+        }
+
+        if (i < opts->num_subjects - 1)
+            strcat(role_encoding, INTERSECTION_SEP);
     }
 
     free(issuer_id);
-
-    // if opts->subject_id, the memory will be freed in main
-    if (opts->subject_cert)
-        free(subject_id);
 
     // create attribute cert
Index: creddy/creddy.c
===================================================================
--- creddy/creddy.c	(revision 413e62850b7ae5c839f53309156d8de20230b672)
+++ creddy/creddy.c	(revision 980a7b6c556281c8ae33130f5b4b5c91fcadf24d)
@@ -19,7 +19,48 @@
 #define OPT_SUBJECT_ID      11
 
+subject_t *subjects = NULL;
+int num_subjects = 0;
+int subjects_size = 0;
+
+char **roles = NULL;
+int num_roles = 0;
+int roles_size = 0;
+
+void subject(char *subject, int cert) {
+    if (num_subjects == subjects_size) {
+        subjects_size *= 2;
+        subjects = xrealloc(subjects, sizeof(subject_t) * subjects_size);
+    }
+
+    int i = num_subjects++;
+    subjects[i].id = NULL;
+    subjects[i].cert = NULL;
+    subjects[i].role = NULL;
+
+    if (cert)
+        subjects[i].cert = subject;
+    else
+        subjects[i].id = subject;
+}
+
+void role(char *role) {
+    if (num_roles == roles_size) {
+        roles_size *= 2;
+        roles = xrealloc(roles, sizeof(char *) * roles_size);
+    }
+
+    int i = num_roles++;
+    roles[i] = role;
+}
+
 int main(int argc, char **argv) {
     int ret;
     options_t options = { 0, };
+
+    subjects = xmalloc(sizeof(subject_t) * 2);
+    subjects_size = 2;
+
+    roles = xmalloc(sizeof(char *) * 2);
+    roles_size = 2;
 
     struct option getopts[] = {
@@ -86,11 +127,11 @@
                 break;
             case OPT_SUBJECT_CERT:
-                options.subject_cert = xstrdup(optarg);
+                subject(xstrdup(optarg), 1);
                 break;
             case OPT_SUBJECT_ID:
-                options.subject_id = xstrdup(optarg);
+                subject(xstrdup(optarg), 0);
                 break;
             case OPT_SUBJECT_ROLE:
-                options.subject_role = xstrdup(optarg);
+                role(xstrdup(optarg));
                 break;
             case OPT_OUT:
@@ -116,4 +157,28 @@
             printf("I don't understand %s\n", argv[optind]);
         usage(&options);
+    }
+
+    if (MODE_ATTRIBUTE) {
+        int i;
+
+        // have to do error checking on subjects here
+        if (
+                (num_subjects == 0) ||
+                (num_subjects != num_roles && num_subjects != 1 && num_roles != 0)
+           ) {
+            printf(
+                "You have %d subject%s and %d role%s, which is invalid\n",
+                num_subjects, num_subjects == 1 ? "" : "s",
+                num_roles, num_roles == 1 ? "" : "s"
+            );
+            usage(&options);
+        }
+
+        for (i = 0; i < num_roles; ++i)
+            subjects[i].role = roles[i];
+        free(roles);
+
+        options.subjects = subjects;
+        options.num_subjects = num_subjects;
     }
 
@@ -190,9 +255,10 @@
             "Usage: identity --attribute \\\n"
             "                --issuer <cert> --key <key> --role <role> \\\n"
-            "                --subject-cert <cert> | --subject-id <sha1> \\\n"
-            "                [ --subject-role <role> ] \\\n"
+            "                [ --subject-cert <cert> | --subject-id <sha1> \\\n"
+            "                    [ --subject-role <role> ]  ... ] \\\n"
             "                [ --validity <days> ] --out <file>\n"
             "    default validity: 365 days\n"
             "    provide exactly one of --subject-cert / --subject-id\n"
+            "    give multiple --subject-{cert,id} / --subject-role pairs for intersection\n"
         );
 
@@ -221,4 +287,11 @@
     if (ret == NULL)
         err(1, "couldn't malloc %d bytes\n", len);
+    return ret;
+}
+
+void *xrealloc(void *ptr, size_t size) {
+    void *ret = realloc(ptr, size);
+    if (ret == NULL)
+        err(1, "couldn't realloc %d bytes\n", size);
     return ret;
 }
Index: creddy/creddy.h
===================================================================
--- creddy/creddy.h	(revision 413e62850b7ae5c839f53309156d8de20230b672)
+++ creddy/creddy.h	(revision 980a7b6c556281c8ae33130f5b4b5c91fcadf24d)
@@ -5,4 +5,10 @@
 #include <credentials/certificates/certificate.h>
 #include <credentials/certificates/x509.h>
+
+typedef struct _subject_t {
+    char *cert;
+    char *id;
+    char *role;
+} subject_t;
 
 typedef struct _options_t {
@@ -20,7 +26,6 @@
     char *key;
     char *role;
-    char *subject_cert;
-    char *subject_id;
-    char *subject_role;
+    subject_t *subjects;
+    int num_subjects;
     char *out;
 
@@ -51,4 +56,5 @@
 void usage(options_t *opts);
 void *xmalloc(size_t len);
+void *xrealloc(void *ptr, size_t size);
 char *xstrdup(char *string);
 
