diff options
| author | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
|---|---|---|
| committer | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
| commit | 37da2899f40661e3e9631e497da8dc59b971cbd0 (patch) | |
| tree | cbc6d4680e347d906f5fa7fca73214418741df72 /appl/lib/crypt/x509.b | |
| parent | 54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff) | |
20060303a
Diffstat (limited to 'appl/lib/crypt/x509.b')
| -rw-r--r-- | appl/lib/crypt/x509.b | 4125 |
1 files changed, 4125 insertions, 0 deletions
diff --git a/appl/lib/crypt/x509.b b/appl/lib/crypt/x509.b new file mode 100644 index 00000000..059a58f6 --- /dev/null +++ b/appl/lib/crypt/x509.b @@ -0,0 +1,4125 @@ +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; + MD4, MD5, SHA1, 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); +} |
