summaryrefslogtreecommitdiffstats
path: root/scan_certificate.c
diff options
context:
space:
mode:
authorSimon Rettberg2014-03-15 01:49:50 +0100
committerSimon Rettberg2014-03-15 01:49:50 +0100
commitbedd2e7ccb1595c23e159eaa952ae1b0b5a3d2ad (patch)
treec7d1995a09f6ed0c4e6873252e957d72f5d07d07 /scan_certificate.c
downloadldadp-bedd2e7ccb1595c23e159eaa952ae1b0b5a3d2ad.tar.gz
ldadp-bedd2e7ccb1595c23e159eaa952ae1b0b5a3d2ad.tar.xz
ldadp-bedd2e7ccb1595c23e159eaa952ae1b0b5a3d2ad.zip
Lean and mean initial commit
Not much functionality yet
Diffstat (limited to 'scan_certificate.c')
-rw-r--r--scan_certificate.c338
1 files changed, 338 insertions, 0 deletions
diff --git a/scan_certificate.c b/scan_certificate.c
new file mode 100644
index 0000000..4fb1b44
--- /dev/null
+++ b/scan_certificate.c
@@ -0,0 +1,338 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include "asn1.h"
+#include <str.h>
+#include "textcode.h"
+#include "fmt.h"
+#include "byte.h"
+
+struct x509signature {
+ struct string oid; /* you are not expected to actually decode this */
+ size_t oididx; /* if this is (size_t)-1, then the parser did not know the OID.
+ Otherwise it's the index into oid2string. oid2string[oididx].id
+ should be something like X509_ALG_SHA1RSA (see asn1.h) */
+ struct string bitstring; /* In this string, the length is in bits, not bytes! */
+ /* If the length is not a multiple of 8, then the unused bits are missing in the last byte.
+ * The parser already validated that the last byte is padded with 0 bits */
+};
+
+struct x509cert {
+ enum { v1=0, v1988=0, v2=1, v3=2, v1996=2 } version;
+ size_t serial;
+ struct x509signature algid;
+ struct string issuer; /* this is the raw asn.1 structure, a SET of "[{op}]" in scan_asn1generic terms */
+ time_t notbefore, notafter;
+ struct string subject; /* this is the raw asn.1 structure, a SET of "[{op}]" in scan_asn1generic terms */
+ struct x509signature sig;
+};
+
+struct rsaprivatekey {
+ size_t* modulus,* publicExponent,* privateExponent,* prime1,* prime2,* exponent1,* exponent2,* coefficient;
+ struct string otherPrimeInfos;
+ size_t* freewhendone;
+};
+
+struct dsaprivatekey {
+}
+
+
+void printasn1(const char* buf,const char* max);
+
+static int findindn(struct string* dn,enum x509_oid id,struct string* dest) {
+ size_t i;
+ const char* c=dn->s;
+ const char* max=dn->s+dn->l;
+ for (;;) {
+ struct string oid;
+ size_t l=scan_asn1generic(c,max,"[{op}]",&oid,dest);
+ if (l) {
+ i=lookupoid(oid.s,oid.l);
+ if (i!=(size_t)-1) { // recognized the oid!
+ if (oid2string[i].id==id)
+ return 1;
+ }
+ c+=l;
+ } else break;
+ }
+ return 0;
+}
+
+static size_t base64_decode(const char* cert, size_t l, const char* name, char** dest) {
+ /* cert should be something like "-----BEGIN CERTIFICATE-----\n[base64 gunk]\n-----END CERTIFICATE-----\n"
+ * l should be strlen(cert), but cert does not need to be 0-terminated
+ * name should be something like "CERTIFICATE" or "RSA PRIVATE KEY", what you are trying to decode
+ * dest will end up pointing to the decoded data.
+ * return value can be 0 if we can't decode the data and it does not
+ * look like it's a binary certificate. Or it can be some length
+ * value, in which case dest points to a malloced area of that length
+ * with the decoded data in it. */
+ size_t taglen=strlen(name)+sizeof("-----BEGIN -----")-1;
+ char tag[taglen+1];
+ char* c=0,* x;
+ tag[fmt_strm(tag,"-----BEGIN ",name,"-----")]=0;
+ if (l > 2*taglen && byte_equal(cert,taglen,tag))
+certfound:
+ {
+ size_t cur,used;
+ /* "-----BEGIN CERTIFICATE-----", "-----END CERTIFICATE-----" and newlines */
+ c=malloc((l-2*taglen)/4*3);
+ if (!c) return 0;
+ x=c;
+ for (cur=taglen; cur+taglen-1<l;) {
+ size_t next;
+ if (cert[cur]=='\r') ++cur; /* skip line ending */
+ if (cert[cur]=='\n') ++cur;
+ next=scan_base64(cert+cur,x,&used);
+ if (next==0) break;
+ cur+=next;
+ x+=used;
+ }
+ if (!str_start(cert+cur,"-----END ") ||
+ byte_diff(cert+cur+sizeof("-----END"),taglen-sizeof("----BEGIN")-1,tag+sizeof("-----BEGIN"))) {
+ free(c);
+ return 0;
+ }
+ cert=c;
+ l=x-c;
+ } else {
+ /* Maybe it has text in front of the BEGIN CERTIFICATE line */
+ size_t i,a=1;
+ for (i=0; i+27+25+2<l; ++i) {
+ if (cert[i]!='\n' && cert[i]!='\r' && (cert[i]<' ' || cert[i]>'~')) {
+ a=0;
+ break;
+ }
+ if (byte_equal(cert+i,taglen,tag)) {
+ cert+=i;
+ l-=i;
+ goto certfound;
+ }
+ }
+ if (a) /* if we end up here, it was ascii but did not contain a certificate. fail. */
+ return 0;
+ }
+ /* if we end up here, we decoded some base64 data or we found some
+ * binary data. See if it looks like x.509 at all. If it does, it
+ * starts with a SEQUENCE_OF, which encodes as '0'. */
+ if (*cert!='0') {
+parseerror:
+ free(c);
+ return 0;
+ }
+ *dest=c;
+ return l;
+}
+
+size_t scan_rsaprivatekey(const char* cert, size_t l, struct rsaprivatekey* C, char** freewhendone) {
+ char* c;
+ size_t maxdigits,ret;
+ unsigned long version;
+ *freewhendone=NULL;
+ l=base64_decode(cert,l,"RSA PRIVATE KEY",&c);
+ if (!l) return 0;
+ if (c!=cert) *freewhendone=c;
+ maxdigits=l/sizeof(size_t)+2;
+ C->freewhendone=malloc(maxdigits*sizeof(size_t)*8);
+ if (!C->freewhendone) {
+fail:
+ free(*freewhendone);
+ freewhendone=NULL;
+ return 0;
+ }
+ C->modulus=C->freewhendone;
+ C->publicExponent=C->modulus+maxdigits;
+ C->privateExponent=C->publicExponent+maxdigits;
+ C->prime1=C->privateExponent+maxdigits;
+ C->prime2=C->prime1+maxdigits;
+ C->exponent1=C->prime2+maxdigits;
+ C->exponent2=C->exponent1+maxdigits;
+ C->coefficient=C->exponent2+maxdigits;
+ C->otherPrimeInfos.l=0;
+ C->otherPrimeInfos.s=NULL;
+ if ((ret=scan_asn1generic(c,c+l,"{iIIIIIIII!}",&version,
+ C->modulus,C->publicExponent,C->privateExponent,
+ C->prime1,C->prime2,C->exponent1,C->exponent2,
+ C->coefficient,&C->otherPrimeInfos))) {
+ if (version!=0 && version!=1) goto fail;
+ if (version==0 && C->otherPrimeInfos.l) goto fail;
+ if (version==1 && !C->otherPrimeInfos.l) goto fail;
+ if (version==0) C->otherPrimeInfos.s=NULL;
+ return ret;
+ } else
+ goto fail;
+}
+
+size_t scan_certificate(const char* cert, size_t l, struct x509cert* C, char** freewhendone) {
+ char* c=0,* x;
+ *freewhendone=NULL;
+ l=base64_decode(cert,l,"CERTIFICATE",&c);
+ if (!l) return 0;
+ if (c!=cert) *freewhendone=c;
+ cert=c;
+
+ /* now for the heavy lifting */
+ {
+ unsigned long tagforversion; // must be 0
+ unsigned long version;
+ struct string oidalg,algparams,pubkeyalg,extensions,oidsig,sigrest,sigdata;
+ size_t i;
+ if (scan_asn1generic(cert,cert+l,"{{ci]i{o!}{!}{uu}{!}{!}!}{o!}b}",
+ &tagforversion,
+ &version,
+ &C->serial,
+ &oidalg, &algparams,
+ &C->issuer,
+ &C->notbefore, &C->notafter,
+ &C->subject,
+ &pubkeyalg,
+ &extensions,
+ &oidsig, &sigrest, &sigdata)) {
+
+ if (version==0)
+ printf("X.509 certificate\n");
+ else if (version==1)
+ printf("X.509v2 certificate\n");
+ else if (version==2)
+ printf("X.509v3 certificate\n");
+ else
+ printf("unsupported version %ld (must be 0, 1 or 2)\n",version);
+
+ printf("serial number %lu\n",C->serial);
+
+ printf("issuer: ");
+ {
+ struct string s;
+ if (findindn(&C->issuer,X509_ATTR_COUNTRY,&s))
+ printf("C=%.*s ",(int)s.l,s.s);
+ if (findindn(&C->issuer,X509_ATTR_ORG,&s))
+ printf("O=%.*s ",(int)s.l,s.s);
+ if (findindn(&C->issuer,X509_ATTR_COMMONNAME,&s))
+ printf("CN=%.*s ",(int)s.l,s.s);
+ }
+ printf("\n");
+
+ {
+ char a[100],b[100];
+ a[fmt_httpdate(a,C->notbefore)]=0;
+ b[fmt_httpdate(b,C->notafter)]=0;
+ printf("valid not before %s and not after %s\n",a,b);
+ }
+
+ printf("subject: ");
+ {
+ struct string s;
+ if (findindn(&C->issuer,X509_ATTR_COUNTRY,&s))
+ printf("C=%.*s ",(int)s.l,s.s);
+ if (findindn(&C->issuer,X509_ATTR_ORG,&s))
+ printf("O=%.*s ",(int)s.l,s.s);
+ if (findindn(&C->issuer,X509_ATTR_COMMONNAME,&s))
+ printf("CN=%.*s ",(int)s.l,s.s);
+ }
+ printf("\n");
+
+ i=lookupoid(oidalg.s,oidalg.l);
+ if (i!=(size_t)-1)
+ printf("signature algorithm %s\n",oid2string[i].name);
+ else {
+ unsigned long temp[100];
+ size_t len=100;
+ if (scan_asn1rawoid(oidalg.s,oidalg.s+oidalg.l,temp,&len)) {
+ printf("Unknown signature algorithm (oid ");
+ for (i=0; i<len; ++i)
+ printf("%lu%s",temp[i],i+1<len?".":")\n");
+ } else
+ printf("I don't know the algorithm and I can't parse/print the OID\n");
+ }
+
+ /* pubkeyalg is a SubjectPublicKeyInfo:
+ SubjectPublicKeyInfo ::= SEQUENCE{
+ algorithm AlgorithmIdentifier,
+ subjectPublicKey BIT STRING}
+
+ AlgorithmIdentifier ::= SEQUENCE{
+ algorithm OBJECT IDENTIFIER,
+ parameters ANY DEFINED BY algorithm OPTIONAL}
+ */
+
+ {
+ struct string pubkeyoid, pubkeyparams, bits;
+ if (scan_asn1generic(pubkeyalg.s,pubkeyalg.s+pubkeyalg.l,"{o!}b",&pubkeyoid,&pubkeyparams,&bits)) {
+
+ i=lookupoid(pubkeyoid.s,pubkeyoid.l);
+ if (i!=(size_t)-1) {
+ printf("public key algorithm %s\n",oid2string[i].name);
+ if (oid2string[i].id==X509_ALG_RSA) {
+ size_t* modulus,* publicExponent;
+ size_t allocsize=bits.l/8+2*sizeof(modulus[0]);
+ modulus=malloc(allocsize);
+ publicExponent=malloc(allocsize);
+ if (!modulus || !publicExponent)
+ printf("malloc for RSA bignums failed!\n");
+ else {
+ if (scan_asn1generic(bits.s,bits.s+bits.l/8,"{II}",modulus,publicExponent)) {
+ if (publicExponent[0]==1)
+ printf("public exponent %lu\n",publicExponent[1]);
+ else
+ printf("public exponent is larger than a word?!\n");
+ printf("modulus: ");
+ for (i=1; i<=modulus[0]; ++i) {
+ size_t j,k;
+ for (j=0, k=modulus[i]; j<sizeof(modulus[0]); ++j) {
+ printf("%02lx:",(k>>((sizeof(modulus[0])*8)-(j+1)*8))&0xff);
+ }
+ if ((i-1)%4==3 || i==modulus[0]) printf("\n");
+ }
+ } else
+ printf("bignum scanning failed!\n");
+ }
+ free(modulus); free(publicExponent);
+ /* for RSA, bits is actually another sequence with two integers, modulus and publicExponent */
+ printf("pubkeyparams len %lu, bits len %lu\n",pubkeyparams.l,bits.l);
+ }
+ } else {
+ unsigned long temp[100];
+ size_t len=100;
+ if (scan_asn1rawoid(pubkeyoid.s,pubkeyoid.s+pubkeyoid.l,temp,&len)) {
+ printf("Unknown public key algorithm (oid ");
+ for (i=0; i<len; ++i)
+ printf("%lu%s",temp[i],i+1<len?".":")\n");
+ } else
+ printf("I don't know the public key algorithm and I can't parse/print the OID\n");
+ }
+
+ } else
+ printf("could not parse public key part!\n");
+ }
+
+ }
+ }
+
+// printasn1(cert,cert+l);
+
+}
+
+#include "mmap.h"
+#include <stdio.h>
+
+#include "printasn1.c"
+
+int main(int argc,char* argv[]) {
+ char* freewhendone;
+ char* buf;
+ size_t l,n;
+ struct x509cert c;
+ struct rsaprivatekey k;
+
+ buf=mmap_read(argc>1?argv[1]:"test.pem",&l);
+ if (!buf) { puts("test.pem not found"); return 1; }
+
+ n=scan_certificate(buf,l,&c,&freewhendone);
+ free(freewhendone);
+
+ buf=mmap_read(argc>1?argv[1]:"privatekey.pem",&l);
+ if (!buf) { puts("privatekey.pem not found"); return 1; }
+
+ n=scan_rsaprivatekey(buf,l,&k,&freewhendone);
+ free(freewhendone);
+ free(k.freewhendone);
+}