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