ref: c116550e6a41572796e4db65e4f6acbcb3d9d6f8
dir: /appl/lib/crypt/x509.b/
implement X509; include "sys.m"; sys : Sys; include "asn1.m"; asn1 : ASN1; Elem, Tag, Value, Oid, Universal, Context, BOOLEAN, INTEGER, BIT_STRING, OCTET_STRING, OBJECT_ID, SEQUENCE, UTCTime, IA5String, GeneralString, GeneralizedTime : import asn1; include "keyring.m"; keyring : Keyring; IPint, DESstate : import keyring; include "security.m"; random : Random; include "daytime.m"; daytime : Daytime; include "pkcs.m"; pkcs : PKCS; include "x509.m"; X509_DEBUG : con 0; logfd : ref Sys->FD; TAG_MASK : con 16r1F; CONSTR_MASK : con 16r20; CLASS_MASK : con 16rC0; # object identifiers objIdTab = array [] of { id_at => Oid(array [] of {2,5,4}), id_at_commonName => Oid(array [] of {2,5,4,3}), id_at_countryName => Oid(array [] of {2,5,4,6}), id_at_localityName => Oid(array [] of {2,5,4,7}), id_at_stateOrProvinceName => Oid(array [] of {2,5,4,8}), id_at_organizationName => Oid(array [] of {2,5,4,10}), id_at_organizationalUnitName => Oid(array [] of {2,5,4,11}), id_at_userPassword => Oid(array [] of {2,5,4,35}), id_at_userCertificate => Oid(array [] of {2,5,4,36}), id_at_cAcertificate => Oid(array [] of {2,5,4,37}), id_at_authorityRevocationList => Oid(array [] of {2,5,4,38}), id_at_certificateRevocationList => Oid(array [] of {2,5,4,39}), id_at_crossCertificatePair => Oid(array [] of {2,5,4,40}), # id_at_crossCertificatePair => Oid(array [] of {2,5,4,58}), id_at_supportedAlgorithms => Oid(array [] of {2,5,4,52}), id_at_deltaRevocationList => Oid(array [] of {2,5,4,53}), id_ce => Oid(array [] of {2,5,29}), id_ce_subjectDirectoryAttributes => Oid(array [] of {2,5,29,9}), id_ce_subjectKeyIdentifier => Oid(array [] of {2,5,29,14}), id_ce_keyUsage => Oid(array [] of {2,5,29,15}), id_ce_privateKeyUsage => Oid(array [] of {2,5,29,16}), id_ce_subjectAltName => Oid(array [] of {2,5,29,17}), id_ce_issuerAltName => Oid(array [] of {2,5,29,18}), id_ce_basicConstraints => Oid(array [] of {2,5,29,19}), id_ce_cRLNumber => Oid(array [] of {2,5,29,20}), id_ce_reasonCode => Oid(array [] of {2,5,29,21}), id_ce_instructionCode => Oid(array [] of {2,5,29,23}), id_ce_invalidityDate => Oid(array [] of {2,5,29,24}), id_ce_deltaCRLIndicator => Oid(array [] of {2,5,29,27}), id_ce_issuingDistributionPoint => Oid(array [] of {2,5,29,28}), id_ce_certificateIssuer => Oid(array [] of {2,5,29,29}), id_ce_nameConstraints => Oid(array [] of {2,5,29,30}), id_ce_cRLDistributionPoint => Oid(array [] of {2,5,29,31}), id_ce_certificatePolicies => Oid(array [] of {2,5,29,32}), id_ce_policyMapping => Oid(array [] of {2,5,29,33}), id_ce_authorityKeyIdentifier => Oid(array [] of {2,5,29,35}), id_ce_policyConstraints => Oid(array [] of {2,5,29,36}), # id_mr => Oid(array [] of {2,5,?}), # id_mr_certificateMatch => Oid(array [] of {2,5,?,35}), # id_mr_certificatePairExactMatch => # Oid(array [] of {2,5,?,36}), # id_mr_certificatePairMatch => Oid(array [] of {2,5,?,37}), # id_mr_certificateListExactMatch => # Oid(array [] of {2,5,?,38}), # id_mr_certificateListMatch => Oid(array [] of {2,5,?,39}), # id_mr_algorithmidentifierMatch => # Oid(array [] of {2,5,?,40}) }; # [public] init(): string { sys = load Sys Sys->PATH; if(X509_DEBUG) logfd = sys->fildes(1); keyring = load Keyring Keyring->PATH; if(keyring == nil) return sys->sprint("load %s: %r", Keyring->PATH); random = load Random Random->PATH; if(random == nil) return sys->sprint("load %s: %r", Random->PATH); daytime = load Daytime Daytime->PATH; if(daytime == nil) return sys->sprint("load %s: %r", Daytime->PATH); asn1 = load ASN1 ASN1->PATH; if(asn1 == nil) return sys->sprint("load %s: %r", ASN1->PATH); asn1->init(); pkcs = load PKCS PKCS->PATH; if(pkcs == nil) return sys->sprint("load %s: %r", PKCS->PATH); if((e := pkcs->init()) != nil) return sys->sprint("pkcs: %s", e); return nil; } # [private] log(s: string) { if(X509_DEBUG) sys->fprint(logfd, "x509: %s\n", s); } ## SIGNED { ToBeSigned } ::= SEQUENCE { ## toBeSigned ToBeSigned, ## COMPONENTS OF SIGNATURE { ToBeSigned }} ## ## SIGNATURE {OfSignature} ::= SEQUENCE { ## algorithmIdentifier AlgorithmIdentifier, ## encrypted ENCRYPTED { HASHED { OfSignature }}} ## ## ENCRYPTED { ToBeEnciphered } ::= BIT STRING ( CONSTRAINED BY { ## -- must be the result of applying an encipherment procedure -- ## -- to the BER-encoded octets of a value of -- ToBeEnciphered } ) # [public] Signed.decode(a: array of byte): (string, ref Signed) { parse: for(;;) { # interpret the enclosing structure (ok, tag, i, n) := der_dec1(a, 0, len a); if(!ok || n != len a || !tag.constr || tag.class != Universal || tag.num != SEQUENCE) break parse; s := ref Signed; # SIGNED sequence istart := i; (ok, tag, i, n) = der_dec1(a, i, len a); if(!ok || n == len a) break parse; s.tobe_signed = a[istart:n]; # AlgIdentifier istart = n; (ok, tag, i, n) = der_dec1(a, n, len a); if(!ok || n == len a || !tag.constr || tag.class != Universal || tag.num != SEQUENCE) { if(X509_DEBUG) log("signed: der data: " + sys->sprint("ok=%d, n=%d, constr=%d, class=%d, num=%d", ok, n, tag.constr, tag.class, tag.num)); break parse; } (ok, s.alg) = decode_algid(a[istart:n]); if(!ok) { if(X509_DEBUG) log("signed: alg identifier: syntax error"); break; } # signature (ok, tag, i, n) = der_dec1(a, n, len a); if(!ok || n != len a || tag.constr || tag.class != Universal || tag.num != BIT_STRING) { if(X509_DEBUG) log("signed: signature: " + sys->sprint("ok=%d, n=%d, constr=%d, class=%d, num=%d", ok, n, tag.constr, tag.class, tag.num)); break parse; } s.signature = a[i:n]; # to the end of no error been found return ("", s); } return ("signed: syntax error", nil); } # [public] # der encoding of signed data Signed.encode(s: self ref Signed): (string, array of byte) { (err, e_dat) := asn1->decode(s.tobe_signed); # why? if(err != "") return (err, nil); e_alg := pack_alg(s.alg); e_sig := ref Elem( Tag(Universal, BIT_STRING, 0), ref Value.BitString(0,s.signature) # DER encode of BIT STRING ); all := ref Elem( Tag(Universal, SEQUENCE, 1), ref Value.Seq(e_dat::e_alg::e_sig::nil) ); return asn1->encode(all); } # [public] Signed.sign(s: self ref Signed, sk: ref PrivateKey, hash: int): (string, array of byte) { # we require tobe_signed has 0 bits of padding if(int s.tobe_signed[0] != 0) return ("syntax error", nil); pick key := sk { RSA => (err, signature) := pkcs->rsa_sign(s.tobe_signed, key.sk, hash); s.signature = signature; # TODO: add AlgIdentifier based on public key and hash return (err, signature); DSS => # TODO: hash s.tobe_signed for signing (err, signature) := pkcs->dss_sign(s.tobe_signed, key.sk); s.signature = signature; return (err, signature); DH => return ("cannot sign using DH algorithm", nil); } return ("sign: failed", nil); } # [public] # hash algorithm should be MD2, MD4, MD5 or SHA Signed.verify(s: self ref Signed, pk: ref PublicKey, hash: int): int { ok := 0; pick key := pk { RSA => ok = pkcs->rsa_verify(s.tobe_signed, s.signature, key.pk, hash); DSS => # TODO: hash s.tobe_signed for verifying ok = pkcs->dss_verify(s.tobe_signed, s.signature, key.pk); DH => # simply failure } return ok; } # [public] Signed.tostring(s: self ref Signed): string { str := "Signed"; str += "\nToBeSigned: " + bastr(s.tobe_signed); str += "\nAlgorithm: " + s.alg.tostring(); str += "\nSignature: " + bastr(s.signature); return str + "\n"; } # DER # a) the definite form of length encoding shall be used, encoded in the minimum number of # octets; # b) for string types, the constructed form of encoding shall not be used; # c) if the value of a type is its default value, it shall be absent; # d) the components of a Set type shall be encoded in ascending order of their tag value; # e) the components of a Set-of type shall be encoded in ascending order of their octet value; # f) if the value of a Boolean type is true, the encoding shall have its contents octet # set to "FF"16; # g) each unused bits in the final octet of the encoding of a Bit String value, if there are # any, shall be set to zero; # h) the encoding of a Real type shall be such that bases 8, 10, and 16 shall not be used, # and the binary scaling factor shall be zero. # [private] # decode ASN1 one record at a time and return (err, tag, start of data, # end of data) for indefinite length, the end of data is same as 'n' der_dec1(a: array of byte, i, n: int): (int, Tag, int, int) { length: int; tag: Tag; ok := 1; (ok, i, tag) = der_dectag(a, i, n); if(ok) { (ok, i, length) = der_declen(a, i, n); if(ok) { if(length == -1) { if(!tag.constr) ok = 0; length = n - i; } else { if(i+length > n) ok = 0; } } } if(!ok && X509_DEBUG) log("der_dec1: syntax error"); return (ok, tag, i, i+length); } # [private] # der tag decode der_dectag(a: array of byte, i, n: int): (int, int, Tag) { ok := 1; class, num, constr: int; if(n-i >= 2) { v := int a[i++]; class = v & CLASS_MASK; if(v & CONSTR_MASK) constr = 1; else constr = 0; num = v & TAG_MASK; if(num == TAG_MASK) # long tag number (ok, i, num) = uint7_decode(a, i, n); } else ok = 0; if(!ok && X509_DEBUG) log("der_declen: syntax error"); return (ok, i, Tag(class, num, constr)); } # [private] int_decode(a: array of byte, i, n, count, unsigned: int): (int, int, int) { ok := 1; num := 0; if(n-i >= count) { if((count > 4) || (unsigned && count == 4 && (int a[i] & 16r80))) ok = 1; else { if(!unsigned && count > 0 && count < 4 && (int a[i] & 16r80)) num = -1; # all bits set for(j := 0; j < count; j++) { v := int a[i++]; num = (num << 8) | v; } } } else ok = 0; if(!ok && X509_DEBUG) log("int_decode: syntax error"); return (ok, i, num); } # [private] uint7_decode(a: array of byte, i, n: int) : (int, int, int) { ok := 1; num := 0; more := 1; while(more && i < n) { v := int a[i++]; if(num & 16r7F000000) { ok = 0; break; } num <<= 7; more = v & 16r80; num |= (v & 16r7F); } if(n == i) ok = 0; if(!ok && X509_DEBUG) log("uint7_decode: syntax error"); return (ok, i, num); } # [private] # der length decode - the definite form of length encoding shall be used, encoded # in the minimum number of octets der_declen(a: array of byte, i, n: int): (int, int, int) { ok := 1; num := 0; if(i < n) { v := int a[i++]; if(v & 16r80) return int_decode(a, i, n, v&16r7F, 1); else if(v == 16r80) # indefinite length ok = 0; else num = v; } else ok = 0; if(!ok && X509_DEBUG) log("der_declen: syntax error"); return (ok, i, num); } # [private] # parse der encoded algorithm identifier decode_algid(a: array of byte): (int, ref AlgIdentifier) { (err, el) := asn1->decode(a); if(err != "") { if(X509_DEBUG) log("decode_algid: " + err); return (0, nil); } return parse_alg(el); } ## TBS (Public Key) Certificate is signed by Certificate Authority and contains ## information of public key usage (as a comparison of Certificate Revocation List ## and Attribute Certificate). # [public] # constructs a certificate by parsing a der encoded certificate # returns error if parsing is failed or nil if parsing is ok certsyn(s: string): (string, ref Certificate) { if(0) sys->fprint(sys->fildes(2), "cert: %s\n", s); return ("certificate syntax: "+s, nil); } # Certificate ::= SEQUENCE { # certificateInfo CertificateInfo, # signatureAlgorithm AlgorithmIdentifier, # signature BIT STRING } # # CertificateInfo ::= SEQUENCE { # version [0] INTEGER DEFAULT v1 (0), # serialNumber INTEGER, # signature AlgorithmIdentifier, # issuer Name, # validity Validity, # subject Name, # subjectPublicKeyInfo SubjectPublicKeyInfo } # (version v2 has two more fields, optional unique identifiers for # issuer and subject; since we ignore these anyway, we won't parse them) # # Validity ::= SEQUENCE { # notBefore UTCTime, # notAfter UTCTime } # # SubjectPublicKeyInfo ::= SEQUENCE { # algorithm AlgorithmIdentifier, # subjectPublicKey BIT STRING } # # AlgorithmIdentifier ::= SEQUENCE { # algorithm OBJECT IDENTIFER, # parameters ANY DEFINED BY ALGORITHM OPTIONAL } # # Name ::= SEQUENCE OF RelativeDistinguishedName # # RelativeDistinguishedName ::= SETOF SIZE(1..MAX) OF AttributeTypeAndValue # # AttributeTypeAndValue ::= SEQUENCE { # type OBJECT IDENTIFER, # value DirectoryString } # (selected attributes have these Object Ids: # commonName {2 5 4 3} # countryName {2 5 4 6} # localityName {2 5 4 7} # stateOrProvinceName {2 5 4 8} # organizationName {2 5 4 10} # organizationalUnitName {2 5 4 11} # ) # # DirectoryString ::= CHOICE { # teletexString TeletexString, # printableString PrintableString, # universalString UniversalString } # # See rfc1423, rfc2437 for AlgorithmIdentifier, subjectPublicKeyInfo, signature. Certificate.decode(a: array of byte): (string, ref Certificate) { parse: # break on error for(;;) { (err, all) := asn1->decode(a); if(err != "") return certsyn(err); c := ref Certificate; # certificate must be a ASN1 sequence (ok, el) := all.is_seq(); if(!ok) return certsyn("invalid certificate sequence"); if(len el == 3){ # ssl3.b uses CertificateInfo; others use Certificate (TO DO: fix this botch) certinfo := hd el; sigalgid := hd tl el; sigbits := hd tl tl el; # certificate info is another ASN1 sequence (ok, el) = certinfo.is_seq(); if(!ok || len el < 6) return certsyn("invalid certificate info sequence"); } c.version = 0; # set to default (v1) (ok, c.version) = parse_version(hd el); if(!ok) return certsyn("can't parse version"); if(c.version > 0) { el = tl el; if(len el < 6) break parse; } # serial number (ok, c.serial_number) = parse_sernum(hd el); if(!ok) return certsyn("can't parse serial number"); el = tl el; # signature algorithm (ok, c.sig) = parse_alg(hd el); if(!ok) return certsyn("can't parse sigalg"); el = tl el; # issuer (ok, c.issuer) = parse_name(hd el); if(!ok) return certsyn("can't parse issuer"); el = tl el; # validity evalidity := hd el; (ok, c.validity) = parse_validity(evalidity); if(!ok) return certsyn("can't parse validity"); el = tl el; # Subject (ok, c.subject) = parse_name(hd el); if(!ok) return certsyn("can't parse subject"); el = tl el; # SubjectPublicKeyInfo (ok, c.subject_pkinfo) = parse_pkinfo(hd el); if(!ok) return certsyn("can't parse subject pk info"); el = tl el; # OPTIONAL for v2 and v3, must be in order # issuer unique identifier if(c.version == 0 && el != nil) return certsyn("bad unique ID"); if(el != nil) { if(c.version < 1) # at least v2 return certsyn("invalid v1 cert"); (ok, c.issuer_uid) = parse_uid(hd el, 1); if(ok) el = tl el; } # subject unique identifier if(el != nil) { if(c.version < 1) # v2 or v3 return certsyn("invalid v1 cert"); (ok, c.issuer_uid) = parse_uid(hd el, 2); if(ok) el = tl el; } # extensions if(el != nil) { if(c.version < 2) # must be v3 return certsyn("invalid v1/v2 cert"); e : ref Elem; (ok, e) = is_context(hd el, 3); if (!ok) break parse; (ok, c.exts) = parse_extlist(e); if(!ok) return certsyn("can't parse extension list"); el = tl el; } # must be no more left if(el != nil) return certsyn("unexpected data at end"); return ("", c); } return ("certificate: syntax error", nil); } # [public] # a der encoding of certificate data; returns (error, nil) tuple in failure Certificate.encode(c: self ref Certificate): (string, array of byte) { pack: for(;;) { el: list of ref Elem; # always has a version packed e_version := pack_version(c.version); if(e_version == nil) break pack; el = e_version :: el; # serial number e_sernum := pack_sernum(c.serial_number); if(e_sernum == nil) break pack; el = e_sernum :: el; # algorithm e_sig := pack_alg(c.sig); if(e_sig == nil) break pack; el = e_sig :: el; # issuer e_issuer := pack_name(c.issuer); if(e_issuer == nil) break pack; el = e_issuer :: el; # validity e_validity := pack_validity(c.validity); if(e_validity == nil) break pack; el = e_validity :: el; # subject e_subject := pack_name(c.subject); if(e_subject == nil) break pack; el = e_subject :: el; # public key info e_pkinfo := pack_pkinfo(c.subject_pkinfo); if(e_pkinfo == nil) break pack; el = e_pkinfo :: el; # issuer unique identifier if(c.issuer_uid != nil) { e_issuer_uid := pack_uid(c.issuer_uid); if(e_issuer_uid == nil) break pack; el = e_issuer_uid :: el; } # subject unique identifier if(c.subject_uid != nil) { e_subject_uid := pack_uid(c.subject_uid); if(e_subject_uid == nil) break pack; el = e_subject_uid :: el; } # extensions if(c.exts != nil) { e_exts := pack_extlist(c.exts); if(e_exts == nil) break pack; el = e_exts :: el; } # SEQUENCE order is important lseq: list of ref Elem; while(el != nil) { lseq = (hd el) :: lseq; el = tl el; } all := ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(lseq)); return asn1->encode(all); } return ("incompleted certificate; unable to pack", nil); } # [public] # converts content of a certificate as visible string Certificate.tostring(c: self ref Certificate): string { s := "\tTBS Certificate"; s += "\n\tVersion:\n\t\t" + string c.version; s += "\n\tSerialNumber:\n\t\t" + c.serial_number.iptostr(10); s += "\n\tSignature: " + c.sig.tostring(); s += "\n\tIssuer: " + c.issuer.tostring(); s += "\n\tValidity: " + c.validity.tostring("local"); s += "\n\tSubject: " + c.subject.tostring(); s += "\n\tSubjectPKInfo: " + c.subject_pkinfo.tostring(); s += "\n\tIssuerUID: " + bastr(c.issuer_uid); s += "\n\tSubjectUID: " + bastr(c.subject_uid); s += "\n\tExtensions: "; exts := c.exts; while(exts != nil) { s += "\t\t" + (hd exts).tostring(); exts = tl exts; } return s; } # [public] Certificate.is_expired(c: self ref Certificate, date: int): int { if(date > c.validity.not_after || date < c.validity.not_before) return 1; return 0; } # [private] # version is optional marked by explicit context tag 0; no version is # required if default version (v1) is used parse_version(e: ref Elem): (int, int) { ver := 0; (ans, ec) := is_context(e, 0); if(ans) { ok := 0; (ok, ver) = ec.is_int(); if(!ok || ver < 0 || ver > 2) return (0, -1); } return (1, ver); } # [private] pack_version(v: int): ref Elem { return ref Elem(Tag(Universal, INTEGER, 0), ref Value.Int(v)); } # [private] parse_sernum(e: ref Elem): (int, ref IPint) { (ok, a) := e.is_bigint(); if(ok) return (1, IPint.bebytestoip(a)); if(X509_DEBUG) log("parse_sernum: syntax error"); return (0, nil); } # [private] pack_sernum(sn: ref IPint): ref Elem { return ref Elem(Tag(Universal, INTEGER, 0), ref Value.BigInt(sn.iptobebytes())); } # [private] parse_alg(e: ref Elem): (int, ref AlgIdentifier) { parse: for(;;) { (ok, el) := e.is_seq(); if(!ok || el == nil) break parse; oid: ref Oid; (ok, oid) = (hd el).is_oid(); if(!ok) break parse; el = tl el; params: array of byte; if(el != nil) { # TODO: determine the object type based on oid # then parse params #unused: int; #(ok, unused, params) = (hd el).is_bitstring(); #if(!ok || unused || tl el != nil) # break parse; } return (1, ref AlgIdentifier(oid, params)); } if(X509_DEBUG) log("parse_alg: syntax error"); return (0, nil); } # [private] pack_alg(a: ref AlgIdentifier): ref Elem { if(a.oid != nil) { el: list of ref Elem; el = ref Elem(Tag(Universal, ASN1->OBJECT_ID, 0), ref Value.ObjId(a.oid)) :: nil; if(a.parameter != nil) { el = ref Elem( Tag(Universal, BIT_STRING, 0), ref Value.BitString(0, a.parameter) ) :: el; } return ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el)); } return nil; } # [private] parse_name(e: ref Elem): (int, ref Name) { parse: for(;;) { (ok, el) := e.is_seq(); if(!ok) break parse; lrd: list of ref RDName; while(el != nil) { rd: ref RDName; (ok, rd) = parse_rdname(hd el); if(!ok) break parse; lrd = rd :: lrd; el = tl el; } # SEQUENCE l: list of ref RDName; while(lrd != nil) { l = (hd lrd) :: l; lrd = tl lrd; } return (1, ref Name(l)); } if(X509_DEBUG) log("parse_name: syntax error"); return (0, nil); } # [private] pack_name(n: ref Name): ref Elem { el: list of ref Elem; lrd := n.rd_names; while(lrd != nil) { rd := pack_rdname(hd lrd); if(rd == nil) return nil; el = rd :: el; lrd = tl lrd; } # reverse order l: list of ref Elem; while(el != nil) { l = (hd el) :: l; el = tl el; } return ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(l)); } # [private] parse_rdname(e: ref Elem): (int, ref RDName) { parse: for(;;) { (ok, el) := e.is_set(); # unordered if(!ok) break parse; lava: list of ref AVA; while(el != nil) { ava: ref AVA; (ok, ava) = parse_ava(hd el); if(!ok) break parse; lava = ava :: lava; el = tl el; } return (1, ref RDName(lava)); } if(X509_DEBUG) log("parse_rdname: syntax error"); return (0, nil); } # [private] pack_rdname(r: ref RDName): ref Elem { el: list of ref Elem; lava := r.avas; while(lava != nil) { ava := pack_ava(hd lava); if(ava == nil) return nil; el = ava :: el; lava = tl lava; } return ref Elem(Tag(Universal, ASN1->SET, 1), ref Value.Set(el)); } # [private] parse_ava(e: ref Elem): (int, ref AVA) { parse: for(;;) { (ok, el) := e.is_seq(); if(!ok || len el != 2) break parse; a := ref AVA; (ok, a.oid) = (hd el).is_oid(); if(!ok) break parse; el = tl el; (ok, a.value) = (hd el).is_string(); if(!ok) break parse; return (1, a); } if(X509_DEBUG) log("parse_ava: syntax error"); return (0, nil); } # [private] pack_ava(a: ref AVA): ref Elem { el: list of ref Elem; if(a.oid == nil || a.value == "") return nil; # Note: order is important el = ref Elem(Tag(Universal, ASN1->GeneralString, 0), ref Value.String(a.value)) :: el; el = ref Elem(Tag(Universal, ASN1->OBJECT_ID, 0), ref Value.ObjId(a.oid)) :: el; return ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el)); } # [private] parse_validity(e: ref Elem): (int, ref Validity) { parse: for(;;) { (ok, el) := e.is_seq(); if(!ok || len el != 2) break parse; v := ref Validity; (ok, v.not_before) = parse_time(hd el, UTCTime); if(!ok) break parse; el = tl el; (ok, v.not_after) = parse_time(hd el, UTCTime); if(!ok) break parse; return (1, v); } if(X509_DEBUG) log("parse_validity: syntax error"); return (0, nil); } # [private] # standard says only UTC Time allowed for TBS Certificate, but there is exception of # GeneralizedTime for CRL and Attribute Certificate. Parsing is based on format of # UTCTime, GeneralizedTime or undetermined (any int not UTCTime or GeneralizedTime). parse_time(e: ref Elem, format: int): (int, int) { parse: for(;;) { (ok, date) := e.is_time(); if(!ok) break parse; if(e.tag.num != UTCTime && e.tag.num != GeneralizedTime) break parse; if(format == UTCTime && e.tag.num != UTCTime) break parse; if(format == GeneralizedTime && e.tag.num != GeneralizedTime) break parse; t := decode_time(date, e.tag.num); if(t < 0) break parse; return (1, t); } if(X509_DEBUG) log("parse_time: syntax error"); return (0, -1); } # [private] # decode a BER encoded UTC or Generalized time into epoch (seconds since 1/1/1970 GMT) # UTC time format: YYMMDDhhmm[ss](Z|(+|-)hhmm) # Generalized time format: YYYYMMDDhhmm[ss.s(...)](Z|(+|-)hhmm[ss.s(...)) decode_time(date: string, format: int): int { time := ref Daytime->Tm; parse: for(;;) { i := 0; if(format == UTCTime) { if(len date < 11) break parse; time.year = get2(date, i); if(time.year < 0) break parse; if(time.year < 70) time.year += 100; i += 2; } else { if(len date < 13) break parse; time.year = get2(date, i); if(time.year-19 < 0) break parse; time.year = (time.year - 19)*100; i += 2; time.year += get2(date, i); i += 2; } time.mon = get2(date, i) - 1; if(time.mon < 0 || time.mon > 11) break parse; i += 2; time.mday = get2(date, i); if(time.mday < 1 || time.mday > 31) break parse; i += 2; time.hour = get2(date, i); if(time.hour < 0 || time.hour > 23) break parse; i += 2; time.min = get2(date, i); if(time.min < 0 || time.min > 59) break parse; i += 2; if(int date[i] >= '0' && int date[i] <= '9') { if(len date < i+3) break parse; time.sec = get2(date, i); if(time.sec < 0 || time.sec > 59) break parse; i += 2; if(format == GeneralizedTime) { if((len date < i+3) || int date[i++] != '.') break parse; # ignore rest ig := int date[i]; while(ig >= '0' && ig <= '9' && i++ < len date) { ig = int date[i]; } } } else { time.sec = 0; } zf := int date[i]; if(zf != 'Z' && zf != '+' && zf != '-') break parse; if(zf == 'Z') { if(len date != i+1) break parse; time.tzoff = 0; } else { if(len date < i + 3) break parse; time.tzoff = get2(date, i+1); if(time.tzoff < 0 || time.tzoff > 23) break parse; i += 2; min := get2(date, i); if(min < 0 || min > 59) break parse; i += 2; sec := 0; if(i != len date) { if(format == UTCTime || len date < i+4) break parse; sec = get2(date, i); i += 2; # ignore the rest } time.tzoff = (time.tzoff*60 + min)*60 + sec; if(zf == '-') time.tzoff = -time.tzoff; } return daytime->tm2epoch(time); } if(X509_DEBUG) log("decode_time: syntax error: " + sys->sprint("year=%d mon=%d mday=%d hour=%d min=%d, sec=%d", time.year, time.mon, time.mday, time.hour, time.min, time.sec)); return -1; } # [private] # pack as UTC time pack_validity(v: ref Validity): ref Elem { el: list of ref Elem; el = ref Elem( Tag(Universal, UTCTime, 0), ref Value.String(pack_time(v.not_before, UTCTime)) ) :: nil; el = ref Elem( Tag(Universal, UTCTime, 0), ref Value.String(pack_time(v.not_after, UTCTime)) ) :: el; return ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el)); } # [private] # Format must be either UTCTime or GeneralizedTime # TODO: convert to coordinate time pack_time(t: int, format: int): string { date := array [32] of byte; tm := daytime->gmt(t); i := 0; if(format == UTCTime) { i = put2(date, tm.year, i); } else { # GeneralizedTime i = put2(date, 19 + tm.year/100, i); i = put2(date, tm.year%100, i); } i = put2(date, tm.mon, i); i = put2(date, tm.mday, i); i = put2(date, tm.hour, i); i = put2(date, tm.min, i); if(tm.sec != 0) { if(format == UTCTime) i = put2(date, tm.sec, i); else { i = put2(date, tm.sec, i); date[i++] = byte '.'; date[i++] = byte 0; } } if(tm.tzoff == 0) { date[i++] = byte 'Z'; } else { off := tm.tzoff; if(tm.tzoff < 0) { off = -off; date[i++] = byte '-'; } else { date[i++] = byte '+'; } hoff := int (off/3600); moff := int ((off%3600)/60); soff := int ((off%3600)%60); i = put2(date, hoff, i); i = put2(date, moff, i); if(soff) { if(format == UTCTime) i = put2(date, soff, i); else { i = put2(date, soff, i); date[i++] = byte '.'; date[i++] = byte 0; } } } return string date[0:i]; } # [private] parse_pkinfo(e: ref Elem): (int, ref SubjectPKInfo) { parse: for(;;) { p := ref SubjectPKInfo; (ok, el) := e.is_seq(); if(!ok || len el != 2) break parse; (ok, p.alg_id) = parse_alg(hd el); if(!ok) break parse; unused: int; (ok, unused, p.subject_pk) = (hd tl el).is_bitstring(); if(!ok || unused != 0) break parse; return (1, p); } if(X509_DEBUG) log("parse_pkinfo: syntax error"); return (0, nil); } # [private] pack_pkinfo(p: ref SubjectPKInfo): ref Elem { el: list of ref Elem; # SEQUENCE order is important el = ref Elem( Tag(Universal, BIT_STRING, 0), ref Value.BitString(0, p.subject_pk) # 0 bits unused ? ) :: nil; el = pack_alg(p.alg_id) :: el; return ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el)); } # [private] parse_uid(e: ref Elem, num: int): (int, array of byte) { ok, unused : int; uid : array of byte; e2 : ref Elem; parse: for(;;) { (ok, e2) = is_context(e, num); if (!ok) break parse; e = e2; (ok, unused, uid) = e.is_bitstring(); # if(!ok || unused != 0) if(!ok) break parse; return (1, uid); } if(X509_DEBUG) log("parse_uid: syntax error"); return (0, nil); } # [private] pack_uid(u: array of byte): ref Elem { return ref Elem(Tag(Universal, ASN1->BIT_STRING, 0), ref Value.BitString(0,u)); } # [private] parse_extlist(e: ref Elem): (int, list of ref Extension) { parse: # dummy loop for breaking out of for(;;) { l: list of ref Extension; (ok, el) := e.is_seq(); if(!ok) break parse; while(el != nil) { ext := ref Extension; (ok, ext) = parse_extension(hd el); if(!ok) break parse; l = ext :: l; el = tl el; } # sort to order nl: list of ref Extension; while(l != nil) { nl = (hd l) :: nl; l = tl l; } return (1, nl); } if(X509_DEBUG) log("parse_extlist: syntax error"); return (0, nil); } # [private] pack_extlist(e: list of ref Extension): ref Elem { el: list of ref Elem; exts := e; while(exts != nil) { ext := pack_extension(hd exts); if(ext == nil) return nil; el = ext :: el; exts = tl exts; } # reverse order l: list of ref Elem; while(el != nil) { l = (hd el) :: l; el = tl el; } return ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(l)); } # [private] # Require further parse to check oid if critical is set to TRUE (see parse_exts) parse_extension(e: ref Elem): (int, ref Extension) { parse: for(;;) { ext := ref Extension; (ok, el) := e.is_seq(); if(!ok) break parse; oid: ref Oid; (ok, oid) = (hd el).is_oid(); if(!ok) break parse; ext.oid = oid; el = tl el; # BOOLEAN DEFAULT FALSE (ok, ext.critical) = (hd el).is_int(); if(ok) el = tl el; else ext.critical = 0; if (len el != 1) { break parse; } (ok, ext.value) = (hd el).is_octetstring(); if(!ok) break parse; return (1, ext); } if(X509_DEBUG) log("parse_extension: syntax error"); return (0, nil); } # [private] pack_extension(e: ref Extension): ref Elem { el: list of ref Elem; if(e.oid == nil || (e.critical !=0 && e.critical != 1) || e.value == nil) return nil; # SEQUENCE order el = ref Elem(Tag(Universal, OCTET_STRING, 0), ref Value.Octets(e.value)) :: el; el = ref Elem(Tag(Universal, BOOLEAN, 0), ref Value.Bool(e.critical)) :: el; el = ref Elem(Tag(Universal, OBJECT_ID, 0), ref Value.ObjId(e.oid)) :: el; return ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el)); } # [public] AlgIdentifier.tostring(a: self ref AlgIdentifier): string { return "\n\t\toid: " + a.oid.tostring() + "\n\t\twith parameter: "+ bastr(a.parameter); } # [public] Name.equal(a: self ref Name, b: ref Name): int { rda := a.rd_names; rdb := b.rd_names; if(len rda != len rdb) return 0; while(rda != nil && rdb != nil) { ok := (hd rda).equal(hd rdb); if(!ok) return 0; rda = tl rda; rdb = tl rdb; } return 1; } # [public] # The sequence of RelativeDistinguishedName's gives a sort of pathname, from most general to # most specific. Each element of the path can be one or more (but usually just one) # attribute-value pair, such as countryName="US". We'll just form a "postal-style" address # string by concatenating the elements from most specific to least specific, separated by commas. Name.tostring(a: self ref Name): string { path: string; rdn := a.rd_names; while(rdn != nil) { path += (hd rdn).tostring(); rdn = tl rdn; if(rdn != nil) path += ","; } return path; } # [public] # The allocation of distinguished names is the responsibility of the Naming Authorities. # Each user shall therefore trust the Naming Authorities not to issue duplicate distinguished # names. The comparison shall be unique one to one match but may not in the same order. RDName.equal(a: self ref RDName, b: ref RDName): int { if(len a.avas != len b.avas) return 0; aa := a.avas; ba := b.avas; while(aa != nil) { found:= 0; rest: list of ref AVA; while(ba != nil) { ok := (hd ba).equal(hd ba); if(!ok) rest = (hd aa) :: rest; else { if(found) return 0; found = 1; } ba = tl ba; } if(found == 0) return 0; ba = rest; aa = tl aa; } return 1; } # [public] RDName.tostring(a: self ref RDName): string { s: string; avas := a.avas; while(avas != nil) { s += (hd avas).tostring(); avas = tl avas; if(avas != nil) s += "-"; } return s; } # [public] # AVA are equal if they have the same type oid and value AVA.equal(a: self ref AVA, b: ref AVA): int { # TODO: need to match different encoding (T61String vs. IA5String) if(a.value != b.value) return 0; return oid_cmp(a.oid, b.oid); } # [public] AVA.tostring(a: self ref AVA): string { return a.value; } # [public] Validity.tostring(v: self ref Validity, format: string): string { s: string; if(format == "local") { s = "\n\t\tnot_before[local]: "; s += daytime->text(daytime->local(v.not_before)); s += "\n\t\tnot_after[local]: "; s += daytime->text(daytime->local(v.not_after)); } else if(format == "gmt") { s = "\n\t\tnot_before[gmt]: "; s += daytime->text(daytime->gmt(v.not_before)); s += "\n\t\tnot_after[gmt]: "; s += daytime->text(daytime->gmt(v.not_after)); } else s += "unknown format: " + format; return s; } # [public] SubjectPKInfo.getPublicKey(pkinfo: self ref SubjectPKInfo): (string, int, ref PublicKey) { parse: for(;;) { pk: ref PublicKey; id := asn1->oid_lookup(pkinfo.alg_id.oid, pkcs->objIdTab); case id { PKCS->id_pkcs_rsaEncryption or PKCS->id_pkcs_md2WithRSAEncryption or PKCS->id_pkcs_md4WithRSAEncryption or PKCS->id_pkcs_md5WithRSAEncryption => (err, k) := pkcs->decode_rsapubkey(pkinfo.subject_pk); if(err != nil) break parse; pk = ref PublicKey.RSA(k); PKCS->id_algorithm_shaWithDSS => (err, k) := pkcs->decode_dsspubkey(pkinfo.subject_pk); if(err != nil) break parse; pk = ref PublicKey.DSS(k); PKCS->id_pkcs_dhKeyAgreement => (err, k) := pkcs->decode_dhpubkey(pkinfo.subject_pk); if(err != nil) break parse; pk = ref PublicKey.DH(k); * => break parse; } return ("", id, pk); } return ("subject public key: syntax error", -1, nil); } # [public] SubjectPKInfo.tostring(pkinfo: self ref SubjectPKInfo): string { s := pkinfo.alg_id.tostring(); s += "\n\t\tencoded key: " + bastr(pkinfo.subject_pk); return s; } # [public] Extension.tostring(e: self ref Extension): string { s := "oid: " + e.oid.tostring(); s += "critical: "; if(e.critical) s += "true "; else s += "false "; s += bastr(e.value); return s; } ## Certificate PATH ## A list of certificates needed to allow a particular user to obtain ## the public key of another, is known as a certificate path. A ## certificate path logically forms an unbroken chain of trusted ## points in the DIT between two users wishing to authenticate. ## To establish a certification path between user A and user B using ## the Directory without any prior information, each CA may store ## one certificate and one reverse certificate designated as ## corresponding to its superior CA. # The ASN.1 data byte definitions for certificates and a certificate # path is # # Certificates ::= SEQUENCE { # userCertificate Certificate, # certificationPath ForwardCertificationPath OPTIONAL } # # ForwardCertificationPath ::= SEQUENCE OF CrossCertificates # CrossCertificates ::= SET OF Certificate # # [public] # Verify a decoded certificate chain in order of root to user. This is useful for # non_ASN.1 encoding of certificates, e.g. in SSL. Return (0, error string) if # verification failure or (1, "") if verification ok verify_certchain(cs: list of array of byte): (int, string) { lsc: list of (ref Signed, ref Certificate); l := cs; while(l != nil) { (err, s) := Signed.decode(hd l); if(err != "") return (0, err); c: ref Certificate; (err, c) = Certificate.decode(s.tobe_signed); if(err != "") return (0, err); lsc = (s, c) :: lsc; l = tl l; } # reverse order a: list of (ref Signed, ref Certificate); while(lsc != nil) { a = (hd lsc) :: a; lsc = tl lsc; } return verify_certpath(a); } # [private] # along certificate path; first certificate is root verify_certpath(sc: list of (ref Signed, ref Certificate)): (int, string) { # verify self-signed root certificate (s, c) := hd sc; # TODO: check root RDName with known CAs and using # external verification of root - Directory service (err, id, pk) := c.subject_pkinfo.getPublicKey(); if(err != "") return (0, err); if(!is_validtime(c.validity) || !c.issuer.equal(c.subject) || !s.verify(pk, 0)) # TODO: prototype verify(key, ref AlgIdentifier)? return (0, "verification failure"); sc = tl sc; while(sc != nil) { (ns, nc) := hd sc; # TODO: check critical flags of extension list # check alt names field (err, id, pk) = c.subject_pkinfo.getPublicKey(); if(err != "") return (0, err); if(!is_validtime(nc.validity) || !nc.issuer.equal(c.subject) || !ns.verify(pk, 0)) # TODO: move prototype as ? return (0, "verification failure"); (s, c) = (ns, nc); sc = tl sc; } return (1, ""); } # [public] is_validtime(validity: ref Validity): int { # a little more expensive but more accurate now := daytime->now(); # need some conversion here if(now < validity.not_before || now > validity.not_after) return 0; return 1; } is_validpair(): int { return 0; } ## Certificate Revocation List (CRL) ## ## A CRL is a time-stampted list identifying revoked certificates. It is signed by a ## Certificate Authority (CA) and made freely available in a public repository. ## ## Each revoked certificate is identified in a CRL by its certificate serial number. ## When a certificate-using system uses a certificate (e.g., for verifying a remote ## user's digital signature), that system not only checks the certificate signature ## and validity but also acquires a suitably-recent CRL and checks that the certificate ## serial number is not on that CRL. The meaning of "suitably-recent" may vary with ## local policy, but it usually means the most recently-issued CRL. A CA issues a new ## CRL on a regular periodic basis (e.g., hourly, daily, or weekly). Entries are added ## on CRLs as revocations occur, and an entry may be removed when the certificate ## expiration date is reached. # [public] CRL.decode(a: array of byte): (string, ref CRL) { parse: # break on error for(;;) { (err, all) := asn1->decode(a); if(err != "") break parse; c := ref CRL; # CRL must be a ASN1 sequence (ok, el) := all.is_seq(); if(!ok || len el < 3) break parse; c.version = 1; # set to default (v2) (ok, c.version) = parse_version(hd el); if(!ok) break parse; if(c.version < 0) { el = tl el; if(len el < 4) break parse; } # signature algorithm (ok, c.sig) = parse_alg(hd el); if(!ok) break parse; el = tl el; # issuer: who issues the CRL (ok, c.issuer) = parse_name(hd el); if(!ok) break parse; el = tl el; # this update (ok, c.this_update) = parse_time(hd el, UTCTime); if(!ok) break parse; el = tl el; # OPTIONAL, must be in order # next_update if(el != nil) { (ok, c.next_update) = parse_time(hd el, UTCTime); if(!ok) break parse; el = tl el; } # revoked certificates if(el != nil) { (ok, c.revoked_certs) = parse_revoked_certs(hd el); if(!ok) break parse; el = tl el; } # extensions if(el != nil) { (ok, c.exts) = parse_extlist(hd el); if(!ok) break parse; el = tl el; } # must be no more left if(el != nil) break parse; return ("", c); } return ("CRL: syntax error", nil); } # [public] CRL.encode(c: self ref CRL): (string, array of byte) { pack: for(;;) { el: list of ref Elem; # always has a version packed e_version := pack_version(c.version); if(e_version == nil) break pack; el = e_version :: el; # algorithm e_sig := pack_alg(c.sig); if(e_sig == nil) break pack; el = e_sig :: el; # crl issuer e_issuer := pack_name(c.issuer); if(e_issuer == nil) break pack; el = e_issuer :: el; # validity e_this_update := pack_time(c.this_update, UTCTime); if(e_this_update == nil) break pack; el = ref Elem( Tag(Universal, ASN1->UTCTime, 0), ref Value.String(e_this_update) ) :: el; # next crl update if(c.next_update != 0) { e_next_update := pack_time(c.next_update, UTCTime); if(e_next_update == nil) break pack; el = ref Elem( Tag(Universal, ASN1->UTCTime, 0), ref Value.String(e_next_update) ) :: el; } # revoked certificates if(c.revoked_certs != nil) { e_revoked_certs := pack_revoked_certs(c.revoked_certs); if(e_revoked_certs == nil) break pack; el = e_revoked_certs :: el; } # crl extensions if(c.exts != nil) { e_exts := pack_extlist(c.exts); if(e_exts == nil) break pack; el = e_exts :: el; } # compose all elements lseq: list of ref Elem; while(el != nil) { lseq = (hd el) :: lseq; el = tl el; } all := ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(lseq)); (err, ret) := asn1->encode(all); if(err != "") break; return ("", ret); } return ("incompleted CRL; unable to pack", nil); } # [public] CRL.tostring(c: self ref CRL): string { s := "Certificate Revocation List (CRL)"; s += "\nVersion: " + string c.version; s += "\nSignature: " + c.sig.tostring(); s += "\nIssuer: " + c.issuer.tostring(); s += "\nThis Update: " + daytime->text(daytime->local(c.this_update)); s += "\nNext Update: " + daytime->text(daytime->local(c.next_update)); s += "\nRevoked Certificates: "; rcs := c.revoked_certs; while(rcs != nil) { s += "\t" + (hd rcs).tostring(); rcs = tl rcs; } s += "\nExtensions: "; exts := c.exts; while(exts != nil) { s += "\t" + (hd exts).tostring(); exts = tl exts; } return s; } # [public] CRL.is_revoked(c: self ref CRL, sn: ref IPint): int { es := c.revoked_certs; while(es != nil) { if(sn.eq((hd es).user_cert)) return 1; es = tl es; } return 0; } # [public] RevokedCert.tostring(rc: self ref RevokedCert): string { s := "Revoked Certificate"; if(rc.user_cert == nil) return s + " [Bad Format]\n"; s += "\nSerial Number: " + rc.user_cert.iptostr(10); if(rc.revoc_date != 0) s += "\nRevocation Date: " + daytime->text(daytime->local(rc.revoc_date)); if(rc.exts != nil) { exts := rc.exts; while(exts != nil) { s += "\t" + (hd exts).tostring(); exts = tl exts; } } return s; } # [private] parse_revoked_certs(e: ref Elem): (int, list of ref RevokedCert) { lc: list of ref RevokedCert; parse: for(;;) { (ok, el) := e.is_seq(); if(!ok) break parse; while(el != nil) { c: ref RevokedCert; (ok, c) = parse_revoked(hd el); if(!ok) break parse; lc = c :: lc; el = tl el; } return (1, lc); } return (0, nil); } # [private] pack_revoked_certs(r: list of ref RevokedCert): ref Elem { el: list of ref Elem; rs := r; while(rs != nil) { rc := pack_revoked(hd rs); if(rc == nil) return nil; el = rc :: el; rs = tl rs; } # reverse order l: list of ref Elem; while(el != nil) { l = (hd el) :: l; el = tl el; } return ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(l)); } # [private] parse_revoked(e: ref Elem): (int, ref RevokedCert) { parse: for(;;) { c: ref RevokedCert; (ok, el) := e.is_seq(); if(!ok || len el < 2) break parse; uc: array of byte; (ok, uc) = (hd el).is_bigint(); if(!ok) break parse; c.user_cert = IPint.bebytestoip(uc); el = tl el; (ok, c.revoc_date) = parse_time(hd el, UTCTime); if(!ok) break parse; el = tl el; if(el != nil) { (ok, c.exts) = parse_extlist(hd el); if(!ok) break parse; } return (1, c); } return (0, nil); } # [private] pack_revoked(r: ref RevokedCert): ref Elem { el: list of ref Elem; if(r.exts != nil) { e_exts := pack_extlist(r.exts); if(e_exts == nil) return nil; el = e_exts :: el; } if(r.revoc_date != 0) { e_date := pack_time(r.revoc_date, UTCTime); if(e_date == nil) return nil; el = ref Elem( Tag(Universal, ASN1->UTCTime, 0), ref Value.String(e_date) ) :: el; } if(r.user_cert == nil) return nil; el = ref Elem(Tag(Universal, INTEGER, 0), ref Value.BigInt(r.user_cert.iptobebytes()) ) :: el; return ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el)); } ## The extensions field allows addition of new fields to the structure ## without modification to the ASN.1 definition. An extension field ## consists of an extension identifier, a criticality flag, and a ## canonical encoding of a data value of an ASN.1 type associated with ## the identified extension. For those extensions where ordering of ## individual extensions within the SEQUENCE is significant, the ## specification of those individual extensions shall include the rules ## for the significance of the ordering. When an implementation ## processing a certificate does not recognize an extension, if the ## criticality flag is FALSE, it may ignore that extension. If the ## criticality flag is TRUE, unrecognized extensions shall cause the ## structure to be considered invalid, i.e. in a certificate, an ## unrecognized critical extension would cause validation of a signature ## using that certificate to fail. # [public] cr_exts(es: list of ref Extension): list of ref Extension { cr: list of ref Extension; l := es; while(l != nil) { e := hd l; if(e.critical == 1) cr = e :: cr; l = tl l; } return cr; } # [public] noncr_exts(es: list of ref Extension): list of ref Extension { ncr: list of ref Extension; l := es; while(l != nil) { e := hd l; if(e.critical == 0) ncr = e :: ncr; l = tl l; } return ncr; } # [public] parse_exts(exts: list of ref Extension): (string, list of ref ExtClass) { ets: list of ref ExtClass; l := exts; while(l != nil) { ext := hd l; (err, et) := ExtClass.decode(ext); if(err != "") return (err, nil); ets = et :: ets; l = tl l; } lseq: list of ref ExtClass; while(ets != nil) { lseq = (hd ets) :: lseq; ets = tl ets; } return ("", lseq); } # [public] ExtClass.decode(ext: ref Extension): (string, ref ExtClass) { err: string; eclass: ref ExtClass; oid := asn1->oid_lookup(ext.oid, objIdTab); case oid { id_ce_authorityKeyIdentifier => (err, eclass) = decode_authorityKeyIdentifier(ext); if(err == "" && ext.critical == 1) { err = "authority key identifier: should be non-critical"; break; } id_ce_subjectKeyIdentifier => (err, eclass) = decode_subjectKeyIdentifier(ext); if(err != "" && ext.critical != 0) { err = "subject key identifier: should be non-critical"; break; } id_ce_basicConstraints => (err, eclass) = decode_basicConstraints(ext); if(err == "" && ext.critical != 1) { err = "basic constraints: should be critical"; break; } id_ce_keyUsage => (err, eclass) = decode_keyUsage(ext); if(err == "" && ext.critical != 1) { err = "key usage: should be critical"; break; } id_ce_privateKeyUsage => (err, eclass) = decode_privateKeyUsage(ext); if(err == "" && ext.critical != 0) { err = "private key usage: should be non-critical"; break; } id_ce_policyMapping => (err, eclass) = decode_policyMapping(ext); if(err == "" && ext.critical != 0) { err = "policy mapping: should be non-critical"; break; } id_ce_certificatePolicies => (err, eclass) = decode_certificatePolicies(ext); # either critical or non-critical id_ce_issuerAltName => n: list of ref GeneralName; (err, n) = decode_alias(ext); if(err == "") eclass = ref ExtClass.IssuerAltName(n); # either critical or non-critical id_ce_subjectAltName => n: list of ref GeneralName; (err, n) = decode_alias(ext); if(err == "") eclass = ref ExtClass.SubjectAltName(n); # either critical or non-critical id_ce_nameConstraints => (err, eclass) = decode_nameConstraints(ext); # either critical or non-critical id_ce_policyConstraints => (err, eclass) = decode_policyConstraints(ext); # either critical or non-critical id_ce_cRLNumber => (err, eclass) = decode_cRLNumber(ext); if(err == "" && ext.critical != 0) { err = "crl number: should be non-critical"; break; } id_ce_reasonCode => (err, eclass) = decode_reasonCode(ext); if(err == "" && ext.critical != 0) { err = "crl reason: should be non-critical"; break; } id_ce_instructionCode => (err, eclass) = decode_instructionCode(ext); if(err == "" && ext.critical != 0) { err = "instruction code: should be non-critical"; break; } id_ce_invalidityDate => (err, eclass) = decode_invalidityDate(ext); if(err == "" && ext.critical != 0) { err = "invalidity date: should be non-critical"; break; } id_ce_issuingDistributionPoint => (err, eclass) = decode_issuingDistributionPoint(ext); if(err == "" && ext.critical != 1) { err = "issuing distribution point: should be critical"; break; } id_ce_cRLDistributionPoint => (err, eclass) = decode_cRLDistributionPoint(ext); # either critical or non-critical id_ce_certificateIssuer => (err, eclass) = decode_certificateIssuer(ext); if(err == "" && ext.critical != 1) { err = "certificate issuer: should be critical"; break; } id_ce_deltaCRLIndicator => (err, eclass) = decode_deltaCRLIndicator(ext); if(err == "" && ext.critical != 1) { err = "delta crl indicator: should be critical"; break; } id_ce_subjectDirectoryAttributes => (err, eclass) = decode_subjectDirectoryAttributes(ext); if(ext.critical != 0) { err = "subject directory attributes should be non-critical"; break; } * => err = "unknown extension class"; } return (err, eclass); } # [public] ExtClass.encode(ec: self ref ExtClass, critical: int): ref Extension { ext: ref Extension; if(critical) ; # unused pick c := ec { AuthorityKeyIdentifier => (err, a) := encode_authorityKeyIdentifier(c); if(err == "") ext = ref Extension(ref objIdTab[id_ce_authorityKeyIdentifier], 0, a); SubjectKeyIdentifier => (err, a) := encode_subjectKeyIdentifier(c); if(err == "") ext = ref Extension(ref objIdTab[id_ce_subjectKeyIdentifier], 0, a); BasicConstraints => (err, a) := encode_basicConstraints(c); if(err == "") ext = ref Extension(ref objIdTab[id_ce_basicConstraints], 0, a); KeyUsage => (err, a) := encode_keyUsage(c); if(err == "") ext = ref Extension(ref objIdTab[id_ce_keyUsage], 0, a); PrivateKeyUsage => (err, a) := encode_privateKeyUsage(c); if(err == "") ext = ref Extension(ref objIdTab[id_ce_privateKeyUsage], 0, a); PolicyMapping => (err, a) := encode_policyMapping(c); if(err == "") ext = ref Extension(ref objIdTab[id_ce_policyMapping], 0, a); CertificatePolicies => (err, a) := encode_certificatePolicies(c); if(err == "") ext = ref Extension(ref objIdTab[id_ce_certificatePolicies], 0, a); IssuerAltName => (err, a) := encode_alias(c.alias); if(err == "") ext = ref Extension(ref objIdTab[id_ce_issuerAltName], 0, a); SubjectAltName => (err, a) := encode_alias(c.alias); if(err == "") ext = ref Extension(ref objIdTab[id_ce_subjectAltName], 0, a); NameConstraints => (err, a) := encode_nameConstraints(c); if(err == "") ext = ref Extension(ref objIdTab[id_ce_nameConstraints], 0, a); PolicyConstraints => (err, a) := encode_policyConstraints(c); if(err == "") ext = ref Extension(ref objIdTab[id_ce_policyConstraints], 0, a); CRLNumber => (err, a) := encode_cRLNumber(c); if(err == "") ext = ref Extension(ref objIdTab[id_ce_cRLNumber], 0, a); ReasonCode => (err, a) := encode_reasonCode(c); if(err == "") ext = ref Extension(ref objIdTab[id_ce_reasonCode], 0, a); InstructionCode => (err, a) := encode_instructionCode(c); if(err == "") ext = ref Extension(ref objIdTab[id_ce_instructionCode], 0, a); InvalidityDate => (err, a) := encode_invalidityDate(c); if(err == "") ext = ref Extension(ref objIdTab[id_ce_invalidityDate], 0, a); CRLDistributionPoint => (err, a) := encode_cRLDistributionPoint(c); if(err == "") ext = ref Extension(ref objIdTab[id_ce_cRLDistributionPoint], 0, a); IssuingDistributionPoint => (err, a) := encode_issuingDistributionPoint(c); if(err == "") ext = ref Extension(ref objIdTab[id_ce_issuingDistributionPoint], 0, a); CertificateIssuer => (err, a) := encode_certificateIssuer(c); if(err == "") ext = ref Extension(ref objIdTab[id_ce_certificateIssuer], 0, a); DeltaCRLIndicator => (err, a) := encode_deltaCRLIndicator(c); if(err == "") ext = ref Extension(ref objIdTab[id_ce_deltaCRLIndicator], 0, a); SubjectDirectoryAttributes => (err, a) := encode_subjectDirectoryAttributes(c); if(err == "") ext = ref Extension(ref objIdTab[id_ce_subjectDirectoryAttributes], 0, a); } return ext; } # [public] ExtClass.tostring(et: self ref ExtClass): string { s: string; pick t := et { AuthorityKeyIdentifier => s = "Authority Key Identifier: "; s += "\n\tid = " + bastr(t.id); s += "\n\tissuer = " + t.issuer.tostring(); s += "\n\tserial_number = " + bastr(t.serial_number.iptobebytes()); SubjectKeyIdentifier => s = "Subject Key Identifier "; s += "\n\tid = " + bastr(t.id); BasicConstraints => s = "Basic Constraints: "; s += "\n\tdepth = " + string t.depth; KeyUsage => s = "Key Usage: "; s += "\n\tusage = "; PrivateKeyUsage => s = "Private Key Usage: "; s += "\n\tusage = "; PolicyMapping => s = "Policy Mapping: "; pl := t.pairs; while(pl != nil) { (issuer_oid, subject_oid) := hd pl; s += "\n\t(" + issuer_oid.tostring() + ", " + subject_oid.tostring() + ")"; pl = tl pl; } CertificatePolicies => s = "Certificate Policies: "; pl := t.policies; while(pl != nil) { s += (hd pl).tostring(); pl = tl pl; } IssuerAltName => s = "Issuer Alt Name: "; al := t.alias; while(al != nil) { s += (hd al).tostring() + ","; al = tl al; } SubjectAltName => s = "Subject Alt Name: "; al := t.alias; while(al != nil) { s += (hd al).tostring() + ","; al = tl al; } NameConstraints => s = "Name Constraints: "; s += "\n\tpermitted = "; p := t.permitted; while(p != nil) { s += (hd p).tostring(); p = tl p; } s += "\n\texcluded = "; e := t.excluded; while(e != nil) { s += (hd e).tostring(); e = tl e; } PolicyConstraints => s = "Policy Constraints: "; s += "\n\trequire = " + string t.require; s += "\n\tinhibit = " + string t.inhibit; CRLNumber => s = "CRL Number: "; s += "\n\tcurrent crl number = " + string t.curr; ReasonCode => s = "Reason Code: "; s += "\n\tcode = "; InstructionCode => s = "Instruction Code: "; s += "\n\thold with oid = " + t.oid.tostring(); InvalidityDate => s = "Invalidity Date: "; s += "\n\tdate = " + daytime->text(daytime->local(t.date)); CRLDistributionPoint => s = "CRL Distribution Point: "; ps := t.ps; while(ps != nil) { s += (hd ps).tostring() + ","; ps = tl ps; } IssuingDistributionPoint => s = "Issuing Distribution Point: "; CertificateIssuer => s = "Certificate Issuer: "; DeltaCRLIndicator => s = "Delta CRL Indicator: "; SubjectDirectoryAttributes => s = "Subject Directory Attributes: "; * => s = "Unknown Extension: "; } return s; } # [private] decode_authorityKeyIdentifier(ext: ref Extension): (string, ref ExtClass) { parse: for(;;) { (err, all) := asn1->decode(ext.value); if(err != "") break parse; (ok, el) := all.is_seq(); if(!ok) break parse; ak := ref ExtClass.AuthorityKeyIdentifier; e := hd el; (ok, e) = is_context(e, 0); if(ok) { (ok, ak.id) = e.is_octetstring(); if(!ok) break parse; el = tl el; } if(el != nil && len el != 2) break parse; e = hd el; (ok, e) = is_context(e, 1); if(!ok) break parse; (ok, ak.issuer) = parse_gname(e); if(!ok) break parse; e = hd tl el; (ok, e) = is_context(e, 2); if(!ok) break parse; (ok, ak.serial_number) = parse_sernum(e); if(!ok) break; return ("", ak); } return ("syntax error", nil); } # [private] encode_authorityKeyIdentifier(c: ref ExtClass.AuthorityKeyIdentifier): (string, array of byte) { el: list of ref Elem; if(c.serial_number != nil) { (ok, e) := pack_context( ref Elem( Tag(Universal, INTEGER, 0), ref Value.BigInt(c.serial_number.iptobebytes()) ), 2 ); if(!ok) return ("syntax error", nil); el = e :: nil; } if(c.issuer != nil) { (ok, e) := pack_gname(c.issuer); if(!ok) return ("authority key identifier: encoding error", nil); (ok, e) = pack_context(e, 1); if(!ok) return ("authority key identifier: encoding error", nil); el = e :: el; } if(c.id != nil) { (ok, e) := pack_context( ref Elem( Tag(Universal, OCTET_STRING, 0), ref Value.Octets(c.id) ), 0 ); if(!ok) return ("authority key identifier: encoding error", nil); el = e :: el; } return asn1->encode(ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el))); } # [private] decode_subjectKeyIdentifier(ext: ref Extension): (string, ref ExtClass) { parse: for(;;) { (err, all) := asn1->decode(ext.value); if(err != "") break parse; (ok, id) := all.is_octetstring(); if(!ok) break parse; return ("", ref ExtClass.SubjectKeyIdentifier(id)); } return ("subject key identifier: syntax error", nil); } # [private] encode_subjectKeyIdentifier(c: ref ExtClass.SubjectKeyIdentifier): (string, array of byte) { if(c.id == nil) return ("syntax error", nil); e := ref Elem(Tag(Universal, OCTET_STRING, 0), ref Value.Octets(c.id)); return asn1->encode(e); } # [private] decode_basicConstraints(ext: ref Extension): (string, ref ExtClass) { parse: for(;;) { (err, all) := asn1->decode(ext.value); if(err != "") break parse; (ok, el) := all.is_seq(); if(!ok || len el != 2) break parse; ca: int; (ok, ca) = (hd el).is_int(); # boolean if(!ok || ca != 1) break parse; path: int; (ok, path) = (hd tl el).is_int(); # integer if(!ok || path < 0) break parse; return ("", ref ExtClass.BasicConstraints(path)); } return ("basic constraints: syntax error", nil); } # [private] encode_basicConstraints(c: ref ExtClass.BasicConstraints): (string, array of byte) { el: list of ref Elem; el = ref Elem(Tag(Universal, INTEGER, 0), ref Value.Int(c.depth)) :: nil; el = ref Elem(Tag(Universal, BOOLEAN, 0), ref Value.Bool(1)) :: el; e := ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el)); return asn1->encode(e); } # [private] decode_keyUsage(ext: ref Extension): (string, ref ExtClass) { parse: for(;;) { # assert bits can fit into a limbo int if(len ext.value > 4) break parse; return ("", ref ExtClass.KeyUsage(b4int(ext.value))); } return ("key usage: syntax error", nil); } # [private] encode_keyUsage(c: ref ExtClass.KeyUsage): (string, array of byte) { return ("", int4b(c.usage)); } # [private] decode_privateKeyUsage(ext: ref Extension): (string, ref ExtClass) { parse: for(;;) { (err, all) := asn1->decode(ext.value); if(err != "") break parse; (ok, el) := all.is_seq(); if(!ok || len el < 1) # at least one exists break parse; v := ref Validity; e := hd el; (ok, e) = is_context(e, 0); if(ok) { (ok, v.not_before) = parse_time(e, GeneralizedTime); if(!ok) break parse; el = tl el; } if(el != nil) { e = hd el; (ok, e) = is_context(e, 1); if(!ok) break parse; (ok, v.not_after) = parse_time(e, GeneralizedTime); if(!ok) break parse; } return ("", ref ExtClass.PrivateKeyUsage(v)); } return ("private key usage: syntax error", nil); } # [private] encode_privateKeyUsage(c: ref ExtClass.PrivateKeyUsage): (string, array of byte) { el: list of ref Elem; e: ref Elem; ok := 1; p := c.period; if(p == nil) return ("encode private key usage: imcomplete data", nil); if(p.not_after > 0) { t := pack_time(p.not_after, GeneralizedTime); e = ref Elem(Tag(Universal, GeneralizedTime, 0), ref Value.String(t)); (ok, e) = pack_context(e, 1); if(!ok) return ("encode private key usage: illegal context", nil); el = e :: nil; } if(p.not_before > 0) { t := pack_time(p.not_before, GeneralizedTime); e = ref Elem(Tag(Universal, GeneralizedTime, 0), ref Value.String(t)); (ok, e) = pack_context(e, 0); if(!ok) return ("encode private key usage: illegal context", nil); el = e :: el; } e = ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el)); return asn1->encode(e); } # [private] decode_policyMapping(ext: ref Extension): (string, ref ExtClass) { parse: for(;;) { (err, all) := asn1->decode(ext.value); if(err != "") break parse; (ok, el) := all.is_seq(); if(!ok) break parse; l_pm: list of (ref Oid, ref Oid); while(el != nil) { e_pm: list of ref Elem; (ok, e_pm) = (hd el).is_seq(); if(!ok || len e_pm != 2) break parse; idp, sdp: ref Oid; (ok, idp) = (hd e_pm).is_oid(); if(!ok) break parse; (ok, sdp) = (hd tl e_pm).is_oid(); if(!ok) break parse; l_pm = (idp, sdp) :: l_pm; } # reverse the order l: list of (ref Oid, ref Oid); while(l_pm != nil) { l = (hd l_pm) :: l; l_pm = tl l_pm; } return ("", ref ExtClass.PolicyMapping(l)); } return ("policy mapping: syntax error", nil); } # [private] encode_policyMapping(c: ref ExtClass.PolicyMapping): (string, array of byte) { el, pel: list of ref Elem; if(c.pairs == nil) return ("policy mapping: incomplete data", nil); pl := c.pairs; while(pl != nil) { (a, b) := hd pl; if(a == nil || b == nil) return ("policy mapping: incomplete data", nil); be := ref Elem(Tag(Universal, OBJECT_ID, 0), ref Value.ObjId(b)); ae := ref Elem(Tag(Universal, OBJECT_ID, 0), ref Value.ObjId(a)); pel = ref Elem( Tag(Universal, SEQUENCE, 1), ref Value.Seq(ae::be::nil) ) :: pel; pl = tl pl; } while(pel != nil) { el = (hd pel) :: el; pel = tl pel; } e := ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el)); return asn1->encode(e); } # [private] decode_certificatePolicies(ext: ref Extension): (string, ref ExtClass) { parse: for(;;) { (err, all) := asn1->decode(ext.value); if(err != "") break parse; (ok, el) := all.is_seq(); if(!ok) break parse; l_pi: list of ref PolicyInfo; while(el != nil) { e_pi: list of ref Elem; (ok, e_pi) = (hd el).is_seq(); if(!ok || len e_pi > 2 || len e_pi < 1) break parse; pi: ref PolicyInfo; (ok, pi.oid) = (hd e_pi).is_oid(); if(!ok) break parse; # get optional policy qualifier info e_pi = tl e_pi; if(e_pi != nil) { e_pq: list of ref Elem; (ok, e_pq) = (hd e_pi).is_seq(); if(!ok || len e_pq > 2 || len e_pq < 1) break parse; l_pq: list of ref PolicyQualifier; while(e_pq != nil) { pq: ref PolicyQualifier; (ok, pq.oid) = (hd e_pq).is_oid(); if(!ok || pq.oid == nil) break parse; # get optional value if(tl e_pq != nil) { (ok, pq.value) = (hd tl e_pq).is_octetstring(); if(!ok) break parse; } l_pq = pq :: l_pq; e_pq = tl e_pq; } # reverse the order while(l_pq != nil) { pi.qualifiers = (hd l_pq) :: pi.qualifiers; l_pq = tl l_pq; } } l_pi = pi :: l_pi; } # reverse the order l: list of ref PolicyInfo; while(l_pi != nil) { l = (hd l_pi) :: l; l_pi = tl l_pi; } return ("", ref ExtClass.CertificatePolicies(l)); } return ("certificate policies: syntax error", nil); } # [private] encode_certificatePolicies(c: ref ExtClass.CertificatePolicies): (string, array of byte) { el, pel: list of ref Elem; pl := c.policies; while(pl != nil) { p := hd pl; if(p.oid == nil) return ("certificate policies: incomplete data", nil); plseq: list of ref Elem; if(p.qualifiers != nil) { ql := p.qualifiers; qel, qlseq: list of ref Elem; while(ql != nil) { pq := hd ql; pqseq: list of ref Elem; if(pq.oid == nil) return ("certificate policies: incomplete data", nil); if(pq.value != nil) { pqseq = ref Elem( Tag(Universal, OCTET_STRING, 0), ref Value.Octets(pq.value) ) :: nil; } pqseq = ref Elem( Tag(Universal, OBJECT_ID, 0), ref Value.ObjId(pq.oid) ) :: pqseq; qlseq = ref Elem( Tag(Universal, SEQUENCE, 1), ref Value.Seq(pqseq) ) :: qlseq; ql = tl ql; } while(qlseq != nil) { qel = (hd qlseq) :: qel; qlseq = tl qlseq; } plseq = ref Elem( Tag(Universal, SEQUENCE, 1), ref Value.Seq(qel) ) :: nil; } plseq = ref Elem( Tag(Universal, OBJECT_ID, 0), ref Value.ObjId(p.oid) ) :: plseq; pel = ref Elem( Tag(Universal, SEQUENCE, 1), ref Value.Seq(plseq) ) :: pel; pl = tl pl; } while(pel != nil) { el = (hd pel) :: el; pel = tl pel; } e := ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el)); return asn1->encode(e); } # [private] decode_alias(ext: ref Extension): (string, list of ref GeneralName) { parse: for(;;) { (err, all) := asn1->decode(ext.value); if(err != "") break parse; (ok, el) := all.is_seq(); if(!ok) break parse; l_sa: list of ref GeneralName; while(el != nil) { gn: ref GeneralName; (ok, gn) = parse_gname(hd el); if(!ok) break parse; l_sa = gn :: l_sa; el = tl el; } # reverse order sa: list of ref GeneralName; while(l_sa != nil) { sa = (hd l_sa) :: sa; l_sa = tl l_sa; } return ("", sa); } return ("alias: syntax error", nil); } # [private] encode_alias(gl: list of ref GeneralName): (string, array of byte) { el, gel: list of ref Elem; while(gl != nil) { g := hd gl; (ok, e) := pack_gname(g); if(!ok) return ("alias: encoding error", nil); gel = e :: gel; gl = tl gl; } while(gel != nil) { el = (hd gel) :: el; gel = tl gel; } e := ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el)); return asn1->encode(e); } # [private] decode_subjectDirectoryAttributes(ext: ref Extension): (string, ref ExtClass) { parse: for(;;) { (err, all) := asn1->decode(ext.value); if(err != "") break parse; (ok, el) := all.is_seq(); if(!ok) break parse; l_a: list of ref Attribute; while(el != nil) { a: ref Attribute; #(ok, a) = parse_attr(hd el); #if(!ok) # break parse; l_a = a :: l_a; el = tl el; } # reverse order as: list of ref Attribute; while(l_a != nil) { as = (hd l_a) :: as; l_a = tl l_a; } return ("", ref ExtClass.SubjectDirectoryAttributes(as)); } return ("subject directory attributes: syntax error", nil); } # [private] encode_subjectDirectoryAttributes(c: ref ExtClass.SubjectDirectoryAttributes) : (string, array of byte) { el, ael: list of ref Elem; al := c.attrs; while(al != nil) { (ok, e) := pack_attr(hd al); if(!ok) return ("subject directory attributes: encoding error", nil); ael = e :: ael; al = tl al; } while(ael != nil) { el = (hd ael) :: el; ael = tl ael; } e := ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el)); return asn1->encode(e); } # [private] decode_nameConstraints(ext: ref Extension): (string, ref ExtClass) { parse: for(;;) { (err, all) := asn1->decode(ext.value); if(err != "") break parse; (ok, el) := all.is_seq(); if(!ok || len el < 1 || len el > 2) break parse; nc := ref ExtClass.NameConstraints; if(el != nil) { (ok, nc.permitted) = parse_gsubtrees(hd el); if(!ok || nc.permitted == nil) break parse; el = tl el; } if(el!= nil) { (ok, nc.excluded) = parse_gsubtrees(hd el); if(!ok || nc.excluded == nil) break parse; } return ("", nc); } return ("name constraints: syntax error", nil); } # [private] encode_nameConstraints(c: ref ExtClass.NameConstraints): (string, array of byte) { el: list of ref Elem; if(c.permitted == nil && c.excluded == nil) return ("name constraints: incomplete data", nil); if(c.excluded != nil) { (ok, e) := pack_gsubtrees(c.excluded); if(!ok) return ("name constraints: encoding error", nil); el = e :: el; } if(c.permitted != nil) { (ok, e) := pack_gsubtrees(c.permitted); if(!ok) return ("name constraints: encoding error", nil); el = e :: el; } e := ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el)); return asn1->encode(e); } # [private] parse_gsubtrees(e: ref Elem): (int, list of ref GSubtree) { parse: for(;;) { (ok, el) := e.is_seq(); if(!ok) break parse; l, lgs: list of ref GSubtree; while(el != nil) { gs: ref GSubtree; (ok, gs) = parse_gsubtree(hd el); if(!ok) break parse; lgs = gs :: lgs; el = tl el; } while(lgs != nil) { l = (hd lgs) :: l; lgs = tl lgs; } return (1, l); } return (0, nil); } # [private] pack_gsubtrees(gs: list of ref GSubtree): (int, ref Elem) { el, l: list of ref Elem; while(gs != nil) { (ok, e) := pack_gsubtree(hd gs); if(!ok) return (0, nil); l = e :: l; } while(l != nil) { el = (hd l) :: el; l = tl l; } e := ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el)); return (1, e); } # [private] parse_gsubtree(e: ref Elem): (int, ref GSubtree) { parse: for(;;) { (ok, el) := e.is_seq(); if(!ok || len el > 3 || len el < 2) break parse; gs := ref GSubtree; e = hd el; (ok, gs.base) = parse_gname(e); if(!ok) break parse; el = tl el; e = hd el; (ok, e) = is_context(e, 0); if(ok) { (ok, gs.min) = e.is_int(); if(!ok) break parse; el = tl el; } # get optional maximum base distance if(el != nil) { e = hd el; (ok, e) = is_context(e, 1); if(!ok) break parse; (ok, gs.max) = e.is_int(); if(!ok) break parse; } return (1, gs); } return (0, nil); } # [private] pack_gsubtree(g: ref GSubtree): (int, ref Elem) { el: list of ref Elem; ok := 1; e: ref Elem; if(g.base == nil) return (0, nil); if(g.max != 0) { e = ref Elem(Tag(Universal, INTEGER, 0), ref Value.Int(g.max)); (ok, e) = pack_context(e, 1); if(!ok) return (0, nil); el = e :: nil; } if(g.min != 0) { e = ref Elem(Tag(Universal, INTEGER, 0), ref Value.Int(g.min)); (ok, e) = pack_context(e, 0); if(!ok) return (0, nil); el = e :: el; } (ok, e) = pack_gname(g.base); if(!ok) return (0, nil); el = e :: el; e = ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el)); return (1, e); } # [private] decode_policyConstraints(ext: ref Extension): (string, ref ExtClass) { parse: for(;;) { (err, all) := asn1->decode(ext.value); if(err != "") break parse; (ok, el) := all.is_seq(); if(!ok || len el < 1 || len el > 2) break parse; pc := ref ExtClass.PolicyConstraints; e := hd el; (ok, e) = is_context(e, 0); if(ok) { (ok, pc.require) = e.is_int(); if(!ok) break parse; el = tl el; } if(el != nil) { e = hd el; (ok, e) = is_context(e, 1); if(!ok) break parse; (ok, pc.inhibit) = e.is_int(); if(!ok) break parse; } return ("", pc); } return ("policy constraints: syntax error", nil); } # [private] encode_policyConstraints(c: ref ExtClass.PolicyConstraints): (string, array of byte) { el: list of ref Elem; ok := 1; if(c.inhibit > 0) { e := ref Elem(Tag(Universal, INTEGER, 0), ref Value.Int(c.inhibit)); (ok, e) = pack_context(e, 1); if(!ok) return ("policy constraints: encoding error", nil); el = e :: nil; } if(c.require > 0) { e := ref Elem(Tag(Universal, INTEGER, 0), ref Value.Int(c.require)); (ok, e) = pack_context(e, 0); if(!ok) return ("policy constraints: encoding error", nil); el = e :: el; } e := ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el)); return asn1->encode(e); } # [private] decode_cRLNumber(ext: ref Extension): (string, ref ExtClass) { parse: for(;;) { (err, all) := asn1->decode(ext.value); if(err != "") break parse; (ok, n) := all.is_int(); # TODO: should be IPint if(!ok) break parse; return ("", ref ExtClass.CRLNumber(n)); } return ("crl number: syntax error", nil); } # [private] encode_cRLNumber(c: ref ExtClass.CRLNumber): (string, array of byte) { e := ref Elem(Tag(Universal, INTEGER, 0), ref Value.Int(c.curr)); return asn1->encode(e); } # [private] decode_reasonCode(ext: ref Extension): (string, ref ExtClass) { parse: for(;;) { (err, all) := asn1->decode(ext.value); if(err != "") break parse; (ok, un_used_bits, code) := all.is_bitstring(); if(!ok) break parse; # no harm to ignore unused bits if(len code > 4) break parse; return ("", ref ExtClass.ReasonCode(b4int(code))); } return ("crl reason: syntax error", nil); } # [private] encode_reasonCode(c: ref ExtClass.ReasonCode): (string, array of byte) { e := ref Elem( Tag(Universal, BIT_STRING, 0), ref Value.BitString(0, int4b(c.code)) ); return asn1->encode(e); } # [private] decode_instructionCode(ext: ref Extension): (string, ref ExtClass) { parse: for(;;) { (err, all) := asn1->decode(ext.value); if(err != "") break parse; (ok, code) := all.is_oid(); if(!ok) break parse; return ("", ref ExtClass.InstructionCode(code)); } return ("instruction code: syntax error", nil); } # [private] encode_instructionCode(c: ref ExtClass.InstructionCode): (string, array of byte) { e := ref Elem(Tag(Universal, OBJECT_ID, 0), ref Value.ObjId(c.oid)); return asn1->encode(e); } # [private] decode_invalidityDate(ext: ref Extension): (string, ref ExtClass) { parse: for(;;) { (err, all) := asn1->decode(ext.value); if(err != "") break parse; (ok, date) := all.is_time(); if(!ok) break parse; t := decode_time(date, GeneralizedTime); if(t < 0) break parse; return ("", ref ExtClass.InvalidityDate(t)); } return ("", nil); } # [private] encode_invalidityDate(c: ref ExtClass.InvalidityDate): (string, array of byte) { e := ref Elem( Tag(Universal, GeneralizedTime, 0), ref Value.String(pack_time(c.date, GeneralizedTime)) ); return asn1->encode(e); } # [private] decode_cRLDistributionPoint(ext: ref Extension): (string, ref ExtClass) { parse: for(;;) { (err, all) := asn1->decode(ext.value); if(err != "") break parse; (ok, el) := all.is_seq(); if(!ok || len el < 1) # Note: at least one break parse; l, dpl: list of ref DistrPoint; while(el != nil) { dp: ref DistrPoint; (ok, dp) = parse_distrpoint(hd el); if(!ok) break parse; dpl = dp :: dpl; } # reverse order while(dpl != nil) { l = (hd dpl) :: l; dpl = tl dpl; } return ("", ref ExtClass.CRLDistributionPoint(l)); } return ("crl distribution point: syntax error", nil); } # [private] encode_cRLDistributionPoint(c: ref ExtClass.CRLDistributionPoint): (string, array of byte) { el, l: list of ref Elem; dpl := c.ps; if(dpl == nil) # at lease one return ("crl distribution point: incomplete data error", nil); while(dpl != nil) { (ok, e) := pack_distrpoint(hd dpl); if(!ok) return ("crl distribution point: encoding error", nil); l = e :: l; } while(l != nil) { el = (hd l) :: el; l = tl l; } e := ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el)); return asn1->encode(e); } # [private] parse_distrpoint(e: ref Elem): (int, ref DistrPoint) { parse: for(;;) { (ok, el) := e.is_seq(); if(!ok) break parse; if(!ok || len el > 3 || len el < 1) break parse; dp: ref DistrPoint; e = hd el; # get optional distribution point name (ok, e) = is_context(e, 0); if(ok) { (ok, dp.name) = parse_dpname(e); if(!ok) break parse; el = tl el; } # get optional reason flags if(el != nil) { e = hd el; (ok, e) = is_context(e, 1); if(ok) { unused_bits: int; reasons: array of byte; (ok, unused_bits, reasons) = e.is_bitstring(); if(!ok) break parse; # no harm to ignore unused bits if(len reasons > 4) break parse; dp.reasons = b4int(reasons); } el = tl el; } # get optional crl issuer if(el != nil) { e = hd el; (ok, e) = is_context(e, 2); if(!ok) break parse; (ok, dp.issuer) = parse_lgname(e); if(!ok) break parse; el = tl el; } # must be no more left if(el != nil) break parse; return (1, dp); } return (0, nil); } # [private] pack_distrpoint(dp: ref DistrPoint): (int, ref Elem) { el: list of ref Elem; if(dp.issuer != nil) { (ok, e) := pack_lgname(dp.issuer); if(!ok) return (0, nil); (ok, e) = pack_context(e, 2); if(!ok) return (0, nil); el = e :: nil; } if(dp.reasons != 0) { e := ref Elem( Tag(Universal, BIT_STRING, 0), ref Value.BitString(0, int4b(dp.reasons)) ); ok := 1; (ok, e) = pack_context(e, 1); if(!ok) return (0, nil); el = e :: el; } if(dp.name != nil) { (ok, e) := pack_dpname(dp.name); if(!ok) return (0, nil); (ok, e) = pack_context(e, 0); if(!ok) return (0, nil); el = e :: el; } e := ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el)); return (1, e); } # [private] parse_dpname(e: ref Elem): (int, ref DistrPointName) { parse: for(;;) { # parse CHOICE ok := 0; (ok, e) = is_context(e, 0); if(ok) { lg: list of ref GeneralName; (ok, lg) = parse_lgname(e); if(!ok) break parse; return (1, ref DistrPointName(lg, nil)); } (ok, e) = is_context(e, 1); if(!ok) break parse; n: ref Name; (ok, n) = parse_name(e); if(!ok) break parse; return (1, ref DistrPointName(nil, n.rd_names)); } return (0, nil); } # [private] pack_dpname(dpn: ref DistrPointName): (int, ref Elem) { if(dpn.full_name != nil) { (ok, e) := pack_lgname(dpn.full_name); if(!ok) return (0, nil); return pack_context(e, 0); } if(dpn.rdname != nil) { rdn := dpn.rdname; el, l: list of ref Elem; while(rdn != nil) { l = pack_rdname(hd rdn) :: l; rdn = tl rdn; } while(l != nil) { el = (hd l) :: el; l = tl l; } e := ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el)); return pack_context(e, 1); } return (0, nil); } # [private] decode_issuingDistributionPoint(ext: ref Extension): (string, ref ExtClass) { parse: for(;;) { (err, all) := asn1->decode(ext.value); if(err != "") break parse; (ok, el) := all.is_seq(); if(!ok || len el < 3 || len el > 5) break parse; ip := ref ExtClass.IssuingDistributionPoint; ae := hd el; # get optional distribution point name (ok, ae) = is_context(ae, 0); if(ok) { #(ok, ip.name) = parse_dpname(ae); if(!ok) break parse; el = tl el; } # get only contains user certs field if(el != nil) { ae = hd el; (ok, ae) = is_context(ae, 1); if(ok) { (ok, ip.only_usercerts) = ae.is_int(); # boolean if(!ok) break parse; } el = tl el; } # get only contains ca certs field if(el != nil) { ae = hd el; (ok, ae) = is_context(ae, 2); if(ok) { (ok, ip.only_cacerts) = ae.is_int(); # boolean if(!ok) break parse; } el = tl el; } # get optioinal only some reasons if(el != nil) { ae = hd el; (ok, ae) = is_context(ae, 3); if(ok) { reasons: array of byte; unused_bits: int; (ok, unused_bits, reasons) = ae.is_bitstring(); if(!ok || len reasons > 4) break parse; ip.only_reasons = b4int(reasons); } el = tl el; } # get indirect crl field if(el != nil) { ae = hd el; (ok, ae) = is_context(ae, 4); if(!ok) break parse; (ok, ip.indirect_crl) = ae.is_int(); # boolean if(!ok) break parse; el = tl el; } # must be no more left if(el != nil) break parse; return ("", ip); } return ("issuing distribution point: syntax error", nil); } # [private] encode_issuingDistributionPoint(c: ref ExtClass.IssuingDistributionPoint) : (string, array of byte) { el: list of ref Elem; ok := 1; if(c.indirect_crl != 0) { # no encode for DEFAULT e := ref Elem( Tag(Universal, BOOLEAN, 0), ref Value.Bool(c.indirect_crl) ); (ok, e) = pack_context(e, 4); if(!ok) return ("issuing distribution point: encoding error", nil); el = e :: el; } if(c.only_reasons != 0) { e := ref Elem( Tag(Universal, BIT_STRING, 0), ref Value.BitString(0, int4b(c.only_reasons)) ); (ok, e) = pack_context(e, 3); if(!ok) return ("issuing distribution point: encoding error", nil); el = e :: el; } if(c.only_cacerts != 0) { e := ref Elem( Tag(Universal, BOOLEAN, 0), ref Value.Bool(c.only_cacerts) ); (ok, e) = pack_context(e, 2); if(!ok) return ("issuing distribution point: encoding error", nil); el = e :: el; } if(c.only_usercerts != 0) { e := ref Elem( Tag(Universal, BOOLEAN, 0), ref Value.Bool(c.only_usercerts) ); (ok, e) = pack_context(e, 1); if(!ok) return ("issuing distribution point: encoding error", nil); el = e :: el; } if(c.name != nil) { e: ref Elem; (ok, e) = pack_dpname(c.name); if(!ok) return ("issuing distribution point: encoding error", nil); (ok, e) = pack_context(e, 0); if(!ok) return ("issuing distribution point: encoding error", nil); el = e :: el; } e := ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el)); return asn1->encode(e); } # [private] decode_certificateIssuer(ext: ref Extension): (string, ref ExtClass) { parse: for(;;) { (err, all) := asn1->decode(ext.value); if(err != "") break parse; (ok, el) := all.is_seq(); if(!ok) break parse; gl, gnl: list of ref GeneralName; while(el != nil) { g: ref GeneralName; (ok, g) = parse_gname(hd el); if(!ok) break parse; gnl = g :: gnl; el = tl el; } while(gnl != nil) { gl = (hd gnl) :: gl; gnl = tl gnl; } return ("", ref ExtClass.CertificateIssuer(gl)); } return ("certificate issuer: syntax error", nil); } # [private] encode_certificateIssuer(c: ref ExtClass.CertificateIssuer): (string, array of byte) { el, nel: list of ref Elem; ns := c.names; while(ns != nil) { (ok, e) := pack_gname(hd ns); if(!ok) return ("certificate issuer: encoding error", nil); nel = e :: nel; ns = tl ns; } while(nel != nil) { el = (hd nel) :: el; nel = tl nel; } e := ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el)); return asn1->encode(e); } # [private] decode_deltaCRLIndicator(ext: ref Extension): (string, ref ExtClass) { parse: for(;;) { (err, all) := asn1->decode(ext.value); if(err != "") break parse; (ok, b) := all.is_bigint(); if(!ok) break parse; return ("", ref ExtClass.DeltaCRLIndicator(IPint.bebytestoip(b))); } return ("delta crl number: syntax error", nil); } # [private] encode_deltaCRLIndicator(c: ref ExtClass.DeltaCRLIndicator): (string, array of byte) { e := ref Elem( Tag(Universal, INTEGER, 0), ref Value.BigInt(c.number.iptobebytes()) ); return asn1->encode(e); } # [public] GeneralName.tostring(gn: self ref GeneralName): string { s: string; pick g := gn { otherName => s = "other name: " + g.str; rfc822Name => s = "rfc822 name: " + g.str; dNSName => s = "dns name: " + g.str; x400Address => s = "x400 address: " + g.str; uniformResourceIdentifier => s = "url: " + g.str; iPAddress => s = "ip address: " + bastr(g.ip); registeredID => s = "oid: " + g.oid.tostring(); ediPartyName => s = "edi party name: "; s += "\n\tname assigner is " + g.nameAssigner.tostring(); s += "\n\tparty name is " + g.partyName.tostring(); directoryName => s = "directory name: " + g.dir.tostring(); } return s; } # [public] PolicyInfo.tostring(pi: self ref PolicyInfo): string { s := "oid: " + pi.oid.tostring(); s += "qualifiers: "; ql := pi.qualifiers; while(ql != nil) { s += (hd ql).tostring(); ql = tl ql; } return s; } # [public] PolicyQualifier.tostring(pq: self ref PolicyQualifier): string { s := "oid: " + pq.oid.tostring(); s += "value: " + bastr(pq.value); return s; } # [public] GSubtree.tostring(gs: self ref GSubtree): string { s := "base: " + gs.base.tostring(); s += "range: " + string gs.min + "-" + string gs.max; return s; } # [public] DistrPoint.tostring(dp: self ref DistrPoint): string { s := "Distribution Point: "; s += "\n\tname = "; d := dp.name; if(d.full_name != nil) { f := d.full_name; while(f != nil) { s += (hd f).tostring() + ","; f = tl f; } } else { r := d.rdname; while(r != nil) { s += (hd r).tostring() + ","; r = tl r; } } s += "\n\treasons = " + string dp.reasons; s += "\n\tissuer = "; gl := dp.issuer; while(gl != nil) { s += (hd gl).tostring() + ","; gl = tl gl; } return s; } # [private] is_context(e: ref Elem, num: int): (int, ref Elem) { if(e.tag.class == ASN1->Context && e.tag.num == num) { pick v := e.val { Octets => (err, all) := asn1->decode(v.bytes); if(err == "") return (1, all); } } return (0, nil); } # [private] pack_context(e: ref Elem, num: int): (int, ref Elem) { (err, b) := asn1->encode(e); if(err == "") return (1, ref Elem(Tag(Context, num, 0), ref Value.Octets(b))); return (0, nil); } # [private] parse_lgname(e: ref Elem): (int, list of ref GeneralName) { parse: for(;;) { (ok, el) := e.is_seq(); if(!ok) break parse; l, lg: list of ref GeneralName; while(el != nil) { g: ref GeneralName; (ok, g) = parse_gname(hd el); if(!ok) break parse; lg = g :: lg; el = tl el; } while(lg != nil) { l = (hd lg) :: l; lg = tl lg; } return (1, l); } return (0, nil); } # [private] pack_lgname(lg: list of ref GeneralName): (int, ref Elem) { el, gel: list of ref Elem; while(lg != nil) { (ok, e) := pack_gname(hd lg); if(!ok) return (0, nil); gel = e :: gel; lg = tl lg; } while(gel != nil) { el = (hd gel) :: el; gel = tl gel; } e := ref Elem(Tag(Universal, SEQUENCE, 1), ref Value.Seq(el)); return (1, e); } # [private] parse_gname(e: ref Elem): (int, ref GeneralName) { parse: for(;;) { g: ref GeneralName; ok := 1; case e.tag.num { 0 => (ok, e) = is_context(e, 0); if(!ok) break parse; str: string; (ok, str) = e.is_string(); if(!ok) break parse; g = ref GeneralName.otherName(str); 1 => (ok, e) = is_context(e, 1); if(!ok) break parse; str: string; (ok, str) = e.is_string(); if(!ok) break parse; g = ref GeneralName.rfc822Name(str); 2 => (ok, e) = is_context(e, 2); if(!ok) break parse; str: string; (ok, str) = e.is_string(); if(!ok) break parse; g = ref GeneralName.dNSName(str); 3 => (ok, e) = is_context(e, 3); if(!ok) break parse; str: string; (ok, str) = e.is_string(); if(!ok) break parse; g = ref GeneralName.x400Address(str); 4 => (ok, e) = is_context(e, 4); if(!ok) break parse; dir: ref Name; (ok, dir) = parse_name(e); if(!ok) break parse; g = ref GeneralName.directoryName(dir); 5 => (ok, e) = is_context(e, 5); if(!ok) break parse; el: list of ref Elem; (ok, el) = e.is_seq(); if(!ok || len el < 1 || len el > 3) break parse; na, pn: ref Name; (ok, e) = is_context(hd el, 0); if(ok) { (ok, na) = parse_name(e); if(!ok) break parse; el = tl el; } if(el != nil) { (ok, e) = is_context(hd el, 1); if(!ok) break parse; (ok, pn) = parse_name(e); if(!ok) break parse; } g = ref GeneralName.ediPartyName(na, pn); 6 => (ok, e) = is_context(e, 6); if(!ok) break parse; str: string; (ok, str) = e.is_string(); if(!ok) break parse; g = ref GeneralName.uniformResourceIdentifier(str); 7 => (ok, e) = is_context(e, 7); if(!ok) break parse; ip: array of byte; (ok, ip) = e.is_octetstring(); if(!ok) break parse; g = ref GeneralName.iPAddress(ip); 8 => (ok, e) = is_context(e, 8); if(!ok) break parse; oid: ref Oid; (ok, oid) = e.is_oid(); if(!ok) break parse; g = ref GeneralName.registeredID(oid); * => break parse; } return (1, g); } return (0, nil); } # [private] pack_gname(gn: ref GeneralName): (int, ref Elem) { e: ref Elem; ok := 1; pick g := gn { otherName => e = ref Elem( Tag(Universal, GeneralString, 0), ref Value.String(g.str) ); (ok, e) = pack_context(e, 0); if(!ok) return (0, nil); rfc822Name => e = ref Elem( Tag(Universal, IA5String, 0), ref Value.String(g.str) ); (ok, e) = pack_context(e, 1); if(!ok) return (0, nil); dNSName => e = ref Elem( Tag(Universal, IA5String, 0), ref Value.String(g.str) ); (ok, e) = pack_context(e, 2); if(!ok) return (0, nil); x400Address => e = ref Elem( Tag(Universal, GeneralString, 0), ref Value.String(g.str) ); (ok, e) = pack_context(e, 3); if(!ok) return (0, nil); uniformResourceIdentifier => e = ref Elem( Tag(Universal, GeneralString, 0), ref Value.String(g.str) ); (ok, e) = pack_context(e, 6); if(!ok) return (0, nil); iPAddress => e = ref Elem( Tag(Universal, OCTET_STRING, 0), ref Value.Octets(g.ip) ); (ok, e) = pack_context(e, 7); if(!ok) return (0, nil); registeredID => e = ref Elem( Tag(Universal, OBJECT_ID, 0), ref Value.ObjId(g.oid) ); (ok, e) = pack_context(e, 8); if(!ok) return (0, nil); ediPartyName => el: list of ref Elem; if(g.partyName != nil) { e = pack_name(g.partyName); (ok, e) = pack_context(e, 1); if(!ok) return (0, nil); el = e :: nil; } if(g.nameAssigner != nil) { e = pack_name(g.nameAssigner); (ok, e) = pack_context(e, 0); if(!ok) return (0, nil); el = e :: el; } e = ref Elem( Tag(Universal, SEQUENCE, 1), ref Value.Seq(el) ); (ok, e) = pack_context(e, 5); if(!ok) return (0, nil); directoryName => e = pack_name(g.dir); (ok, e) = pack_context(e, 4); if(!ok) return (0, nil); } return (1, e); } # [private] # convert at most 4 bytes to int, len buf must be less than 4 b4int(buf: array of byte): int { val := 0; for(i := 0; i < len buf; i++) val = (val << 8) | (int buf[i]); return val; } # [private] int4b(value: int): array of byte { n := 4; buf := array [n] of byte; while(n--) { buf[n] = byte value; value >>= 8; } return buf; } # [private] oid_cmp(a, b: ref Oid): int { na := len a.nums; nb := len b.nums; if(na != nb) return 0; for(i := 0; i < na; i++) { if(a.nums[i] != b.nums[i]) return 0; } return 1; } # [private] # decode two bytes into an integer [0-99] # return -1 for an invalid encoding get2(a: string, i: int): int { a0 := int a[i]; a1 := int a[i+1]; if(a0 < '0' || a0 > '9' || a1 < '0' || a1 > '9') return -1; return (a0 - '0')*10 + a1 - '0'; } # [private] # encode an integer [0-99] into two bytes put2(a: array of byte, n, i: int): int { a[i] = byte (n/10 + '0'); a[i+1] = byte (n%10 + '0'); return i+2; } # [private] bastr(a: array of byte) : string { ans := ""; for(i := 0; i < len a; i++) { if(i < len a - 1 && i%10 == 0) ans += "\n\t\t"; ans += sys->sprint("%2x ", int a[i]); } return ans; } # [private] parse_attr(nil: ref Elem): (int, ref Attribute) { return (0, nil); } # [private] pack_attr(nil: ref Attribute): (int, ref Elem) { return (0, nil); }