summaryrefslogtreecommitdiff
path: root/appl/lib/crypt
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
commit37da2899f40661e3e9631e497da8dc59b971cbd0 (patch)
treecbc6d4680e347d906f5fa7fca73214418741df72 /appl/lib/crypt
parent54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff)
20060303a
Diffstat (limited to 'appl/lib/crypt')
-rw-r--r--appl/lib/crypt/mkfile24
-rw-r--r--appl/lib/crypt/pkcs.b572
-rw-r--r--appl/lib/crypt/ssl3.b5557
-rw-r--r--appl/lib/crypt/sslsession.b176
-rw-r--r--appl/lib/crypt/x509.b4125
5 files changed, 10454 insertions, 0 deletions
diff --git a/appl/lib/crypt/mkfile b/appl/lib/crypt/mkfile
new file mode 100644
index 00000000..c97dab08
--- /dev/null
+++ b/appl/lib/crypt/mkfile
@@ -0,0 +1,24 @@
+<../../../mkconfig
+
+TARG=\
+ pkcs.dis\
+ ssl3.dis\
+ sslsession.dis\
+ x509.dis\
+
+MODULES=
+
+SYSMODULES= \
+ asn1.m\
+ daytime.m\
+ keyring.m\
+ pkcs.m\
+ security.m\
+ ssl3.m\
+ sslsession.m\
+ sys.m\
+ x509.m\
+
+DISBIN=$ROOT/dis/lib/crypt
+
+<$ROOT/mkfiles/mkdis
diff --git a/appl/lib/crypt/pkcs.b b/appl/lib/crypt/pkcs.b
new file mode 100644
index 00000000..e74391ca
--- /dev/null
+++ b/appl/lib/crypt/pkcs.b
@@ -0,0 +1,572 @@
+implement PKCS;
+
+include "sys.m";
+ sys : Sys;
+
+include "keyring.m";
+ keyring : Keyring;
+ IPint : import keyring;
+ DESstate : import keyring;
+
+include "security.m";
+ random : Random;
+
+include "asn1.m";
+ asn1 : ASN1;
+ Elem, Oid : import asn1;
+
+include "pkcs.m";
+
+# pkcs object identifiers
+
+objIdTab = array [] of {
+ id_pkcs => Oid(array [] of {1,2,840,113549,1}),
+ id_pkcs_1 => Oid(array [] of {1,2,840,113549,1,1}),
+ id_pkcs_rsaEncryption => Oid(array [] of {1,2,840,113549,1,1,1}),
+ id_pkcs_md2WithRSAEncryption => Oid(array [] of {1,2,840,113549,1,1,2}),
+ id_pkcs_md4WithRSAEncryption => Oid(array [] of {1,2,840,113549,1,1,3}),
+ id_pkcs_md5WithRSAEncryption => Oid(array [] of {1,2,840,113549,1,1,4}),
+
+ id_pkcs_3 => Oid(array [] of {1,2,840,113549,1,3}),
+ id_pkcs_dhKeyAgreement => Oid(array [] of {1,2,840,113549,1,3,1}),
+
+ id_pkcs_5 => Oid(array [] of {1,2,840,113549,1,5}),
+ id_pkcs_pbeWithMD2AndDESCBC => Oid(array [] of {1,2,840,113549,1,5,1}),
+ id_pkcs_pbeWithMD5AndDESCBC => Oid(array [] of {1,2,840,113549,1,5,3}),
+
+ id_pkcs_7 => Oid(array [] of {1,2,840,113549,1,7}),
+ id_pkcs_data => Oid(array [] of {1,2,840,113549,1,7,1}),
+ id_pkcs_singnedData => Oid(array [] of {1,2,840,113549,1,7,2}),
+ id_pkcs_envelopedData => Oid(array [] of {1,2,840,113549,1,7,3}),
+ id_pkcs_signedAndEnvelopedData =>
+ Oid(array [] of {1,2,840,113549,1,7,4}),
+ id_pkcs_digestData => Oid(array [] of {1,2,840,113549,1,7,5}),
+ id_pkcs_encryptedData => Oid(array [] of {1,2,840,113549,1,7,6}),
+
+ id_pkcs_9 => Oid(array [] of {1,2,840,113549,1,9}),
+ id_pkcs_emailAddress => Oid(array [] of {1,2,840,113549,1,9,1}),
+ id_pkcs_unstructuredName => Oid(array [] of {1,2,840,113549,1,9,2}),
+ id_pkcs_contentType => Oid(array [] of {1,2,840,113549,1,9,3}),
+ id_pkcs_messageDigest => Oid(array [] of {1,2,840,113549,1,9,4}),
+ id_pkcs_signingTime => Oid(array [] of {1,2,840,113549,1,9,5}),
+ id_pkcs_countersignature => Oid(array [] of {1,2,840,113549,1,9,6}),
+ id_pkcs_challengePassword => Oid(array [] of {1,2,840,113549,1,9,7}),
+ id_pkcs_unstructuredAddress => Oid(array [] of {1,2,840,113549,1,9,8}),
+ id_pkcs_extCertAttrs => Oid(array [] of {1,2,840,113549,1,9,9}),
+ id_algorithm_shaWithDSS => Oid(array [] of {1,3,14,3,2,13})
+};
+
+# [public]
+# initialize PKCS module
+
+init(): string
+{
+ sys = load Sys Sys->PATH;
+ if(sys == nil)
+ return "load sys module failed";
+
+ keyring = load Keyring Keyring->PATH;
+ if(keyring == nil)
+ return "load keyring module failed";
+
+ random = load Random Random->PATH;
+ if(random == nil)
+ return "load random module failed";
+
+ asn1 = load ASN1 ASN1->PATH;
+ if(asn1 == nil)
+ return "load asn1 module failed";
+ asn1->init();
+
+ return "";
+}
+
+# [public]
+# Encrypt data according to PKCS#1, with given blocktype, using given key.
+
+rsa_encrypt(data: array of byte, key: ref RSAKey, blocktype: int)
+ : (string, array of byte)
+{
+ if(key == nil)
+ return ("null pkcs#1 key", nil);
+ k := key.modlen;
+ dlen := len data;
+ if(k < 12 || dlen > k-11)
+ return ("bad parameters for pkcs#1", nil);
+ padlen := k-3-dlen;
+ pad := random->randombuf(Random->NotQuiteRandom, padlen);
+ for(i:=0; i < padlen; i++) {
+ if(blocktype == 0)
+ pad[i] = byte 0;
+ else if(blocktype == 1)
+ pad[i] = byte 16rff;
+ else if(pad[i] == byte 0)
+ pad[i] = byte 1;
+ }
+ eb := array[k] of byte;
+ eb[0] = byte 0;
+ eb[1] = byte blocktype;
+ eb[2:] = pad[0:];
+ eb[padlen+2] = byte 0;
+ eb[padlen+3:] = data[0:];
+ return ("", rsacomp(eb, key));
+}
+
+# [public]
+# Decrypt data according to PKCS#1, with given key.
+# If public is true, expect a block type of 0 or 1, else 2.
+
+rsa_decrypt(data: array of byte, key: ref RSAKey, public: int)
+ : (string, array of byte)
+{
+ eb := rsacomp(data, key);
+ k := key.modlen;
+ if(len eb == k) {
+ bt := int eb[1];
+ if(int eb[0] == 0 && ((public && (bt == 0 || bt == 1)) || (!public && bt == 2))) {
+ for(i := 2; i < k; i++)
+ if(eb[i] == byte 0)
+ break;
+ if(i < k-1) {
+ ans := array[k-(i+1)] of byte;
+ ans[0:] = eb[i+1:];
+ return ("", ans);
+ }
+ }
+ }
+ return ("pkcs1 decryption error", nil);
+}
+
+# [private]
+# Do RSA computation on block according to key, and pad
+# result on left with zeros to make it key.modlen long.
+
+rsacomp(block: array of byte, key: ref RSAKey): array of byte
+{
+ x := keyring->IPint.bebytestoip(block);
+ y := x.expmod(key.exponent, key.modulus);
+ ybytes := y.iptobebytes();
+ k := key.modlen;
+ ylen := len ybytes;
+ if(ylen < k) {
+ a := array[k] of { * => byte 0};
+ a[k-ylen:] = ybytes[0:];
+ ybytes = a;
+ }
+ else if(ylen > k) {
+ # assume it has leading zeros (mod should make it so)
+ a := array[k] of byte;
+ a[0:] = ybytes[ylen-k:];
+ ybytes = a;
+ }
+ return ybytes;
+}
+
+# [public]
+
+rsa_sign(data: array of byte, sk: ref RSAKey, algid: int): (string, array of byte)
+{
+ # digesting and add proper padding to it
+ ph := padhash(data, algid);
+
+ return rsa_encrypt(ph, sk, 0); # blocktype <- padding with zero
+}
+
+# [public]
+
+rsa_verify(data, signature: array of byte, pk: ref RSAKey, algid: int): int
+{
+ # digesting and add proper padding to it
+ ph := padhash(data, algid);
+
+ (err, orig) := rsa_decrypt(signature, pk, 0); # blocktype ?
+ if(err != "" || !byte_cmp(orig, ph))
+ return 0;
+
+ return 1;
+}
+
+# [private]
+# padding block A
+PA := array [] of {
+ byte 16r30, byte 16r20, byte 16r30, byte 16r0c,
+ byte 16r06, byte 16r08, byte 16r2a, byte 16r86,
+ byte 16r48, byte 16r86, byte 16rf7, byte 16r0d,
+ byte 16r02
+};
+
+# [private]
+# padding block B
+PB := array [] of {byte 16r05, byte 16r00, byte 16r04, byte 16r10};
+
+# [private]
+# require either md5 or md2 of 16 bytes digest
+# length of padded digest = 13 + 1 + 4 + 16
+
+padhash(data: array of byte, algid: int): array of byte
+{
+ padded := array [34] of byte;
+ case algid {
+ MD2_WithRSAEncryption =>
+ padded[13] = byte 2;
+ # TODO: implement md2 in keyring module
+ # keyring->md2(data, len data, padded[18:], nil);
+
+ MD5_WithRSAEncryption =>
+ padded[13] = byte 5;
+ keyring->md5(data, len data, padded[18:], nil);
+ * =>
+ return nil;
+ }
+ padded[0:] = PA;
+ padded[14:] = PB;
+
+ return padded;
+}
+
+# [private]
+# compare byte to byte of two array of byte
+
+byte_cmp(a, b: array of byte): int
+{
+ if(len a != len b)
+ return 0;
+
+ for(i := 0; i < len a; i++) {
+ if(a[i] != b[i])
+ return 0;
+ }
+
+ return 1;
+}
+
+# [public]
+
+RSAKey.bits(key: self ref RSAKey): int
+{
+ return key.modulus.bits();
+}
+
+# [public]
+# Decode an RSAPublicKey ASN1 type, defined as:
+#
+# RSAPublickKey :: SEQUENCE {
+# modulus INTEGER,
+# publicExponent INTEGER
+# }
+
+decode_rsapubkey(a: array of byte): (string, ref RSAKey)
+{
+parse:
+ for(;;) {
+ (err, e) := asn1->decode(a);
+ if(err != "")
+ break parse;
+ (ok, el) := e.is_seq();
+ if(!ok || len el != 2)
+ break parse;
+ modbytes, expbytes: array of byte;
+ (ok, modbytes) = (hd el).is_bigint();
+ if(!ok)
+ break parse;
+ modulus := IPint.bebytestoip(modbytes);
+ # get modlen this way, because sometimes it
+ # comes with leading zeros that are to be ignored!
+ mbytes := modulus.iptobebytes();
+ modlen := len mbytes;
+ (ok, expbytes) = (hd tl el).is_bigint();
+ if(!ok)
+ break parse;
+ exponent := keyring->IPint.bebytestoip(expbytes);
+ return ("", ref RSAKey(modulus, modlen, exponent));
+ }
+ return ("rsa public key: syntax error", nil);
+}
+
+
+# [public]
+# generate a pair of DSS public and private keys
+
+generateDSSKeyPair(strength: int): (ref DSSPublicKey, ref DSSPrivateKey)
+{
+ # TODO: need add getRandBetween in IPint
+ return (nil, nil);
+}
+
+# [public]
+
+dss_sign(a: array of byte, sk: ref DSSPrivateKey): (string, array of byte)
+{
+ #signature, digest: array of byte;
+
+ #case hash {
+ #Keyring->MD4 =>
+ # digest = array [Keyring->MD4dlen] of byte;
+ # keyring->md4(a, len a, digest, nil);
+ #Keyring->MD5 =>
+ # digest = array [Keyring->MD5dlen] of byte;
+ # keyring->md5(a, len a, digest, nil);
+ #Keyring->SHA =>
+ # digest = array [Keyring->SHA1dlen] of byte;
+ # keyring->sha1(a, len a, digest, nil);
+ #* =>
+ # return ("unknown hash algorithm", nil);
+ #}
+
+ # TODO: add gcd or getRandBetween in Keyring->IPint
+ return ("unsupported error", nil);
+}
+
+# [public]
+
+dss_verify(a, signa: array of byte, pk: ref DSSPublicKey): int
+{
+ unsigned: array of byte;
+
+ #case hash {
+ #Keyring->MD4 =>
+ # digest = array [Keyring->MD4dlen] of byte;
+ # keyring->md4(a, len a, digest, nil);
+ #Keyring->MD5 =>
+ # digest = array [Keyring->MD5dlen] of byte;
+ # keyring->md5(a, len a, digest, nil);
+ #Keyring->SHA =>
+ # digest = array [Keyring->SHA1dlen] of byte;
+ # keyring->sha1(a, len a, digest, nil);
+ #* =>
+ # return 0;
+ #}
+
+ # get unsigned from signa and compare it with digest
+
+ if(byte_cmp(unsigned, a))
+ return 1;
+
+ return 0;
+}
+
+# [public]
+decode_dsspubkey(a: array of byte): (string, ref DSSPublicKey)
+{
+ return ("unsupported error", nil);
+}
+
+
+# [public]
+# generate DH parameters with prime length at least (default) 512 bits
+
+generateDHParams(primelen: int): ref DHParams
+{
+ # prime - at least 512 bits
+ if(primelen < 512) # DHmodlen
+ primelen = 512;
+
+ # generate prime and base (generator) integers
+ (p, g) := keyring->dhparams(primelen);
+ if(p == nil || g == nil)
+ return nil;
+
+ return ref DHParams(p, g, 0);
+}
+
+# [public]
+# generate public and private key pair
+# Note: use iptobytes as integer to octet string conversion
+# and bytestoip as octect string to integer reversion
+
+setupDHAgreement(dh: ref DHParams): (ref DHPrivateKey, ref DHPublicKey)
+{
+ if(dh == nil || dh.prime == nil || dh.base == nil)
+ return (nil, nil);
+
+ # prime length in bits
+ bits := dh.prime.bits();
+
+ # generate random private key of length between bits/4 and bits
+ x := IPint.random(bits/4, bits);
+ if(x == nil)
+ return (nil, nil);
+ dh.privateValueLength = x.bits();
+
+ # calculate public key
+ y := dh.base.expmod(x, dh.prime);
+ if(y == nil)
+ return (nil, nil);
+
+ return (ref DHPrivateKey(dh, y, x), ref DHPublicKey(dh, x));
+}
+
+# [public]
+# The second phase of Diffie-Hellman key agreement
+
+computeDHAgreedKey(dh: ref DHParams, mysk, upk: ref IPint)
+ : array of byte
+{
+ if(mysk == nil || upk == nil)
+ return nil;
+
+ # exponential - calculate agreed key (shared secret)
+ z := upk.expmod(mysk, dh.prime);
+
+ # integer to octet conversion
+ return z.iptobebytes();
+}
+
+# [public]
+# ASN1 encoding
+
+decode_dhpubkey(a: array of byte): (string, ref DHPublicKey)
+{
+ return ("unsupported error", nil);
+}
+
+
+# [public]
+# Digest the concatenation of password and salt with count iterations of
+# selected message-digest algorithm (either md2 or md5).
+# The first 8 bytes of the message digest become the DES key.
+# The last 8 bytes of the message digest become the initializing vector IV.
+
+generateDESKey(pw: array of byte, param: ref PBEParams, alg: int)
+ : (ref DESstate, array of byte, array of byte)
+{
+ if(param.iterationCount < 1)
+ return (nil, nil, nil);
+
+ # concanate password and salt
+ pwlen := len pw;
+ pslen := pwlen + len param.salt;
+ ps := array [pslen] of byte;
+ ps[0:] = pw;
+ ps[pwlen:] = param.salt;
+ key, iv: array of byte;
+
+ # digest iterations
+ case alg {
+ PBE_MD2_DESCBC =>
+ ds : ref Keyring->DigestState = nil;
+ # TODO: implement md2 in keyring module
+ #result := array [Keyring->MD2dlen] of byte;
+ #for(i := 0; i < param.iterationCount; i++)
+ # ds = keyring->md2(ps, pslen, nil, ds);
+ #keyring->md2(ps, pslen, result, ds);
+ #key = result[0:8];
+ #iv = result[8:];
+
+ PBE_MD5_DESCBC =>
+ ds: ref Keyring->DigestState = nil;
+ result := array [Keyring->MD5dlen] of byte;
+ for(i := 0; i < param.iterationCount; i++)
+ ds = keyring->md5(ps, pslen, nil, ds);
+ keyring->md5(ps, pslen, result, ds);
+ key = result[0:8];
+ iv = result[8:];
+
+ * =>
+ return (nil, nil, nil);
+ }
+
+ state := keyring->dessetup(key, iv);
+
+ return (state, key, iv);
+}
+
+# [public]
+# The message M and a padding string PS shall be formatted into
+# an octet string EB
+# EB = M + PS
+# where
+# PS = 1 if M mod 8 = 7;
+# PS = 2 + 2 if M mod 8 = 6;
+# ...
+# PS = 8 + 8 + 8 + 8 + 8 + 8 + 8 + 8 if M mod 8 = 0;
+
+pbe_encrypt(state: ref DESstate, m: array of byte): array of byte
+{
+ mlen := len m;
+ padvalue := mlen % 8;
+ pdlen := 8 - padvalue;
+
+ eb := array [mlen + pdlen] of byte;
+ eb[0:] = m;
+ for(i := mlen; i < pdlen; i++)
+ eb[i] = byte padvalue;
+
+ keyring->descbc(state, eb, len eb, Keyring->Encrypt);
+
+ return eb;
+}
+
+# [public]
+
+pbe_decrypt(state: ref DESstate, eb: array of byte): array of byte
+{
+ eblen := len eb;
+ if(eblen%8 != 0) # must a multiple of 8 bytes
+ return nil;
+
+ keyring->descbc(state, eb, eblen, Keyring->Decrypt);
+
+ # remove padding
+ for(i := eblen -8; i < 8; i++) {
+ if(int eb[i] == i) {
+ for(j := i; j < 8; j++)
+ if(int eb[j] != i)
+ break;
+ if(j == 8)
+ break;
+ }
+ }
+
+ return eb[0:i];
+}
+
+# [public]
+
+PrivateKeyInfo.encode(p: self ref PrivateKeyInfo): (string, array of byte)
+{
+
+ return ("unsupported error", nil);
+}
+
+# [public]
+
+PrivateKeyInfo.decode(a: array of byte): (string, ref PrivateKeyInfo)
+{
+ return ("unsupported error", nil);
+}
+
+# [public]
+
+EncryptedPrivateKeyInfo.encode(p: self ref EncryptedPrivateKeyInfo)
+ : (string, array of byte)
+{
+
+ return ("unsupported error", nil);
+}
+
+# [public]
+
+EncryptedPrivateKeyInfo.decode(a: array of byte)
+ : (string, ref EncryptedPrivateKeyInfo)
+{
+ return ("unsupported error", nil);
+}
+
+# [public]
+
+decode_extcertorcert(a: array of byte): (int, int, array of byte)
+{
+ (err, all) := asn1->decode(a);
+ if(err == "") {
+
+ }
+}
+
+# [public]
+
+encode_extcertorcert(a: array of byte, which: int): (int, array of byte)
+{
+
+}
+
diff --git a/appl/lib/crypt/ssl3.b b/appl/lib/crypt/ssl3.b
new file mode 100644
index 00000000..91819851
--- /dev/null
+++ b/appl/lib/crypt/ssl3.b
@@ -0,0 +1,5557 @@
+#
+# SSL 3.0 protocol
+#
+implement SSL3;
+
+include "sys.m";
+ sys : Sys;
+
+include "keyring.m";
+ keyring : Keyring;
+ IPint, DigestState : import keyring;
+
+include "security.m";
+ random : Random;
+ ssl : SSL;
+
+include "daytime.m";
+ daytime : Daytime;
+
+include "asn1.m";
+ asn1 : ASN1;
+
+include "pkcs.m";
+ pkcs : PKCS;
+ DHParams, DHPublicKey, DHPrivateKey,
+ RSAParams, RSAKey,
+ DSSPrivateKey, DSSPublicKey : import PKCS;
+
+include "x509.m";
+ x509 : X509;
+ Signed, Certificate, SubjectPKInfo : import x509;
+
+include "sslsession.m";
+ sslsession : SSLsession;
+ Session : import sslsession;
+
+include "ssl3.m";
+
+#
+# debug mode
+#
+SSL_DEBUG : con 0;
+logfd : ref Sys->FD;
+
+#
+# version {major, minor}
+#
+SSL_VERSION_2_0 := array [] of {byte 0, byte 16r02};
+SSL_VERSION_3_0 := array [] of {byte 16r03, byte 0};
+
+
+# SSL Record Protocol
+
+SSL_CHANGE_CIPHER_SPEC : con 20;
+ SSL_ALERT : con 21;
+ SSL_HANDSHAKE : con 22;
+ SSL_APPLICATION_DATA : con 23;
+ SSL_V2HANDSHAKE : con 0; # escape to sslv2
+
+# SSL Application Protocol consists of alert protocol, change cipher spec protocol
+# v2 and v3 handshake protocol and application data protocol. The v2 handshake
+# protocol is included only for backward compatibility
+
+Protocol: adt {
+ pick {
+ pAlert =>
+ alert : ref Alert;
+ pChangeCipherSpec =>
+ change_cipher_spec : ref ChangeCipherSpec;
+ pHandshake =>
+ handshake : ref Handshake;
+ pV2Handshake =>
+ handshake : ref V2Handshake;
+ pApplicationData =>
+ data : array of byte;
+ }
+
+ decode: fn(r: ref Record, ctx: ref Context): (ref Protocol, string);
+ encode: fn(p: self ref Protocol, vers: array of byte): (ref Record, string);
+ tostring: fn(p: self ref Protocol): string;
+};
+
+#
+# ssl alert protocol
+#
+SSL_WARNING : con 1;
+ SSL_FATAL : con 2;
+
+SSL_CLOSE_NOTIFY : con 0;
+ SSL_UNEXPECTED_MESSAGE : con 10;
+ SSL_BAD_RECORD_MAC : con 20;
+ SSL_DECOMPRESSION_FAILURE : con 30;
+ SSL_HANDSHAKE_FAILURE : con 40;
+ SSL_NO_CERTIFICATE : con 41;
+ SSL_BAD_CERTIFICATE : con 42;
+ SSL_UNSUPPORTED_CERTIFICATE : con 43;
+ SSL_CERTIFICATE_REVOKED : con 44;
+ SSL_CERTIFICATE_EXPIRED : con 45;
+ SSL_CERTIFICATE_UNKNOWN : con 46;
+ SSL_ILLEGAL_PARAMETER : con 47;
+
+Alert: adt {
+ level : int;
+ description : int;
+
+ tostring: fn(a: self ref Alert): string;
+};
+
+#
+# ssl change cipher spec protocol
+#
+ChangeCipherSpec: adt {
+ value : int;
+};
+
+#
+# ssl handshake protocol
+#
+SSL_HANDSHAKE_HELLO_REQUEST : con 0;
+ SSL_HANDSHAKE_CLIENT_HELLO : con 1;
+ SSL_HANDSHAKE_SERVER_HELLO : con 2;
+ SSL_HANDSHAKE_CERTIFICATE : con 11;
+ SSL_HANDSHAKE_SERVER_KEY_EXCHANGE : con 12;
+ SSL_HANDSHAKE_CERTIFICATE_REQUEST : con 13;
+ SSL_HANDSHAKE_SERVER_HELLO_DONE : con 14;
+ SSL_HANDSHAKE_CERTIFICATE_VERIFY : con 15;
+ SSL_HANDSHAKE_CLIENT_KEY_EXCHANGE : con 16;
+ SSL_HANDSHAKE_FINISHED : con 20;
+
+Handshake: adt {
+ pick {
+ HelloRequest =>
+ ClientHello =>
+ version : array of byte; # [2]
+ random : array of byte; # [32]
+ session_id : array of byte; # <0..32>
+ suites : array of byte; # [2] x
+ compressions : array of byte; # [1] x
+ ServerHello =>
+ version : array of byte; # [2]
+ random : array of byte; # [32]
+ session_id : array of byte; # <0..32>
+ suite : array of byte; # [2]
+ compression : byte; # [1]
+ Certificate =>
+ cert_list : list of array of byte; # X509 cert chain
+ ServerKeyExchange =>
+ xkey : array of byte; # exchange_keys
+ CertificateRequest =>
+ cert_types : array of byte;
+ dn_list : list of array of byte; # DN list
+ ServerHelloDone =>
+ CertificateVerify =>
+ signature : array of byte;
+ ClientKeyExchange =>
+ xkey : array of byte;
+ Finished =>
+ md5_hash : array of byte; # [16] Keyring->MD5dlen
+ sha_hash : array of byte; # [20] Keyring->SHA1dlen
+ }
+
+ decode: fn(buf: array of byte): (ref Handshake, string);
+ encode: fn(hm: self ref Handshake): (array of byte, string);
+ tostring: fn(hm: self ref Handshake): string;
+};
+
+# cipher suites and cipher specs (default, not all supported)
+# - key exchange, signature, encrypt and digest algorithms
+
+SSL3_Suites := array [] of {
+ NULL_WITH_NULL_NULL => array [] of {byte 0, byte 16r00},
+
+ RSA_WITH_NULL_MD5 => array [] of {byte 0, byte 16r01},
+ RSA_WITH_NULL_SHA => array [] of {byte 0, byte 16r02},
+ RSA_EXPORT_WITH_RC4_40_MD5 => array [] of {byte 0, byte 16r03},
+ RSA_WITH_RC4_128_MD5 => array [] of {byte 0, byte 16r04},
+ RSA_WITH_RC4_128_SHA => array [] of {byte 0, byte 16r05},
+ RSA_EXPORT_WITH_RC2_CBC_40_MD5 => array [] of {byte 0, byte 16r06},
+ RSA_WITH_IDEA_CBC_SHA => array [] of {byte 0, byte 16r07},
+ RSA_EXPORT_WITH_DES40_CBC_SHA => array [] of {byte 0, byte 16r08},
+ RSA_WITH_DES_CBC_SHA => array [] of {byte 0, byte 16r09},
+ RSA_WITH_3DES_EDE_CBC_SHA => array [] of {byte 0, byte 16r0A},
+
+ DH_DSS_EXPORT_WITH_DES40_CBC_SHA => array [] of {byte 0, byte 16r0B},
+ DH_DSS_WITH_DES_CBC_SHA => array [] of {byte 0, byte 16r0C},
+ DH_DSS_WITH_3DES_EDE_CBC_SHA => array [] of {byte 0, byte 16r0D},
+ DH_RSA_EXPORT_WITH_DES40_CBC_SHA => array [] of {byte 0, byte 16r0E},
+ DH_RSA_WITH_DES_CBC_SHA => array [] of {byte 0, byte 16r0F},
+ DH_RSA_WITH_3DES_EDE_CBC_SHA => array [] of {byte 0, byte 16r10},
+ DHE_DSS_EXPORT_WITH_DES40_CBC_SHA => array [] of {byte 0, byte 16r11},
+ DHE_DSS_WITH_DES_CBC_SHA => array [] of {byte 0, byte 16r12},
+ DHE_DSS_WITH_3DES_EDE_CBC_SHA => array [] of {byte 0, byte 16r13},
+ DHE_RSA_EXPORT_WITH_DES40_CBC_SHA => array [] of {byte 0, byte 16r14},
+ DHE_RSA_WITH_DES_CBC_SHA => array [] of {byte 0, byte 16r15},
+ DHE_RSA_WITH_3DES_EDE_CBC_SHA => array [] of {byte 0, byte 16r16},
+
+ DH_anon_EXPORT_WITH_RC4_40_MD5 => array [] of {byte 0, byte 16r17},
+ DH_anon_WITH_RC4_128_MD5 => array [] of {byte 0, byte 16r18},
+ DH_anon_EXPORT_WITH_DES40_CBC_SHA => array [] of {byte 0, byte 16r19},
+ DH_anon_WITH_DES_CBC_SHA => array [] of {byte 0, byte 16r1A},
+ DH_anon_WITH_3DES_EDE_CBC_SHA => array [] of {byte 0, byte 16r1B},
+
+ FORTEZZA_KEA_WITH_NULL_SHA => array [] of {byte 0, byte 16r1C},
+ FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA => array [] of {byte 0, byte 16r1D},
+ FORTEZZA_KEA_WITH_RC4_128_SHA => array [] of {byte 0, byte 16r1E},
+};
+
+#
+# key exchange algorithms
+#
+DHmodlen : con 512; # default length
+
+
+#
+# certificate types
+#
+SSL_RSA_sign : con 1;
+ SSL_DSS_sign : con 2;
+ SSL_RSA_fixed_DH : con 3;
+ SSL_DSS_fixed_DH : con 4;
+ SSL_RSA_emhemeral_DH : con 5;
+ SSL_DSS_empemeral_DH : con 6;
+ SSL_FORTEZZA_MISSI : con 20;
+
+#
+# cipher definitions
+#
+SSL_EXPORT_TRUE : con 0;
+ SSL_EXPORT_FALSE : con 1;
+
+SSL_NULL_CIPHER,
+ SSL_RC4,
+ SSL_RC2_CBC,
+ SSL_IDEA_CBC,
+ SSL_DES_CBC,
+ SSL_3DES_EDE_CBC,
+ SSL_FORTEZZA_CBC : con iota;
+
+SSL_STREAM_CIPHER,
+ SSL_BLOCK_CIPHER : con iota;
+
+SSL_NULL_MAC,
+ SSL_MD5,
+ SSL_SHA : con iota;
+
+#
+# MAC paddings
+#
+SSL_MAX_MAC_PADDING : con 48;
+SSL_MAC_PAD1 := array [] of {
+ byte 16r36, byte 16r36, byte 16r36, byte 16r36,
+ byte 16r36, byte 16r36, byte 16r36, byte 16r36,
+ byte 16r36, byte 16r36, byte 16r36, byte 16r36,
+ byte 16r36, byte 16r36, byte 16r36, byte 16r36,
+ byte 16r36, byte 16r36, byte 16r36, byte 16r36,
+ byte 16r36, byte 16r36, byte 16r36, byte 16r36,
+ byte 16r36, byte 16r36, byte 16r36, byte 16r36,
+ byte 16r36, byte 16r36, byte 16r36, byte 16r36,
+ byte 16r36, byte 16r36, byte 16r36, byte 16r36,
+ byte 16r36, byte 16r36, byte 16r36, byte 16r36,
+ byte 16r36, byte 16r36, byte 16r36, byte 16r36,
+ byte 16r36, byte 16r36, byte 16r36, byte 16r36,
+};
+SSL_MAC_PAD2 := array [] of {
+ byte 16r5c, byte 16r5c, byte 16r5c, byte 16r5c,
+ byte 16r5c, byte 16r5c, byte 16r5c, byte 16r5c,
+ byte 16r5c, byte 16r5c, byte 16r5c, byte 16r5c,
+ byte 16r5c, byte 16r5c, byte 16r5c, byte 16r5c,
+ byte 16r5c, byte 16r5c, byte 16r5c, byte 16r5c,
+ byte 16r5c, byte 16r5c, byte 16r5c, byte 16r5c,
+ byte 16r5c, byte 16r5c, byte 16r5c, byte 16r5c,
+ byte 16r5c, byte 16r5c, byte 16r5c, byte 16r5c,
+ byte 16r5c, byte 16r5c, byte 16r5c, byte 16r5c,
+ byte 16r5c, byte 16r5c, byte 16r5c, byte 16r5c,
+ byte 16r5c, byte 16r5c, byte 16r5c, byte 16r5c,
+ byte 16r5c, byte 16r5c, byte 16r5c, byte 16r5c,
+};
+
+#
+# finished messages
+#
+SSL_CLIENT_SENDER := array [] of {
+ byte 16r43, byte 16r4C, byte 16r4E, byte 16r54};
+SSL_SERVER_SENDER := array [] of {
+ byte 16r53, byte 16r52, byte 16r56, byte 16r52};
+
+#
+# a default distiguished names
+#
+RSA_COMMERCIAL_CA_ROOT_SUBJECT_NAME := array [] of {
+ byte 16r30, byte 16r5F, byte 16r31, byte 16r0B,
+ byte 16r30, byte 16r09, byte 16r06, byte 16r03,
+ byte 16r55, byte 16r04, byte 16r06, byte 16r13,
+ byte 16r02, byte 16r55, byte 16r53, byte 16r31,
+ byte 16r20, byte 16r30, byte 16r1E, byte 16r06,
+ byte 16r03, byte 16r55, byte 16r04, byte 16r0A,
+ byte 16r13, byte 16r17, byte 16r52, byte 16r53,
+ byte 16r41, byte 16r20, byte 16r44, byte 16r61,
+ byte 16r74, byte 16r61, byte 16r20, byte 16r53,
+ byte 16r65, byte 16r63, byte 16r75, byte 16r72,
+ byte 16r69, byte 16r74, byte 16r79, byte 16r2C,
+ byte 16r20, byte 16r49, byte 16r6E, byte 16r63,
+ byte 16r2E, byte 16r31, byte 16r2E, byte 16r30,
+ byte 16r2C, byte 16r06, byte 16r03, byte 16r55,
+ byte 16r04, byte 16r0B, byte 16r13, byte 16r25,
+ byte 16r53, byte 16r65, byte 16r63, byte 16r75,
+ byte 16r72, byte 16r65, byte 16r20, byte 16r53,
+ byte 16r65, byte 16r72, byte 16r76, byte 16r65,
+ byte 16r72, byte 16r20, byte 16r43, byte 16r65,
+ byte 16r72, byte 16r74, byte 16r69, byte 16r66,
+ byte 16r69, byte 16r63, byte 16r61, byte 16r74,
+ byte 16r69, byte 16r6F, byte 16r6E, byte 16r20,
+ byte 16r41, byte 16r75, byte 16r74, byte 16r68,
+ byte 16r6F, byte 16r72, byte 16r69, byte 16r74,
+ byte 16r79,
+};
+
+# SSL internal status
+USE_DEVSSL,
+ SSL3_RECORD,
+ SSL3_HANDSHAKE,
+ SSL2_HANDSHAKE,
+ CLIENT_SIDE,
+ SESSION_RESUMABLE,
+ CLIENT_AUTH,
+ CERT_REQUEST,
+ CERT_SENT,
+ CERT_RECEIVED,
+ OUT_READY,
+ IN_READY : con 1 << iota;
+
+# SSL internal state
+STATE_EXIT,
+ STATE_CHANGE_CIPHER_SPEC,
+ STATE_HELLO_REQUEST,
+ STATE_CLIENT_HELLO,
+ STATE_SERVER_HELLO,
+ STATE_CLIENT_KEY_EXCHANGE,
+ STATE_SERVER_KEY_EXCHANGE,
+ STATE_SERVER_HELLO_DONE,
+ STATE_CLIENT_CERTIFICATE,
+ STATE_SERVER_CERTIFICATE,
+ STATE_CERTIFICATE_VERIFY,
+ STATE_FINISHED : con iota;
+
+#
+# load necessary modules
+#
+init(): string
+{
+ sys = load Sys Sys->PATH;
+ if(sys == nil)
+ return "ssl3: load sys module failed";
+ logfd = sys->fildes(1);
+
+ keyring = load Keyring Keyring->PATH;
+ if(keyring == nil)
+ return "ssl3: load keyring module failed";
+
+ random = load Random Random->PATH;
+ if(random == nil)
+ return "ssl3: load random module failed";
+
+ daytime = load Daytime Daytime->PATH;
+ if(daytime == nil)
+ return "ssl3: load Daytime module failed";
+
+ pkcs = load PKCS PKCS->PATH;
+ if(pkcs == nil)
+ return "ssl3: load pkcs module failed";
+ pkcs->init();
+
+ x509 = load X509 X509->PATH;
+ if(x509 == nil)
+ return "ssl3: load x509 module failed";
+ x509->init();
+
+ ssl = load SSL SSL->PATH;
+ if(ssl == nil)
+ return "ssl3: load SSL module failed";
+ sslsession = load SSLsession SSLsession->PATH;
+ if(sslsession == nil)
+ return "ssl3: load sslsession module failed";
+ e := sslsession->init();
+ if(e != nil)
+ return "ssl3: sslsession init failed: "+e;
+
+ return "";
+}
+
+log(s: string)
+{
+ a := array of byte (s + "\n");
+ sys->write(logfd, a, len a);
+}
+
+#
+# protocol context
+#
+
+Context.new(): ref Context
+{
+ ctx := ref Context;
+
+ ctx.c = nil;
+ ctx.session = nil;
+ ctx.local_info = nil;
+
+ ctx.sha_state = nil;
+ ctx.md5_state = nil;
+
+ ctx.status = 0;
+ ctx.state = 0;
+
+ ctx.client_random = array [32] of byte;
+ ctx.server_random = array [32] of byte;
+
+ ctx.cw_mac = nil;
+ ctx.sw_mac = nil;
+ ctx.cw_key = nil;
+ ctx.sw_key = nil;
+ ctx.cw_IV = nil;
+ ctx.sw_IV = nil;
+
+ ctx.in_queue = RecordQueue.new();
+ ctx.in_queue.data = ref Record(0, nil, array [1<<15] of byte) :: nil;
+ ctx.out_queue = RecordQueue.new();
+
+ # set session resumable as default
+ ctx.status |= SESSION_RESUMABLE;
+
+ return ctx;
+}
+
+Context.client(ctx: self ref Context, fd: ref Sys->FD, peername: string, ver: int, info: ref Authinfo)
+ : (string, int)
+{
+ if(SSL_DEBUG)
+ log(sys->sprint("ssl3: Context.Client peername=%s ver=%d\n", peername, ver));
+ if ((ckstr := cksuites(info.suites)) != nil)
+ return (ckstr, ver);
+ # the order is important
+ ctx.local_info = info;
+ ctx.state = STATE_HELLO_REQUEST;
+ e := ctx.connect(fd);
+ if(e != "")
+ return (e, ver);
+ ctx.session = sslsession->get_session_byname(peername);
+
+ # Request to resume an SSL 3.0 session should use an SSL 3.0 client hello
+ if(ctx.session.session_id != nil) {
+ if((ctx.session.version[0] == SSL_VERSION_3_0[0]) &&
+ (ctx.session.version[1] == SSL_VERSION_3_0[1])) {
+ ver = 3;
+ ctx.status |= SSL3_HANDSHAKE;
+ ctx.status &= ~SSL2_HANDSHAKE;
+ }
+ }
+ e = ctx.set_version(ver);
+ if(e != "")
+ return (e, ver);
+ reset_client_random(ctx);
+ ctx.status |= CLIENT_SIDE;
+ e = do_protocol(ctx);
+ if(e != nil)
+ return (e, ver);
+
+ if(ctx.status & SSL3_RECORD)
+ ver = 3;
+ else
+ ver = 2;
+ return (nil, ver);
+}
+
+Context.server(ctx: self ref Context, fd: ref Sys->FD, info: ref Authinfo, client_auth: int)
+ : string
+{
+ if ((ckstr := cksuites(info.suites)) != nil)
+ return ckstr;
+ ctx.local_info = info;
+ if(client_auth)
+ ctx.status |= CLIENT_AUTH;
+ ctx.state = STATE_CLIENT_HELLO;
+ e := ctx.connect(fd);
+ if(e != "")
+ return e;
+ reset_server_random(ctx);
+ e = ctx.set_version(3); # set ssl device to version 3
+ if(e != "")
+ return e;
+ ctx.status &= ~CLIENT_SIDE;
+ e = do_protocol(ctx);
+ if(e != nil)
+ return e;
+
+ return "";
+}
+
+
+Context.use_devssl(ctx: self ref Context)
+{
+ if(!(ctx.status & IN_READY) && !(ctx.status & OUT_READY))
+ ctx.status |= USE_DEVSSL;
+}
+
+Context.set_version(ctx: self ref Context, vers: int): string
+{
+ err := "";
+
+ if(ctx.c == nil) {
+ err = "no connection provided";
+ }
+ else {
+ if(SSL_DEBUG)
+ log("ssl3: record version = " + string vers);
+
+ if(vers == 2) {
+ ctx.status &= ~SSL3_RECORD;
+ ctx.status &= ~SSL3_HANDSHAKE;
+ ctx.status |= SSL2_HANDSHAKE;
+ if (ctx.session != nil)
+ ctx.session.version = SSL_VERSION_2_0;
+ }
+ else if(vers == 3) { # may be sslv2 handshake using ssl3 record
+ ctx.status |= SSL3_RECORD;
+ ctx.status |= SSL3_HANDSHAKE;
+ ctx.status &= ~SSL2_HANDSHAKE; # tmp test only
+ if (ctx.session != nil)
+ ctx.session.version = SSL_VERSION_3_0;
+ }
+ else if(vers == 23) { # may be sslv2 handshake using ssl3 record
+ ctx.status &= ~SSL3_RECORD;
+ ctx.status |= SSL3_HANDSHAKE;
+ ctx.status |= SSL2_HANDSHAKE;
+ vers = 2;
+ }
+ else
+ err = "unsupported ssl device version";
+
+ if((err == "") && (ctx.status & USE_DEVSSL)) {
+ if(sys->fprint(ctx.c.cfd, "ver %d", vers) < 0)
+ err = sys->sprint("ssl3: set ssl device version failed: %r");
+ }
+ }
+
+ return err;
+}
+
+Context.connect(ctx: self ref Context, fd: ref Sys->FD): string
+{
+ err := "";
+
+ if(ctx.status & USE_DEVSSL)
+ (err, ctx.c) = ssl->connect(fd);
+ else {
+ ctx.c = ref Sys->Connection(fd, nil, "");
+ ctx.in_queue.sequence_numbers[0] = 0;
+ ctx.out_queue.sequence_numbers[1] = 0;
+ }
+
+ return err;
+}
+
+Context.read(ctx: self ref Context, a: array of byte, n: int): int
+{
+ if(ctx.state != STATE_EXIT || !(ctx.status & IN_READY)) {
+ if(SSL_DEBUG)
+ log("ssl3: read not ready\n");
+ return -1;
+ }
+
+ if(ctx.out_queue.data != nil)
+ record_write_queue(ctx);
+
+ if(ctx.status & USE_DEVSSL) {
+ fd := ctx.c.dfd;
+ if(ctx.status & SSL3_RECORD) {
+ buf := array [n+3] of byte;
+ m := sys->read(fd, buf, n+3); # header + n bytes
+ if(m < 3) {
+ if(SSL_DEBUG)
+ log(sys->sprint("ssl3: read failure: %r"));
+ return -1;
+ }
+
+ if(buf[1] != SSL_VERSION_3_0[0] || buf[2] != SSL_VERSION_3_0[1]) {
+ if(SSL_DEBUG)
+ log("ssl3: not ssl version 3 data: header = [" + bastr(buf[0:3]) + "]");
+ return -1;
+ }
+
+ a[0:] = buf[3:m];
+
+ content_type := int buf[0];
+ case content_type {
+ SSL_APPLICATION_DATA =>
+ break;
+ SSL_ALERT =>
+ if(SSL_DEBUG)
+ log("ssl3: expect application data, got alert: [" + bastr(buf[3:m]) +"]");
+ return 0;
+ SSL_HANDSHAKE =>
+ if(SSL_DEBUG)
+ log("ssl3: expect application data, got handshake message");
+ return 0;
+ SSL_CHANGE_CIPHER_SPEC =>
+ if(SSL_DEBUG)
+ log("ssl3: dynamic change cipher spec not supported yet");
+ return 0;
+ }
+ return m-3;
+ }
+ else
+ return sys->read(fd, a, n);
+ }
+ else {
+ q := ctx.in_queue;
+ got := 0;
+ if(q.fragment) {
+ d := (hd q.data).data;
+ m := q.e - q.b;
+ i := q.e - q.fragment;
+ if(q.fragment > n) {
+ a[0:] = d[i:i+n];
+ q.fragment -= n;
+ got = n;
+ }
+ else {
+ a[0:] = d[i:q.e];
+ got = q.fragment;
+ q.fragment = 0;
+ }
+ }
+out:
+ while(got < n) {
+ err := q.read(ctx, ctx.c.dfd);
+ if(err != "") {
+ if(SSL_DEBUG)
+ log("ssl3: read: " + err);
+ break;
+ }
+ r := hd q.data;
+ if(ctx.status & SSL3_RECORD) {
+ case r.content_type {
+ SSL_APPLICATION_DATA =>
+ break;
+ SSL_ALERT =>
+ if(SSL_DEBUG)
+ log("ssl3: read: got alert\n\t\t" + bastr(r.data[q.b:q.e]));
+ # delete session id
+ ctx.session.session_id = nil;
+ ctx.status &= ~IN_READY;
+ break out;
+ SSL_CHANGE_CIPHER_SPEC =>
+ if(SSL_DEBUG)
+ log("ssl3: read: got change cipher spec\n");
+ SSL_HANDSHAKE =>
+ if(SSL_DEBUG)
+ log("ssl3: read: got handshake data\n");
+ #do_handshake(ctx, r); # ?
+ * =>
+ if(SSL_DEBUG)
+ log("ssl3: read: unknown data\n");
+ }
+ }
+
+ if((n - got) <= (q.e - q.b)) {
+ a[got:] = r.data[q.b:q.b+n-got];
+ q.fragment = q.e - q.b - n + got;
+ got = n;
+ }
+ else {
+ a[got:] = r.data[q.b:q.e];
+ q.fragment = 0;
+ got += q.e - q.b;
+ }
+ }
+ if(SSL_DEBUG)
+ log(sys->sprint("ssl3: read: returning %d bytes\n", got));
+ return got;
+ }
+}
+
+Context.write(ctx: self ref Context, a: array of byte, n: int): int
+{
+ if(ctx.state != STATE_EXIT || !(ctx.status & OUT_READY))
+ return-1;
+
+ if(ctx.out_queue.data != nil)
+ record_write_queue(ctx);
+
+ if(ctx.status & USE_DEVSSL) {
+ if(ctx.status & SSL3_RECORD) {
+ buf := array [n+3] of byte;
+ buf[0] = byte SSL_APPLICATION_DATA;
+ buf[1:] = SSL_VERSION_3_0;
+ buf[3:] = a[0:n];
+ n = sys->write(ctx.c.dfd, buf, n+3);
+ if(n > 0)
+ n -= 3;
+ }
+ else
+ n = sys->write(ctx.c.dfd, a, n);
+ }
+ else {
+ q := ctx.out_queue;
+ v := SSL_VERSION_2_0;
+ if(ctx.status&SSL3_RECORD)
+ v = SSL_VERSION_3_0;
+ for(i := 0; i < n;){
+ m := n-i;
+ if(m > q.length)
+ m = q.length;
+ r := ref Record(SSL_APPLICATION_DATA, v, a[i:i+m]);
+ record_write(r, ctx); # return error?
+ i += m;
+ }
+ }
+ return n;
+}
+
+devssl_read(ctx: ref Context): (ref Record, string)
+{
+ buf := array [Sys->ATOMICIO] of byte;
+ r: ref Record;
+ c := ctx.c;
+
+ n := sys->read(c.dfd, buf, 3);
+ if(n < 0)
+ return (nil, sys->sprint("record read: read failure: %r"));
+
+ # in case of undetermined, do auto record version detection
+ if((ctx.state == SSL2_STATE_SERVER_HELLO) &&
+ (ctx.status & SSL2_HANDSHAKE) && (ctx.status & SSL3_HANDSHAKE)) {
+
+ fstatus := sys->open(ctx.c.dir + "/status", Sys->OREAD);
+ if(fstatus == nil)
+ return (nil, "open status: " + sys->sprint("%r"));
+ status := array [64] of byte;
+ nbyte := sys->read(fstatus, status, len status);
+ if(nbyte != 1)
+ return (nil, "read status: " + sys->sprint("%r"));
+
+ ver := int status[0];
+
+ if(SSL_DEBUG)
+ log("ssl3: auto record version detect as: " + string ver);
+
+ # assert ctx.status & SSL2_RECORD true ? before reset
+ if(ver == 2) {
+ ctx.status &= ~SSL3_RECORD;
+ ctx.status |= SSL2_HANDSHAKE;
+ ctx.status &= ~SSL3_HANDSHAKE;
+ }
+ else {
+ ctx.status |= SSL3_RECORD;
+ }
+ }
+
+ if(ctx.status & SSL3_RECORD) {
+ if(n < 3)
+ return (nil, sys->sprint("record read: read failure: %r"));
+
+ # assert only major version number
+ if(buf[1] != SSL_VERSION_3_0[0])
+ return (nil, "record read: version mismatch");
+
+ case int buf[0] {
+ SSL_ALERT =>
+ n = sys->read(c.dfd, buf, 5); # read in header plus rest
+ if(n != 5)
+ return (nil, sys->sprint("read alert failed: %r"));
+ r = ref Record(SSL_ALERT, SSL_VERSION_3_0, buf[3:5]);
+
+ SSL_CHANGE_CIPHER_SPEC =>
+ n = sys->read(c.dfd, buf, 4); # read in header plus rest
+ if(n != 4)
+ return (nil, sys->sprint("read change_cipher_spec failed: %r"));
+ r = ref Record(SSL_CHANGE_CIPHER_SPEC, SSL_VERSION_3_0, buf[3:4]);
+
+ SSL_HANDSHAKE =>
+ n = sys->read(c.dfd, buf, 7); # header + msg length
+ if(n != 7)
+ return (nil, sys->sprint("read handshake header + msg length failed: %r"));
+ m := int_decode(buf[4:7]);
+ if(m < 0)
+ return (nil, "read handshake failed: unexpected length");
+ data := array [m+4] of byte;
+ data[0:] = buf[3:7]; # msg type + length
+ if(m != 0) {
+ # need exact m bytes (exclude header), otherwise failure
+ remain := m;
+ now := 4;
+ while(remain > 0) {
+ n = sys->read(c.dfd, buf, remain+3); # header + msg
+ if(n < 3 || int buf[0] != SSL_HANDSHAKE)
+ return (nil, sys->sprint("read handshake msg body failed: %r"));
+ sys->print("expect %d, got %d bytes\n", m, n-3);
+ remain -= n - 3;
+ data[now:] = buf[3:n];
+ now += n - 3;
+ }
+ }
+
+ r = ref Record(SSL_HANDSHAKE, SSL_VERSION_3_0, data);
+ * =>
+ return (nil, "trying to read unknown protocol message");
+ }
+
+ if(SSL_DEBUG)
+ log("ssl3: record_read: \n\theader = \n\t\t" + bastr(buf[0:3])
+ + "\n\tdata = \n\t\t" + bastr(r.data) + "\n");
+ }
+ # v2 record layer
+ else {
+ # assume the handshake record size less than Sys->ATOMICIO
+ # in most case, this is ok
+ if(n == 3) {
+ n = sys->read(c.dfd, buf[3:], Sys->ATOMICIO - 3);
+ if(n < 0)
+ return (nil, sys->sprint("v2 record read: read failure: %r"));
+ }
+
+ r = ref Record(SSL_V2HANDSHAKE, SSL_VERSION_2_0, buf[0:n+3]);
+
+ if(SSL_DEBUG)
+ log("ssl3: v2 record_read: \n\tdata = \n\t\t" + bastr(r.data) + "\n");
+ }
+
+ return (r, nil);
+}
+
+record_read(ctx: ref Context): (ref Record, string)
+{
+ q := ctx.in_queue;
+ if(q.fragment == 0) {
+ err := q.read(ctx, ctx.c.dfd);
+ if(err != "")
+ return (nil, err);
+ q.fragment = q.e - q.b;
+ }
+
+ r := hd q.data;
+ if(ctx.status & SSL3_RECORD) {
+ # confirm only major version number
+ if(r.version[0] != SSL_VERSION_3_0[0])
+ return (nil, "record read: not v3 record");
+
+ case r.content_type {
+ SSL_ALERT =>
+ a := array [2] of byte;
+ n := fetch_data(ctx, a, 2);
+ if(n != 2)
+ return (nil, "read alert failed");
+ r = ref Record(SSL_ALERT, SSL_VERSION_3_0, a);
+
+ SSL_CHANGE_CIPHER_SPEC =>
+ a := array [1] of byte;
+ n := fetch_data(ctx, a, 1);
+ if(n != 1)
+ return (nil, "read change_cipher_spec failed");
+ r = ref Record(SSL_CHANGE_CIPHER_SPEC, SSL_VERSION_3_0, a);
+
+ SSL_HANDSHAKE =>
+ a := array [4] of byte;
+ n := fetch_data(ctx, a, 4);
+ if(n != 4)
+ return (nil, "read message length failed");
+ m := int_decode(a[1:]);
+ if(m < 0)
+ return (nil, "unexpected handshake message length");
+ b := array [m+4] of byte;
+ b[0:] = a;
+ n = fetch_data(ctx, b[4:], m);
+ if(n != m)
+ return (nil, "read message body failed");
+ r = ref Record(SSL_HANDSHAKE, SSL_VERSION_3_0, b);
+ * =>
+ return (nil, "trying to read unknown protocol message");
+ }
+ }
+ # v2 record layer
+ else {
+ r = ref Record(SSL_V2HANDSHAKE, SSL_VERSION_2_0, r.data[q.b:q.e]);
+ q.fragment = 0;
+ }
+
+ return (r, nil);
+}
+
+fetch_data(ctx: ref Context, a: array of byte, n: int): int
+{
+ q := ctx.in_queue;
+ r := hd q.data;
+
+ got := 0;
+ cnt := -1;
+out:
+ while(got < n) {
+ if(q.fragment) {
+ cnt = r.content_type;
+ i := q.e - q.fragment;
+ if(n-got <= q.fragment) {
+ a[got:] = r.data[i:i+n-got];
+ q.fragment -= n - got;
+ got = n;
+ }
+ else {
+ a[got:] = r.data[i:q.e];
+ got += q.fragment;
+ q.fragment = 0;
+ }
+ }
+ else {
+ err := q.read(ctx, ctx.c.dfd);
+ if(err != "")
+ break out;
+ if(cnt == -1)
+ cnt = r.content_type;
+ if(ctx.status & SSL3_RECORD) {
+ case r.content_type {
+ SSL_APPLICATION_DATA =>
+ break;
+ * =>
+ if(cnt != r.content_type)
+ break out;
+ }
+ }
+ else {
+ r.content_type = SSL_V2HANDSHAKE;
+ }
+ }
+ }
+ return got;
+}
+
+record_write(r: ref Record, ctx: ref Context)
+{
+ if(ctx.status & USE_DEVSSL) {
+ buf: array of byte;
+ n: int;
+ c := ctx.c;
+
+ if(ctx.status & SSL3_RECORD) {
+ buf = array [3 + len r.data] of byte;
+ buf[0] = byte r.content_type;
+ buf[1:] = r.version;
+ buf[3:] = r.data;
+ n = sys->write(c.dfd, buf, len buf);
+ if(n < 0 || n != len buf) {
+ if(SSL_DEBUG)
+ log(sys->sprint("ssl3: v3 record write error: %d %r", n));
+ return; # don't terminated until alerts being read
+ }
+ }
+ else {
+ buf = r.data;
+ n = sys->write(c.dfd, buf, len buf);
+ if(n < 0 || n != len buf) {
+ if(SSL_DEBUG)
+ log(sys->sprint("ssl3: v2 record write error: %d %r", n));
+ return; # don't terminated until alerts being read
+ }
+ }
+ }
+ else
+ ctx.out_queue.write(ctx, ctx.c.dfd, r);
+
+ # if(SSL_DEBUG)
+ # log("ssl3: record_write: \n\t\t" + bastr(buf) + "\n");
+}
+
+RecordQueue.new(): ref RecordQueue
+{
+ q := ref RecordQueue(
+ ref MacState.null(0),
+ ref CipherState.null(1),
+ 1 << 15,
+ array [2] of { * => 0},
+ nil,
+ 0,
+ 0, # b
+ 0 # e
+ );
+ return q;
+}
+
+RecordQueue.read(q: self ref RecordQueue, ctx: ref Context, fd: ref Sys->FD): string
+{
+ r := hd q.data;
+ a := r.data;
+ if(ensure(fd, a, 2) < 0)
+ return "no more data";
+ # auto record version detection
+ m, h, pad: int = 0;
+ if(int a[0] < 20 || int a[0] > 23) {
+ ctx.status &= ~SSL3_RECORD;
+ if(int a[0] & 16r80) {
+ h = 2;
+ m = ((int a[0] & 16r7f) << 8) | int a[1];
+ pad = 0;
+ } else {
+ h = 3;
+ m = ((int a[0] & 16r3f) << 8) | int a[1];
+ if(ensure(fd, a[2:], 1) < 0)
+ return "bad v2 record";
+ pad = int a[2];
+ if(pad > m)
+ return "bad v2 pad";
+ }
+ r.content_type = SSL_V2HANDSHAKE;
+ r.version = SSL_VERSION_2_0;
+ }
+ else {
+ ctx.status |= SSL3_RECORD;
+ h = 5;
+ if(ensure(fd, a[2:], 3) < 0)
+ return "bad v3 record";
+ m = ((int a[3]) << 8) | int a[4];
+ r.content_type = int a[0];
+ r.version = a[1:3];
+ }
+ if(ensure(fd, a[h:], m) < 0)
+# return "data too short";
+ return sys->sprint("data too short wanted %d", m);
+ if(SSL_DEBUG) {
+ log("ssl3: record read\n\tbefore decrypt\n\t\t" + bastr(a[0:m+h]));
+ log(sys->sprint("SSL3=%d\n", ctx.status & SSL3_RECORD));
+ }
+
+ # decrypt (data, pad, mac)
+ pick dec := q.cipherState {
+ null =>
+ rc4 =>
+ keyring->rc4(dec.es, a[h:], m);
+ if (SSL_DEBUG) log("rc4 1");
+ descbc =>
+ keyring->descbc(dec.es, a[h:], m, 1);
+ if (SSL_DEBUG) log("descbc 1");
+ ideacbc =>
+ keyring->ideacbc(dec.es, a[h:], m, 1);
+ if (SSL_DEBUG) log("ideacbc 1");
+ * =>
+ }
+
+ if(SSL_DEBUG)
+ log("ssl3: record read\n\tafter decrypt\n\t\t" + bastr(a[0:m]));
+
+ idata, imac, ipad: int = 0;
+ if(ctx.status & SSL3_RECORD) {
+ if(q.cipherState.block_size > 1){
+ pad = int a[h + m - 1];
+ if(pad >= q.cipherState.block_size)
+ return "bad v3 pad";
+ # pad++;
+ ipad = h+m-pad-1;
+ }
+ else
+ ipad = h+m-pad;
+ imac = ipad - q.macState.hash_size;
+ idata = h;
+ }
+ else {
+ imac = h;
+ idata = imac + q.macState.hash_size;
+ ipad = h + m - pad;
+ }
+ if(tagof q.macState != tagof MacState.null) {
+ if (ctx.status & SSL3_RECORD)
+ mac := q.calcmac(ctx, r.content_type, a, idata, imac-idata);
+ else
+ mac = q.calcmac(ctx, r.content_type, a, idata, ipad+pad-idata);
+ if(bytes_cmp(mac, a[imac:imac+len mac]) < 0)
+ return "bad mac";
+ }
+ q.b = idata;
+ if (ctx.status & SSL3_RECORD)
+ q.e = imac;
+ else
+ q.e = ipad;
+ q.fragment = q.e - q.b;
+
+ if((++q.sequence_numbers[0] == 0) && (ctx.status&SSL3_RECORD))
+ ++q.sequence_numbers[1];
+
+ return "";
+}
+
+ensure(fd: ref Sys->FD, a: array of byte, n: int): int
+{
+ i, m: int = 0;
+ while(i < n) {
+ m = sys->read(fd, a[i:], n - i);
+ if(m <= 0) {
+ return -1;
+ }
+ i += m;
+ }
+ return n;
+}
+
+RecordQueue.write(q: self ref RecordQueue, ctx: ref Context, fd: ref Sys->FD,
+ r: ref Record): string
+{
+ m := len r.data;
+ h, pad: int = 0;
+ if(ctx.status & SSL3_RECORD) {
+ h = 5;
+ if(q.cipherState.block_size > 1) {
+ pad = (m+q.macState.hash_size+1)%q.cipherState.block_size;
+ if (pad)
+ pad = q.cipherState.block_size - pad;
+ }
+ }
+ else {
+ h = 2;
+ if(q.cipherState.block_size > 1) {
+ pad = m%q.cipherState.block_size;
+ if(pad) {
+ pad = q.cipherState.block_size - pad;
+ h++;
+ }
+ }
+ }
+
+ m += pad + q.macState.hash_size;
+ if ((ctx.status & SSL3_RECORD) && q.cipherState.block_size > 1)
+ m++;
+ a := array [h+m] of byte;
+
+ idata, imac, ipad: int = 0;
+ if(ctx.status & SSL3_RECORD) {
+ a[0] = byte r.content_type;
+ a[1:] = r.version;
+ a[3] = byte (m >> 8); #CJL - netscape ssl3 traces do not show top bit set
+# a[3] = byte ((m >> 8) | 16r80); #CJL
+# a[3] = byte (m | 16r8000) >> 8;
+ a[4] = byte m;
+ idata = h;
+ imac = idata + len r.data;
+ ipad = imac + q.macState.hash_size;
+ if (q.cipherState.block_size > 1)
+ a[h+m-1] = byte pad;
+ }
+ else {
+ if(pad) {
+ a[0] = byte m >> 8;
+ a[2] = byte pad;
+ }
+ else
+ a[0] = byte ((m >> 8) | 16r80);
+ a[1] = byte m;
+ imac = h;
+ idata = imac + q.macState.hash_size;
+ ipad = idata + len r.data;
+ }
+ a[idata:] = r.data;
+ if(pad)
+ a[ipad:] = array [pad] of { * => byte (pad-1)};
+
+ if(tagof q.macState != tagof MacState.null) {
+ if (ctx.status & SSL3_RECORD)
+ a[imac:] = q.calcmac(ctx, r.content_type, a, idata, len r.data);
+ else
+ a[imac:] = q.calcmac(ctx, r.content_type, a, idata, ipad+pad-idata);
+ }
+
+ if(SSL_DEBUG) {
+ log("ssl3: record write\n\tbefore encrypt\n\t\t" + bastr(a));
+ log(sys->sprint("SSL3=%d\n", ctx.status & SSL3_RECORD));
+ }
+
+ # encrypt (data, pad, mac)
+ pick enc := q.cipherState {
+ null =>
+ rc4 =>
+ keyring->rc4(enc.es, a[h:], m);
+ if (SSL_DEBUG) log("rc4 0");
+ descbc =>
+ keyring->descbc(enc.es, a[h:], m, 0);
+ if (SSL_DEBUG) log(sys->sprint("descbc 0 %d", m));
+ ideacbc =>
+ keyring->ideacbc(enc.es, a[h:], m, 0);
+ if (SSL_DEBUG) log(sys->sprint("ideacbc 0 %d", m));
+ * =>
+ }
+
+ if(SSL_DEBUG)
+ log("ssl3: record write\n\tafter encrypt\n\t\t" + bastr(a));
+
+ if(sys->write(fd, a, h+m) < 0)
+ return sys->sprint("ssl3: record write: %r");
+
+ if((++q.sequence_numbers[0] == 0) && (ctx.status&SSL3_RECORD))
+ ++q.sequence_numbers[1];
+
+ return "";
+}
+
+RecordQueue.calcmac(q: self ref RecordQueue, ctx: ref Context, cntype: int, a: array of byte,
+ ofs, n: int) : array of byte
+{
+ digest, b: array of byte;
+
+ if(ctx.status & SSL3_RECORD) {
+ b = array [11] of byte;
+ i := putn(b, 0, q.sequence_numbers[1], 4);
+ i = putn(b, i, q.sequence_numbers[0], 4);
+ b[i++] = byte cntype;
+ putn(b, i, n, 2);
+ }
+ else {
+ b = array [4] of byte;
+ putn(b, 0, q.sequence_numbers[0], 4);
+ }
+
+ # if(SSL_DEBUG)
+ # log("ssl3: record mac\n\tother =\n\t\t" + bastr(b));
+
+ pick ms := q.macState {
+ md5 =>
+ digest = array [Keyring->MD5dlen] of byte;
+ ds0 := ms.ds[0].copy();
+ if(ctx.status & SSL3_RECORD) {
+ keyring->md5(b, len b, nil, ds0);
+ keyring->md5(a[ofs:], n, digest, ds0);
+ ds1 := ms.ds[1].copy();
+ keyring->md5(digest, len digest, digest, ds1);
+ }
+ else {
+ keyring->md5(a[ofs:], n, nil, ds0);
+ keyring->md5(b, len b, digest, ds0);
+ }
+ sha =>
+ digest = array [Keyring->SHA1dlen] of byte;
+ ds0 := ms.ds[0].copy();
+ if(ctx.status & SSL3_RECORD) {
+ keyring->sha1(b, len b, nil, ds0);
+ keyring->sha1(a[ofs:], n, digest, ds0);
+ ds1 := ms.ds[1].copy();
+ keyring->sha1(digest, len digest, digest, ds1);
+ }
+ else {
+ keyring->sha1(a[ofs:], n, nil, ds0);
+ keyring->sha1(b, len b, digest, ds0);
+ }
+ }
+ return digest;
+}
+
+set_queues(ctx: ref Context): string
+{
+ sw: array of byte;
+ if(ctx.sw_key != nil) {
+ sw = array [len ctx.sw_key + len ctx.sw_IV] of byte;
+ sw[0:] = ctx.sw_key;
+ sw[len ctx.sw_key:] = ctx.sw_IV;
+ }
+ cw: array of byte;
+ if(ctx.cw_key != nil) {
+ cw = array [len ctx.cw_key + len ctx.cw_IV] of byte;
+ cw[0:] = ctx.cw_key;
+ cw[len ctx.cw_key:] = ctx.cw_IV;
+ }
+
+ err := "";
+ if(ctx.status & USE_DEVSSL) {
+ err = set_secrets(ctx.c, ctx.sw_mac, ctx.cw_mac, sw, cw);
+ if(err == "")
+ err = set_cipher_algs(ctx);
+ }
+ else {
+ err = set_out_queue(ctx);
+ if(err == "")
+ err = set_in_queue(ctx);
+ }
+
+ return err;
+}
+
+set_in_queue(ctx: ref Context): string
+{
+ sw: array of byte;
+ if(ctx.sw_key != nil) {
+ sw = array [len ctx.sw_key + len ctx.sw_IV] of byte;
+ sw[0:] = ctx.sw_key;
+ sw[len ctx.sw_key:] = ctx.sw_IV;
+ }
+
+ err := "";
+ if(ctx.status & USE_DEVSSL) {
+ err = set_secrets(ctx.c, ctx.sw_mac, nil, sw, nil);
+ if(err == "")
+ err = set_cipher_algs(ctx);
+ }
+ else
+ err = set_queue(ctx, ctx.in_queue, ctx.sw_mac, sw);
+
+ return err;
+}
+
+set_out_queue(ctx: ref Context): string
+{
+ cw: array of byte;
+ if(ctx.cw_key != nil) {
+ cw = array [len ctx.cw_key + len ctx.cw_IV] of byte;
+ cw[0:] = ctx.cw_key;
+ cw[len ctx.cw_key:] = ctx.cw_IV;
+ }
+
+ err := "";
+ if(ctx.status & USE_DEVSSL) {
+ err = set_secrets(ctx.c, nil, ctx.cw_mac, nil, cw);
+ if(err == "")
+ err = set_cipher_algs(ctx);
+ }
+ else
+ err = set_queue(ctx, ctx.out_queue, ctx.cw_mac, cw);
+
+ return err;
+}
+
+set_queue(ctx: ref Context, q: ref RecordQueue, mac, key: array of byte): string
+{
+ e := "";
+
+ case ctx.sel_ciph.mac_algorithm {
+ SSL_NULL_MAC =>
+ q.macState = ref MacState.null(0);
+ SSL_MD5 =>
+ ds: array of ref DigestState;
+ if(ctx.status & SSL3_RECORD) {
+ ds = array [2] of ref DigestState;
+ ds[0] = keyring->md5(mac, len mac, nil, nil);
+ ds[1] = keyring->md5(mac, len mac, nil, nil);
+ ds[0] = keyring->md5(SSL_MAC_PAD1, 48, nil, ds[0]);
+ ds[1] = keyring->md5(SSL_MAC_PAD2, 48, nil, ds[1]);
+ }
+ else {
+ ds = array [1] of ref DigestState;
+ ds[0] = keyring->md5(mac, len mac, nil, nil);
+ }
+ q.macState = ref MacState.md5(Keyring->MD5dlen, ds);
+ SSL_SHA =>
+ ds: array of ref DigestState;
+ if(ctx.status & SSL3_RECORD) {
+ ds = array [2] of ref DigestState;
+ ds[0] = keyring->sha1(mac, len mac, nil, nil);
+ ds[1] = keyring->sha1(mac, len mac, nil, nil);
+ ds[0] = keyring->sha1(SSL_MAC_PAD1, 40, nil, ds[0]);
+ ds[1] = keyring->sha1(SSL_MAC_PAD2, 40, nil, ds[1]);
+ }
+ else {
+ ds = array [1] of ref DigestState;
+ ds[0] = keyring->sha1(mac, len mac, nil, nil);
+ }
+ q.macState = ref MacState.sha(Keyring->SHA1dlen, ds);
+ * =>
+ e = "ssl3: digest method: unknown";
+ }
+
+ case ctx.sel_ciph.bulk_cipher_algorithm {
+ SSL_NULL_CIPHER =>
+ q.cipherState = ref CipherState.null(1);
+ SSL_RC4 =>
+ if (SSL_DEBUG) log("rc4setup");
+ rcs := keyring->rc4setup(key);
+ q.cipherState = ref CipherState.rc4(1, rcs);
+ SSL_DES_CBC =>
+ dcs : ref keyring->DESstate;
+
+ if (SSL_DEBUG) log(sys->sprint("dessetup %d", len key));
+ if (len key >= 16)
+ dcs = keyring->dessetup(key[0:8], key[8:16]);
+ else if (len key >= 8)
+ dcs = keyring->dessetup(key[0:8], nil);
+ else
+ e = "ssl3: bad DES key length";
+ q.cipherState = ref CipherState.descbc(8, dcs);
+ SSL_IDEA_CBC =>
+ ics : ref keyring->IDEAstate;
+
+ if (SSL_DEBUG) log(sys->sprint("ideasetup %d", len key));
+ if (len key >= 24)
+ ics = keyring->ideasetup(key[0:16], key[16:24]);
+ else if (len key >= 16)
+ ics = keyring->ideasetup(key[0:16], nil);
+ else
+ e = "ssl3: bad IDEA key length";
+ q.cipherState = ref CipherState.ideacbc(8, ics);
+ SSL_RC2_CBC or
+ SSL_3DES_EDE_CBC or
+ SSL_FORTEZZA_CBC =>
+ e = "ssl3: unsupported cipher";
+ * =>
+ e = "ssl3: unknown cipher";
+ }
+
+ if(ctx.status & SSL3_RECORD) {
+ q.length = 1 << 14;
+ if(tagof q.macState != tagof MacState.null)
+ q.length += 2048;
+ }
+ else {
+ if(q.cipherState.block_size > 1) {
+ q.length = (1<<14) - q.macState.hash_size - 1;
+ q.length -= q.length % q.cipherState.block_size;
+ }
+ else
+ q.length = (1<<15) - q.macState.hash_size - 1;
+ }
+ if(ctx.status & SSL3_RECORD)
+ q.sequence_numbers[0] = q.sequence_numbers[1] = 0;
+
+ return e;
+}
+
+set_cipher_algs(ctx: ref Context) : string
+{
+ e: string;
+
+ algspec := "alg";
+
+ case enc := ctx.sel_ciph.bulk_cipher_algorithm {
+ SSL_NULL_CIPHER =>
+ algspec += " clear";
+ SSL_RC4 => # stream cipher
+ algspec += " rc4_128";
+ SSL_DES_CBC => # block cipher
+ algspec += " descbc";
+ SSL_IDEA_CBC => # block cipher
+ algspec += " ideacbc";
+ SSL_RC2_CBC or
+ SSL_3DES_EDE_CBC or
+ SSL_FORTEZZA_CBC =>
+ e = "ssl3: encrypt method: unsupported";
+ * =>
+ e = "ssl3: encrypt method: unknown";
+ }
+
+ case mac := ctx.sel_ciph.mac_algorithm {
+ SSL_NULL_MAC =>
+ algspec += " clear";
+ SSL_MD5 =>
+ algspec += " md5";
+ SSL_SHA =>
+ algspec += " sha1";
+ * =>
+ e = "ssl3: digest method: unknown";
+ }
+
+ e = set_ctl(ctx.c, algspec);
+ if(e != "") {
+ if(SSL_DEBUG)
+ log("failed to set cipher algs: " + e);
+ }
+
+ return e;
+}
+
+set_ctl(c: ref Sys->Connection, s: string): string
+{
+ a := array of byte s;
+ if(sys->write(c.cfd, a, len a) < 0)
+ return sys->sprint("error writing sslctl: %r");
+
+ if(SSL_DEBUG)
+ log("ssl3: set cipher algorithm:\n\t\t" + s + "\n");
+
+ return "";
+}
+
+set_secrets(c: ref Sys->Connection, min, mout, sin, sout: array of byte) : string
+{
+ fmin := sys->open(c.dir + "/macin", Sys->OWRITE);
+ fmout := sys->open(c.dir + "/macout", Sys->OWRITE);
+ fsin := sys->open(c.dir + "/secretin", Sys->OWRITE);
+ fsout := sys->open(c.dir + "/secretout", Sys->OWRITE);
+ if(fmin == nil || fmout == nil || fsin == nil || fsout == nil)
+ return sys->sprint("can't open ssl secret files: %r\n");
+
+ if(sin != nil) {
+ if(SSL_DEBUG)
+ log("ssl3: set encryption secret and IV\n\tsecretin:\n\t\t" + bastr(sin) + "\n");
+ if(sys->write(fsin, sin, len sin) < 0)
+ return sys->sprint("error writing secretin: %r");
+ }
+ if(sout != nil) {
+ if(SSL_DEBUG)
+ log("ssl3: set encryption secret and IV\n\tsecretout:\n\t\t" + bastr(sout) + "\n");
+ if(sys->write(fsout, sout, len sout) < 0)
+ return sys->sprint("error writing secretout: %r");
+ }
+ if(min != nil) {
+ if(SSL_DEBUG)
+ log("ssl3: set digest secret\n\tmacin:\n\t\t" + bastr(min) + "\n");
+ if(sys->write(fmin, min, len min) < 0)
+ return sys->sprint("error writing macin: %r");
+ }
+ if(mout != nil) {
+ if(SSL_DEBUG)
+ log("ssl3: set digest secret\n\tmacout:\n\t\t" + bastr(mout) + "\n");
+ if(sys->write(fmout, mout, len mout) < 0)
+ return sys->sprint("error writing macout: %r");
+ }
+
+ return "";
+}
+
+#
+# description must be alert description
+#
+fatal(description: int, debug_msg: string, ctx: ref Context)
+{
+ if(SSL_DEBUG)
+ log("ssl3: " + debug_msg);
+
+ # TODO: use V2Handshake.Error for v2
+ alert_enque(ref Alert(SSL_FATAL, description), ctx);
+
+ # delete session id
+ ctx.session.session_id = nil;
+
+ ctx.state = STATE_EXIT;
+}
+
+alert_enque(a: ref Alert, ctx: ref Context)
+{
+ p := ref Protocol.pAlert(a);
+
+ protocol_write(p, ctx);
+}
+
+# clean up out queue before switch cipher. this is why
+# change cipher spec differs from handshake message by ssl spec
+
+ccs_enque(cs: ref ChangeCipherSpec, ctx: ref Context)
+{
+ p := ref Protocol.pChangeCipherSpec(cs);
+
+ protocol_write(p, ctx);
+
+ record_write_queue(ctx);
+ ctx.out_queue.data = nil;
+}
+
+handshake_enque(h: ref Handshake, ctx: ref Context)
+{
+ p := ref Protocol.pHandshake(h);
+
+ protocol_write(p, ctx);
+}
+
+protocol_write(p: ref Protocol, ctx: ref Context)
+{
+ record_version := SSL_VERSION_2_0;
+ if(ctx.status & SSL3_RECORD)
+ record_version = SSL_VERSION_3_0;
+ (r, e) := p.encode(record_version);
+ if(e != "") {
+ if(SSL_DEBUG)
+ log("ssl3: protocol_write: " + e);
+ exit;
+ }
+
+ # Note: only for sslv3
+ if((ctx.status&SSL2_HANDSHAKE) && (ctx.status&SSL3_HANDSHAKE)) {
+ if(ctx.state == STATE_HELLO_REQUEST) {
+ e = update_handshake_hash(ctx, r);
+ if(e != "") {
+ if(SSL_DEBUG)
+ log("ssl3: protocol_write: " + e);
+ exit;
+ }
+ }
+ }
+ if((ctx.status&SSL3_HANDSHAKE) && (r.content_type == SSL_HANDSHAKE)) {
+ e = update_handshake_hash(ctx, r);
+ if(e != "") {
+ if(SSL_DEBUG)
+ log("ssl3: protocol_write: " + e);
+ exit;
+ }
+ }
+
+ ctx.out_queue.data = r :: ctx.out_queue.data;
+}
+
+#feed_data(ctx: ref Context, a: array of byte, n: int): int
+#{
+#
+#}
+
+# FIFO
+record_write_queue(ctx: ref Context)
+{
+ write_queue : list of ref Record;
+
+ wq := ctx.out_queue.data;
+ while(wq != nil) {
+ write_queue = hd wq :: write_queue;
+ wq = tl wq;
+ }
+
+ wq = write_queue;
+ while(wq != nil) {
+ record_write(hd wq, ctx);
+ wq = tl wq;
+ }
+}
+
+# Possible combinations are v2 only, v3 only and both (undetermined). The v2 only must be
+# v2 handshake and v2 record layer. The v3 only must be v3 handshake and v3 record layer.
+# If both v2 and v3 are supported, it may be v2 handshake and v2 record layer, or v3
+# handshake and v3 record layer, or v2 handshake and v3 record layer. In the case of
+# both, the client should send a v2 client hello message with handshake protocol version v3.
+
+do_protocol(ctx: ref Context): string
+{
+ r: ref Record;
+ in: ref Protocol;
+ e: string = nil;
+
+ while(ctx.state != STATE_EXIT) {
+
+ if(SSL_DEBUG)
+ log("ssl3: state = " + state_info(ctx));
+
+ # init a new handshake
+ if(ctx.state == STATE_HELLO_REQUEST) {
+ # v2 and v3
+ if((ctx.status&SSL2_HANDSHAKE) && (ctx.status&SSL3_HANDSHAKE)) {
+ ch := ref V2Handshake.ClientHello(
+ SSL_VERSION_3_0,
+ v3tov2specs(ctx.local_info.suites),
+ ctx.session.session_id,
+ ctx.client_random
+ );
+ v2handshake_enque(ch, ctx);
+ in = ref Protocol.pV2Handshake(ch);
+ }
+ # v3 only
+ else if(ctx.status&SSL3_HANDSHAKE) {
+ in = ref Protocol.pHandshake(ref Handshake.HelloRequest());
+ }
+ # v2 only
+ else if(ctx.status&SSL2_HANDSHAKE) {
+ ch := ref V2Handshake.ClientHello(
+ SSL_VERSION_2_0,
+ v3tov2specs(ctx.local_info.suites),
+ ctx.session.session_id,
+ ctx.client_random[32-SSL2_CHALLENGE_LENGTH:32]
+ );
+ v2handshake_enque(ch, ctx);
+ in = ref Protocol.pV2Handshake(ch);
+ }
+ # unknown version
+ else {
+ e = "unknown ssl device version";
+ fatal(SSL_CLOSE_NOTIFY, "ssl3: " + e, ctx);
+ continue;
+ }
+ }
+
+ if(in == nil) {
+ (r, in, e) = protocol_read(ctx);
+ if(e != "") {
+ fatal(SSL_CLOSE_NOTIFY, "ssl3: " + e, ctx);
+ continue;
+ }
+ if(SSL_DEBUG)
+ log("ssl3: protocol_read: ------\n" + in.tostring());
+ }
+
+ pick p := in {
+ pAlert =>
+ do_alert(p.alert, ctx);
+
+ pChangeCipherSpec =>
+ if(ctx.state != STATE_CHANGE_CIPHER_SPEC) {
+ e += "ChangeCipherSpec";
+ break;
+ }
+ do_change_cipher_spec(ctx);
+
+ pHandshake =>
+ if(!(ctx.status & SSL3_HANDSHAKE)) {
+ e = "Wrong Handshake";
+ break;
+ }
+ if((ctx.status & SSL3_RECORD) &&
+ (ctx.state == SSL2_STATE_SERVER_HELLO)) {
+ ctx.state = STATE_SERVER_HELLO;
+ ctx.status &= ~SSL2_HANDSHAKE;
+ }
+ e = do_handshake(p.handshake, ctx);
+
+ pV2Handshake =>
+ if(ctx.state != STATE_HELLO_REQUEST) {
+ if(!(ctx.status & SSL2_HANDSHAKE)) {
+ e = "Wrong Handshake";
+ break;
+ }
+ e = do_v2handshake(p.handshake, ctx);
+ }
+ else
+ ctx.state = SSL2_STATE_SERVER_HELLO;
+
+
+ * =>
+ e = "unknown protocol message";
+ }
+
+ if(e != nil) {
+ e = "do_protocol: wrong protocol side or protocol message: " + e;
+ fatal(SSL_UNEXPECTED_MESSAGE, e, ctx);
+ }
+
+ in = nil;
+
+ record_write_queue(ctx);
+ ctx.out_queue.data = nil;
+ }
+
+ return e;
+}
+
+state_info(ctx: ref Context): string
+{
+ info: string;
+
+ if(ctx.status & SSL3_RECORD)
+ info = "\n\tRecord Version 3: ";
+ else
+ info = "\n\tRecord Version 2: ";
+
+ if(ctx.status & SSL2_HANDSHAKE) {
+
+ if(ctx.status & SSL3_HANDSHAKE) {
+ info += "\n\tHandshake Version Undetermined: Client Hello";
+ }
+ else {
+ info += "\n\tHandshake Version 2: ";
+
+ case ctx.state {
+ SSL2_STATE_CLIENT_HELLO =>
+ info += "Client Hello";
+ SSL2_STATE_SERVER_HELLO =>
+ info += "Server Hello";
+ SSL2_STATE_CLIENT_MASTER_KEY =>
+ info += "Client Master Key";
+ SSL2_STATE_SERVER_VERIFY =>
+ info += "Server Verify";
+ SSL2_STATE_REQUEST_CERTIFICATE =>
+ info += "Request Certificate";
+ SSL2_STATE_CLIENT_CERTIFICATE =>
+ info += "Client Certificate";
+ SSL2_STATE_CLIENT_FINISHED =>
+ info += "Client Finished";
+ SSL2_STATE_SERVER_FINISHED =>
+ info += "Server Finished";
+ SSL2_STATE_ERROR =>
+ info += "Error";
+ }
+ }
+ }
+ else {
+ info = "\n\tHandshake Version 3: ";
+
+ case ctx.state {
+ STATE_EXIT =>
+ info += "Exit";
+
+ STATE_CHANGE_CIPHER_SPEC =>
+ info += "Change Cipher Spec";
+
+ STATE_HELLO_REQUEST =>
+ info += "Hello Request";
+
+ STATE_CLIENT_HELLO =>
+ info += "Client Hello";
+
+ STATE_SERVER_HELLO =>
+ info += "Server Hello";
+
+ STATE_CLIENT_KEY_EXCHANGE =>
+ info += "Client Key Exchange";
+
+ STATE_SERVER_KEY_EXCHANGE =>
+ info += "Server Key Exchange";
+
+ STATE_SERVER_HELLO_DONE =>
+ info += "Server Hello Done";
+
+ STATE_CLIENT_CERTIFICATE =>
+ info += "Client Certificate";
+
+ STATE_SERVER_CERTIFICATE =>
+ info += "Server Certificate";
+
+ STATE_CERTIFICATE_VERIFY =>
+ info += "Certificate Verify";
+
+ STATE_FINISHED =>
+ info += "Finished";
+ }
+ }
+
+ if(ctx.status & CLIENT_AUTH)
+ info += ": Client Auth";
+ if(ctx.status & CERT_REQUEST)
+ info += ": Cert Request";
+ if(ctx.status & CERT_SENT)
+ info += ": Cert Sent";
+ if(ctx.status & CERT_RECEIVED)
+ info += ": Cert Received";
+
+ return info;
+}
+
+reset_client_random(ctx: ref Context)
+{
+ ctx.client_random[0:] = int_encode(ctx.session.connection_time, 4);
+ ctx.client_random[4:] = random->randombuf(Random->NotQuiteRandom, 28);
+}
+
+reset_server_random(ctx: ref Context)
+{
+ ctx.server_random[0:] = int_encode(ctx.session.connection_time, 4);
+ ctx.server_random[4:] = random->randombuf(Random->NotQuiteRandom, 28);
+}
+
+update_handshake_hash(ctx: ref Context, r: ref Record): string
+{
+ err := "";
+
+ ctx.sha_state = keyring->sha1(r.data, len r.data, nil, ctx.sha_state);
+ ctx.md5_state = keyring->md5(r.data, len r.data, nil, ctx.md5_state);
+ if(ctx.sha_state == nil || ctx.md5_state == nil)
+ err = "update handshake hash failed";
+
+ # if(SSL_DEBUG)
+ # log("ssl3: update_handshake_hash\n\tmessage_data =\n\t\t" + bastr(r.data) + "\n");
+
+ return err;
+}
+
+# Note:
+# this depends on the record protocol
+protocol_read(ctx: ref Context): (ref Record, ref Protocol, string)
+{
+ p: ref Protocol;
+ r: ref Record;
+ e: string;
+
+ vers := SSL_VERSION_2_0;
+ if(ctx.status & SSL3_RECORD)
+ vers = SSL_VERSION_3_0;
+ if(ctx.status & USE_DEVSSL)
+ (r, e) = devssl_read(ctx);
+ else
+ (r, e) = record_read(ctx);
+ if(e != "")
+ return (nil, nil, e);
+
+ (p, e) = Protocol.decode(r, ctx);
+ if(e != "")
+ return (r, nil, e);
+
+ return (r, p, nil);
+}
+
+# Alert messages with a level of fatal result in the immediate
+# termination of the connection and zero out session.
+
+do_alert(a: ref Alert, ctx: ref Context)
+{
+ case a.level {
+ SSL_FATAL =>
+
+ case a.description {
+ SSL_UNEXPECTED_MESSAGE =>
+
+ # should never be observed in communication
+ # between proper implementations.
+ break;
+
+ SSL_HANDSHAKE_FAILURE =>
+
+ # unable to negotiate an acceptable set of security
+ # parameters given the options available.
+ break;
+
+ * =>
+ break;
+ }
+
+ ctx.session.session_id = nil;
+ ctx.state = STATE_EXIT;
+
+ SSL_WARNING =>
+
+ case a.description {
+ SSL_CLOSE_NOTIFY =>
+
+ if(SSL_DEBUG)
+ log("ssl3: do_alert SSL_WARNING:SSL_CLOSE_NOTIFY\n");
+ # notifies the recipient that the sender will not
+ # send any more messages on this connection.
+
+ ctx.state = STATE_EXIT;
+ fatal(SSL_CLOSE_NOTIFY, "ssl3: response close notify", ctx);
+
+ SSL_NO_CERTIFICATE =>
+
+ # A no_certificate alert message may be sent in
+ # response to a certification request if no
+ # appropriate certificate is available.
+
+ if(ctx.state == STATE_CLIENT_CERTIFICATE) {
+ hm := ref Handshake.Certificate(ctx.local_info.certs);
+ handshake_enque(hm, ctx);
+ }
+
+ SSL_BAD_CERTIFICATE or
+
+ # A certificate was corrupt, contained signatures
+ # that did not verify correctly, etc.
+
+ SSL_UNSUPPORTED_CERTIFICATE or
+
+ # A certificate was of an unsupported type.
+
+ SSL_CERTIFICATE_REVOKED or
+
+ # A certificate was revoked by its signer.
+
+ SSL_CERTIFICATE_EXPIRED or
+
+ # A certificate has expired or is not currently
+ # valid.
+
+ SSL_CERTIFICATE_UNKNOWN =>
+
+ # Some other (unspecified) issue arose in
+ # processing the certificate, rendering it
+ # unacceptable.
+ break;
+
+ * =>
+ ctx.session.session_id = nil;
+ fatal(SSL_ILLEGAL_PARAMETER, "ssl3: unknown alert description", ctx);
+ }
+
+ * =>
+ ctx.session.session_id = nil;
+ fatal(SSL_ILLEGAL_PARAMETER, "ssl3: unknown alert level received", ctx);
+ }
+}
+
+# notify the receiving party that subsequent records will
+# be protected under the just-negotiated CipherSpec and keys.
+
+do_change_cipher_spec(ctx: ref Context)
+{
+ # calculate and set new keys
+ if(!(ctx.status & IN_READY)) {
+ e := set_in_queue(ctx);
+ if(e != "") {
+ fatal(SSL_CLOSE_NOTIFY, "do_change_cipher_spec: setup new cipher failed", ctx);
+ return;
+ }
+ ctx.status |= IN_READY;
+
+ if(SSL_DEBUG)
+ log("ssl3: set in cipher done\n");
+ }
+
+ ctx.state = STATE_FINISHED;
+}
+
+
+# process and advance handshake messages, update internal stack and switch to next
+# expected state(s).
+
+do_handshake(handshake: ref Handshake, ctx: ref Context) : string
+{
+ e := "";
+
+ pick h := handshake {
+ HelloRequest =>
+ if(!(ctx.status & CLIENT_SIDE) || ctx.state != STATE_HELLO_REQUEST) {
+ e = "HelloRequest";
+ break;
+ }
+ do_hello_request(ctx);
+
+ ClientHello =>
+ if((ctx.status & CLIENT_SIDE) || ctx.state != STATE_CLIENT_HELLO) {
+ e = "ClientHello";
+ break;
+ }
+ do_client_hello(h, ctx);
+
+ ServerHello =>
+ if(!(ctx.status & CLIENT_SIDE) || ctx.state != STATE_SERVER_HELLO) {
+ e = "ServerHello";
+ break;
+ }
+ do_server_hello(h, ctx);
+
+ ClientKeyExchange =>
+ if((ctx.status & CLIENT_SIDE) || ctx.state != STATE_CLIENT_KEY_EXCHANGE) {
+ e = "ClientKeyExchange";
+ break;
+ }
+ do_client_keyex(h, ctx);
+
+ ServerKeyExchange =>
+ if(!(ctx.status & CLIENT_SIDE) ||
+ (ctx.state != STATE_SERVER_KEY_EXCHANGE && ctx.state != STATE_SERVER_HELLO_DONE)) {
+ e = "ServerKeyExchange";
+ break;
+ }
+ do_server_keyex(h, ctx);
+
+ ServerHelloDone =>
+ # diff from SSLRef, to support variant impl
+ if(!(ctx.status & CLIENT_SIDE) ||
+ (ctx.state != STATE_SERVER_HELLO_DONE && ctx.state != STATE_SERVER_KEY_EXCHANGE)) {
+ e = "ServerHelloDone";
+ break;
+ }
+ do_server_done(ctx);
+
+ Certificate =>
+ if(ctx.status & CLIENT_SIDE) {
+ if(ctx.state != STATE_SERVER_CERTIFICATE) {
+ e = "ServerCertificate";
+ break;
+ }
+ do_server_cert(h, ctx);
+ }
+ else {
+ if(ctx.state != STATE_CLIENT_CERTIFICATE) {
+ e = "ClientCertificate";
+ break;
+ }
+ do_client_cert(h, ctx); # server_side
+ }
+
+ CertificateRequest =>
+ if(!(ctx.status & CLIENT_SIDE) || ctx.state != STATE_SERVER_HELLO_DONE
+ || ctx.state != STATE_SERVER_KEY_EXCHANGE) {
+ e = "CertificateRequest";
+ break;
+ }
+ do_cert_request(h, ctx);
+
+ CertificateVerify =>
+ if((ctx.status & CLIENT_SIDE) || ctx.state != STATE_CERTIFICATE_VERIFY) {
+ e = "CertificateVerify";
+ break;
+ }
+ do_cert_verify(h, ctx);
+
+ Finished =>
+ if(ctx.status & CLIENT_SIDE) {
+ if(ctx.state != STATE_FINISHED) {
+ e = "ClientFinished";
+ break;
+ }
+ do_finished(SSL_CLIENT_SENDER, ctx);
+ }
+ else {
+ if(ctx.state != STATE_FINISHED) {
+ e = "ServerFinished";
+ break;
+ }
+ do_finished(SSL_SERVER_SENDER, ctx);
+ }
+
+ * =>
+ e = "unknown handshake message";
+ }
+
+ if(e != nil)
+ e = "do_handshake: " + e;
+
+ return e;
+}
+
+# [client side]
+# The hello request message may be sent by server at any time, but will be ignored by
+# the client if the handshake protocol is already underway. It is simple notification
+# that the client should begin the negotiation process anew by sending a client hello
+# message.
+
+do_hello_request(ctx: ref Context)
+{
+ # start from new handshake digest state
+ ctx.sha_state = ctx.md5_state = nil;
+
+ # Note:
+ # sending ctx.local_info.suites instead of ctx.session.suite,
+ # if session is resumable by server, ctx.session.suite will be used.
+ handshake_enque(
+ ref Handshake.ClientHello(
+ ctx.session.version,
+ ctx.client_random,
+ ctx.session.session_id,
+ ctx.local_info.suites,
+ ctx.local_info.comprs
+ ),
+ ctx
+ );
+
+ ctx.state = STATE_SERVER_HELLO;
+}
+
+# [client side]
+# Processes the received server hello handshake message and determines if the session
+# is resumable. (The client sends a client hello using the session id of the session
+# to be resumed. The server then checks its session cache for a match. If a match is
+# FOUND, and the server is WILLING to re-establish the connection under the specified
+# session state, it will send a server hello with the SAME session id value.) If the
+# session is resumed, at this point both client and server must send change cipher
+# spec messages. If the session is not resumable, the client and server perform
+# a full handshake. (On the server side, if a session id match is not found, the
+# server generates a new session id or if the server is not willing to resume, the
+# server uses a null session id).
+
+do_server_hello(hm: ref Handshake.ServerHello, ctx: ref Context)
+{
+ # trying to resume
+ if(bytes_cmp(ctx.session.session_id, hm.session_id) == 0) {
+
+ if(SSL_DEBUG)
+ log("ssl3: session resumed\n");
+
+ ctx.status |= SESSION_RESUMABLE;
+ # avoid version attack
+ if(ctx.session.version[0] != hm.version[0] ||
+ ctx.session.version[1] != hm.version[1]) {
+ fatal(SSL_CLOSE_NOTIFY, "do_server_hello: version mismatch", ctx);
+ return;
+ }
+
+ ctx.server_random = hm.random;
+
+ # uses the retrieved session suite by server (should be same by client)
+ (ciph, keyx, sign, e)
+ := suite_to_spec(hm.suite, SSL3_Suites);
+ if(e != nil) {
+ fatal(SSL_UNEXPECTED_MESSAGE, "server hello: suite not found", ctx);
+ return;
+ }
+ ctx.sel_ciph = ciph;
+ ctx.sel_keyx = keyx;
+ ctx.sel_sign = sign;
+ ctx.sel_cmpr = int ctx.session.compression; # not supported by ssl3 yet
+
+ # calculate keys
+ (ctx.cw_mac, ctx.sw_mac, ctx.cw_key, ctx.sw_key, ctx.cw_IV, ctx.sw_IV)
+ = calc_keys(ctx.sel_ciph, ctx.session.master_secret,
+ ctx.client_random, ctx.server_random);
+
+
+ ctx.state = STATE_CHANGE_CIPHER_SPEC;
+ }
+ else {
+ ctx.status &= ~SESSION_RESUMABLE;
+
+ # On the server side, if a session id match is not found, the
+ # server generates a new session id or if the server is not willing
+ # to resume, the server uses an empty session id and cannot be
+ # cached by both client and server.
+
+ ctx.session.session_id = hm.session_id;
+ ctx.session.version = hm.version;
+ ctx.server_random = hm.random;
+
+ if(SSL_DEBUG)
+ log("ssl3: do_server_hello:\n\tselected cipher suite =\n\t\t"
+ + cipher_suite_info(hm.suite, SSL3_Suites) + "\n");
+
+ (ciph, keyx, sign, e) := suite_to_spec(hm.suite, SSL3_Suites);
+ if(e != nil) {
+ fatal(SSL_UNEXPECTED_MESSAGE, "server hello: suite not found", ctx);
+ return;
+ }
+
+ ctx.sel_ciph = ciph;
+ ctx.sel_keyx = keyx;
+ ctx.sel_sign = sign;
+ ctx.sel_cmpr = int hm.compression; # not supported by ssl3 yet
+
+ # next state is determined by selected key exchange and signature methods
+ # the ctx.sel_keyx and ctx.sel_sign are completed by the following handshake
+ # Certificate and/or ServerKeyExchange
+
+ if(tagof ctx.sel_keyx == tagof KeyExAlg.DH &&
+ tagof ctx.sel_sign == tagof SigAlg.anon)
+ ctx.state = STATE_SERVER_KEY_EXCHANGE;
+ else
+ ctx.state = STATE_SERVER_CERTIFICATE;
+ }
+}
+
+# [client side]
+# Processes the received server key exchange message. The server key exchange message
+# is sent by the server if it has no certificate, has a certificate only used for
+# signing, or FORTEZZA KEA key exchange is used.
+
+do_server_keyex(hm: ref Handshake.ServerKeyExchange, ctx: ref Context)
+{
+ # install exchange keys sent by server, this may require public key
+ # retrieved from certificate sent by Handshake.Certificate message
+
+ (err, i) := install_server_xkey(hm.xkey, ctx.sel_keyx);
+ if(err == "")
+ err = verify_server_xkey(ctx.client_random, ctx.server_random, hm.xkey, i, ctx.sel_sign);
+
+ if(err == "")
+ ctx.state = STATE_SERVER_HELLO_DONE;
+ else
+ fatal(SSL_HANDSHAKE_FAILURE, "do_server_keyex: " + err, ctx);
+}
+
+# [client side]
+# Processes the received server hello done message by verifying that the server
+# provided a valid certificate if required and checking that the server hello
+# parameters are acceptable.
+
+do_server_done(ctx: ref Context)
+{
+ # On client side, optionally send client cert chain if client_auth
+ # is required by the server. The server may drop the connection,
+ # if it does not receive client certificate in the following
+ # Handshake.ClientCertificate message
+ if(ctx.status & CLIENT_AUTH) {
+ if(ctx.local_info.certs != nil) {
+ handshake_enque(
+ ref Handshake.Certificate(ctx.local_info.certs),
+ ctx
+ );
+ ctx.status |= CERT_SENT;
+ }
+ else {
+ alert_enque(
+ ref Alert(SSL_WARNING, SSL_NO_CERTIFICATE),
+ ctx
+ );
+ }
+ }
+
+ # calculate premaster secrect, client exchange keys and update ref KeyExAlg
+ # of the client side
+ (x, pm, e) := calc_client_xkey(ctx.sel_keyx);
+ if(e != "") {
+ fatal(SSL_HANDSHAKE_FAILURE, e, ctx);
+ return;
+ }
+ handshake_enque(ref Handshake.ClientKeyExchange(x), ctx);
+
+ ms := calc_master_secret(pm, ctx.client_random, ctx.server_random);
+ if(ms == nil) {
+ fatal(SSL_HANDSHAKE_FAILURE, "server hello done: calc master secret failed", ctx);
+ return;
+ }
+ # ctx.premaster_secret = pm;
+ ctx.session.master_secret = ms;
+
+ # sending certificate verifiy message if the client auth is required
+ # and client certificate has been sent,
+ if(ctx.status & CERT_SENT) {
+ sig : array of byte;
+ (md5_hash, sha_hash)
+ := calc_finished(nil, ctx.session.master_secret, ctx.sha_state, ctx.md5_state);
+ # check type of client cert being sent
+ pick sk := ctx.local_info.sk {
+ RSA =>
+ hashes := array [36] of byte;
+ hashes[0:] = md5_hash;
+ hashes[16:] = sha_hash;
+ #(e, sig) = pkcs->rsa_sign(hashes, sk, PKCS->MD5_WithRSAEncryption);
+ DSS =>
+ #(e, sig) = pkcs->dss_sign(sha_hash, sk);
+ * =>
+ e = "unknown sign";
+ }
+ if(e != "") {
+ fatal(SSL_HANDSHAKE_FAILURE, "server hello done: sign cert verify failed", ctx);
+ return;
+ }
+ handshake_enque(ref Handshake.CertificateVerify(sig), ctx);
+ }
+
+ ccs_enque(ref ChangeCipherSpec(1), ctx);
+ (ctx.cw_mac, ctx.sw_mac, ctx.cw_key, ctx.sw_key, ctx.cw_IV, ctx.sw_IV)
+ = calc_keys(ctx.sel_ciph, ctx.session.master_secret,
+ ctx.client_random, ctx.server_random);
+
+ # set cipher on write channel
+ e = set_out_queue(ctx);
+ if(e != nil) {
+ fatal(SSL_HANDSHAKE_FAILURE, "do_server_done: " + e, ctx);
+ return;
+ }
+ ctx.status |= OUT_READY;
+
+ if(SSL_DEBUG)
+ log("ssl3: set out cipher done\n");
+ (mh, sh) := calc_finished(SSL_CLIENT_SENDER, ctx.session.master_secret,
+ ctx.sha_state, ctx.md5_state);
+# sending out the Finished msg causes MS https servers to hangup
+#sys->print("RETURNING FROM DO_SERVER_DONE\n");
+#return;
+ handshake_enque(ref Handshake.Finished(mh, sh), ctx);
+
+ ctx.state = STATE_CHANGE_CIPHER_SPEC;
+}
+
+# [client side]
+# Process the received certificate message.
+# Note:
+# according to current US export law, RSA moduli larger than 512 bits
+# may not be used for key exchange in software exported from US. With
+# this message, larger RSA keys may be used as signature only
+# certificates to sign temporary shorter RSA keys for key exchange.
+
+do_server_cert(hm: ref Handshake.Certificate, ctx: ref Context)
+{
+ if(hm.cert_list == nil) {
+ fatal(SSL_UNEXPECTED_MESSAGE, "nil peer certificate", ctx);
+ return;
+ }
+
+ # server's certificate is the last one in the chain (reverse required)
+ cl := hm.cert_list;
+ ctx.session.peer_certs = nil;
+ while(cl != nil) {
+ ctx.session.peer_certs = hd cl::ctx.session.peer_certs;
+ cl = tl cl;
+ }
+
+ # TODO: verify certificate chain
+ # check if in the acceptable dnlist
+ # ctx.sel_keyx.peer_pk = x509->verify_chain(ctx.session.peer_certs);
+ if(SSL_DEBUG)
+ log("ssl3: number certificates got: " + string len ctx.session.peer_certs);
+ peer_cert := hd ctx.session.peer_certs;
+ (e, signed) := x509->Signed.decode(peer_cert);
+ if(e != "") {
+ if(SSL_DEBUG)
+ log("ss3: server certificate: " + e);
+ fatal(SSL_HANDSHAKE_FAILURE, "server certificate: " + e, ctx);
+ return;
+ }
+
+ srv_cert: ref Certificate;
+ (e, srv_cert) = x509->Certificate.decode(signed.tobe_signed);
+ if(e != "") {
+ if(SSL_DEBUG)
+ log("ss3: server certificate: " + e);
+ fatal(SSL_HANDSHAKE_FAILURE, "server certificate: " + e, ctx);
+ return;
+ }
+ if(SSL_DEBUG)
+ log("ssl3: " + srv_cert.tostring());
+
+ # extract and determine byte of user certificate
+ id: int;
+ peer_pk: ref X509->PublicKey;
+ (e, id, peer_pk) = srv_cert.subject_pkinfo.getPublicKey();
+ if(e != "") {
+ if(SSL_DEBUG)
+ log("ss3: server certificate: " + e);
+ fatal(SSL_HANDSHAKE_FAILURE, "server certificate:" + e, ctx);
+ return;
+ }
+
+ pick key := peer_pk {
+ RSA =>
+ # TODO: to allow checking X509v3 KeyUsage extension
+ if((0 && key.pk.modulus.bits() > 512 && ctx.sel_ciph.is_exportable)
+ || id == PKCS->id_pkcs_md2WithRSAEncryption
+ || id == PKCS->id_pkcs_md4WithRSAEncryption
+ || id == PKCS->id_pkcs_md5WithRSAEncryption) {
+ pick sign := ctx.sel_sign {
+ anon =>
+ break;
+ RSA =>
+ break;
+ * =>
+ # error
+ }
+ if(ctx.local_info.sk == nil)
+ ctx.sel_sign = ref SigAlg.RSA(nil, key.pk);
+ else {
+ pick mysk := ctx.local_info.sk {
+ RSA =>
+ ctx.sel_sign = ref SigAlg.RSA(mysk.sk, key.pk);
+ * =>
+ ctx.sel_sign = ref SigAlg.RSA(nil, key.pk);
+ }
+ }
+ # key exchange may be tmp RSA, emhemeral DH depending on cipher suite
+ ctx.state = STATE_SERVER_KEY_EXCHANGE;
+ }
+ # TODO: allow id == PKCS->id_rsa
+ else if(id == PKCS->id_pkcs_rsaEncryption) {
+ pick sign := ctx.sel_sign {
+ anon =>
+ break;
+ * =>
+ # error
+ }
+ ctx.sel_sign = ref SigAlg.anon();
+ pick keyx := ctx.sel_keyx {
+ RSA =>
+ keyx.peer_pk = key.pk;
+ * =>
+ # error
+ }
+ ctx.state = STATE_SERVER_HELLO_DONE;
+ }
+ else {
+ # error
+ }
+ DSS =>
+ pick sign := ctx.sel_sign {
+ DSS =>
+ sign.peer_pk = key.pk;
+ break;
+ * =>
+ # error
+ }
+ # should be key exchagne such as emhemeral DH
+ ctx.state = STATE_SERVER_KEY_EXCHANGE;
+ DH =>
+ # fixed DH signed in certificate either by RSA or DSS???
+ pick keyx := ctx.sel_keyx {
+ DH =>
+ keyx.peer_pk = key.pk;
+ * =>
+ # error
+ }
+ ctx.state = STATE_SERVER_KEY_EXCHANGE;
+ }
+
+ if(e != nil) {
+ fatal(SSL_HANDSHAKE_FAILURE, "do_server_cert: " + e, ctx);
+ return;
+ }
+}
+
+# [client side]
+# Processes certificate request message. A non-anonymous server can optionally
+# request a certificate from the client, if appropriate for the selected cipher
+# suite It is a fatal handshake failure alert for an anonymous server to
+# request client identification.
+
+# TODO: use another module to do x509 certs, lookup and matching rules
+
+do_cert_request(hm: ref Handshake.CertificateRequest, ctx: ref Context)
+{
+ found := 0;
+ for(i := 0; i < len hm.cert_types; i++) {
+ if(ctx.local_info.root_type == int hm.cert_types[i]) {
+ found = 1;
+ break;
+ }
+ }
+ if(!found) {
+ fatal(SSL_HANDSHAKE_FAILURE, "do_cert_request: no required type of cert", ctx);
+ return;
+ }
+ if(dn_cmp(ctx.local_info.dns, hm.dn_list) < 0) {
+ fatal(SSL_HANDSHAKE_FAILURE, "do_cert_request: no required dn", ctx);
+ return;
+ }
+ if(ctx.session.peer_certs == nil) {
+ fatal(SSL_NO_CERTIFICATE, "certificate request: no peer certificates", ctx);
+ return;
+ }
+
+ ctx.status |= CLIENT_AUTH;
+}
+
+dn_cmp(a, b: list of array of byte): int
+{
+ return -1;
+}
+
+# [server side]
+# Process client hello message.
+
+do_client_hello(hm: ref Handshake.ClientHello, ctx: ref Context)
+{
+ sndm : ref Handshake;
+ e : string;
+
+ if(hm.version[0] != SSL_VERSION_3_0[0] || hm.version[1] != SSL_VERSION_3_0[1]) {
+ fatal(SSL_UNEXPECTED_MESSAGE, "client hello: version mismatch", ctx);
+ return;
+ }
+ # else SSL_VERSION_2_0
+
+ if(hm.session_id != nil) { # trying to resume
+ if(ctx.status & SESSION_RESUMABLE) {
+ s := sslsession->get_session_byid(hm.session_id);
+ if(s == nil) {
+ fatal(SSL_UNEXPECTED_MESSAGE, "client hello: retrieve nil session", ctx);
+ return;
+ }
+
+ if(s.version[0] != hm.version[0] || s.version[1] != hm.version[1]) {
+ # avoid version attack
+ fatal(SSL_UNEXPECTED_MESSAGE, "client hello: protocol mismatch", ctx);
+ return;
+ }
+
+ reset_server_random(ctx);
+ ctx.client_random = hm.random;
+
+ sndm = ref Handshake.ServerHello(s.version, ctx.server_random,
+ s.session_id, s.suite, s.compression);
+ handshake_enque(sndm, ctx);
+
+ ccs_enque(ref ChangeCipherSpec(1), ctx);
+ # use existing master_secret, calc keys
+ (ctx.cw_mac, ctx.sw_mac, ctx.cw_key, ctx.sw_key, ctx.cw_IV, ctx.sw_IV)
+ = calc_keys(ctx.sel_ciph, ctx.session.master_secret, ctx.client_random,
+ ctx.server_random);
+ e = set_out_queue(ctx);
+ if(e != nil) {
+ fatal(SSL_CLOSE_NOTIFY, "client hello: setup new cipher failure", ctx);
+ return;
+ }
+ if(SSL_DEBUG)
+ log("do_client_hello: set out cipher done\n");
+
+ (md5_hash, sha_hash) := calc_finished(SSL_SERVER_SENDER,
+ s.master_secret, ctx.sha_state, ctx.md5_state);
+
+ handshake_enque(ref Handshake.Finished(md5_hash, sha_hash), ctx);
+
+ ctx.session = s;
+ ctx.state = STATE_CHANGE_CIPHER_SPEC;
+ return;
+ }
+
+ fatal(SSL_CLOSE_NOTIFY, "client hello: resume session failed", ctx);
+ return;
+ }
+
+ ctx.session.version = hm.version;
+ if(ctx.session.peer != nil) {
+ ctx.session.session_id = random->randombuf(Random->NotQuiteRandom, 32);
+ if(ctx.session.session_id == nil) {
+ fatal(SSL_CLOSE_NOTIFY, "client hello: generate session id failed", ctx);
+ return;
+ }
+ }
+
+ suite := find_cipher_suite(hm.suites, ctx.local_info.suites);
+ if(suite != nil) {
+ fatal(SSL_HANDSHAKE_FAILURE, "client hello: find cipher suite failed", ctx);
+ return;
+ }
+
+ (ctx.sel_ciph, ctx.sel_keyx, ctx.sel_sign, e) = suite_to_spec(suite, SSL3_Suites);
+ if(e != nil) {
+ fatal(SSL_HANDSHAKE_FAILURE, "client hello: find cipher suite failed" + e, ctx);
+ return;
+ }
+
+ # not supported by ssl3 yet
+ ctx.sel_cmpr = int hm.compressions[0];
+ ctx.client_random = hm.random;
+ ctx.sha_state = nil;
+ ctx.md5_state = nil;
+
+ sndm = ref Handshake.ServerHello(ctx.session.version, ctx.server_random,
+ ctx.session.session_id, ctx.session.suite, ctx.session.compression);
+ handshake_enque(sndm, ctx);
+
+ # set up keys based on algorithms
+
+ if(tagof ctx.sel_keyx != tagof KeyExAlg.DH) {
+ if(ctx.local_info.certs == nil || ctx.local_info.sk == nil) {
+ fatal(SSL_HANDSHAKE_FAILURE, "client hello: no local cert or key", ctx);
+ return;
+ }
+
+ sndm = ref Handshake.Certificate(ctx.local_info.certs);
+ handshake_enque(sndm, ctx);
+ }
+
+ if(tagof ctx.sel_keyx != tagof KeyExAlg.RSA ||
+ tagof ctx.sel_sign != tagof SigAlg.anon) {
+ params, signed_params, xkey: array of byte;
+ (params, e) = calc_server_xkey(ctx.sel_keyx);
+ if(e == "")
+ (signed_params, e) = sign_server_xkey(ctx.sel_sign, params,
+ ctx.client_random, ctx.server_random);
+ if(e != "")
+
+ n := len params + 2 + len signed_params;
+ xkey = array [n] of byte;
+ xkey[0:] = params;
+ xkey[len params:] = int_encode(len signed_params, 2);
+ xkey[len params+2:] = signed_params;
+ handshake_enque(ref Handshake.ServerKeyExchange(xkey), ctx);
+ }
+
+ if(ctx.status & CLIENT_AUTH) {
+ sndm = ref Handshake.CertificateRequest(ctx.local_info.types, ctx.local_info.dns);
+ handshake_enque(sndm, ctx);
+
+ ctx.status |= CERT_REQUEST;
+ ctx.state = STATE_CLIENT_CERTIFICATE;
+ }
+ else
+ ctx.state = STATE_CLIENT_KEY_EXCHANGE;
+
+ handshake_enque(ref Handshake.ServerHelloDone(), ctx);
+}
+
+# [server side]
+# Process the received client key exchange message.
+
+do_client_keyex(hm: ref Handshake.ClientKeyExchange, ctx: ref Context)
+{
+ (premaster_secret, err) := install_client_xkey(hm.xkey, ctx.sel_keyx);
+ if(err != "") {
+ fatal(SSL_HANDSHAKE_FAILURE, err, ctx);
+ return;
+ }
+
+ ctx.session.master_secret = calc_master_secret(premaster_secret,
+ ctx.client_random, ctx.server_random);
+
+ if(ctx.status & CERT_RECEIVED)
+ ctx.state = STATE_CERTIFICATE_VERIFY;
+ else
+ ctx.state = STATE_CHANGE_CIPHER_SPEC;
+}
+
+# [server side]
+# Process the received certificate message from client.
+
+do_client_cert(hm: ref Handshake.Certificate, ctx: ref Context)
+{
+ ctx.session.peer_certs = hm.cert_list;
+
+ # verify cert chain and determine the type of cert
+ # ctx.peer_info.sk = x509->verify_chain(ctx.session.peer_certs);
+ # if(ctx.peer_info.key == nil) {
+ # fatal(SSL_HANDSHAKE_FAILURE, "client certificate: cert verify failed", ctx);
+ # return;
+ # }
+
+ ctx.status |= CERT_RECEIVED;
+
+ ctx.state = STATE_CLIENT_KEY_EXCHANGE;
+}
+
+# [server side]
+# Process the received certificate verify message from client.
+
+do_cert_verify(hm: ref Handshake.CertificateVerify, ctx: ref Context)
+{
+ if(ctx.status & CERT_RECEIVED) {
+ # exp : array of byte;
+ (md5_hash, sha_hash)
+ := calc_finished(nil, ctx.session.master_secret, ctx.sha_state, ctx.md5_state);
+ ok := 0;
+ pick upk := ctx.sel_sign {
+ RSA =>
+ hashes := array [36] of byte;
+ hashes[0:] = md5_hash;
+ hashes[16:] = sha_hash;
+ ok = pkcs->rsa_verify(hashes, hm.signature, upk.peer_pk, PKCS->MD5_WithRSAEncryption);
+ DSS =>
+ ok = pkcs->dss_verify(sha_hash, hm.signature, upk.peer_pk);
+ }
+
+ if(!ok) {
+ fatal(SSL_HANDSHAKE_FAILURE, "do_cert_verify: client auth failed", ctx);
+ return;
+ }
+ }
+ else {
+ alert_enque(ref Alert(SSL_WARNING, SSL_NO_CERTIFICATE), ctx);
+ return;
+ }
+
+ ctx.state = STATE_CHANGE_CIPHER_SPEC;
+}
+
+# [client or server side]
+# Process the received finished message either from client or server.
+
+do_finished(sender: array of byte, ctx: ref Context)
+{
+ # setup write_cipher if not yet
+ if(!(ctx.status & OUT_READY)) {
+ ccs_enque(ref ChangeCipherSpec(1), ctx);
+ e := set_out_queue(ctx);
+ if(e != nil) {
+ fatal(SSL_CLOSE_NOTIFY, "do_finished: setup new cipher failed", ctx);
+ return;
+ }
+ ctx.status |= OUT_READY;
+
+ if(SSL_DEBUG)
+ log("ssl3: set out cipher done\n");
+
+ (md5_hash, sha_hash) := calc_finished(sender, ctx.session.master_secret,
+ ctx.sha_state, ctx.md5_state);
+ handshake_enque(ref Handshake.Finished(md5_hash, sha_hash), ctx);
+ }
+
+ ctx.state = STATE_EXIT; # normal
+
+ # clean read queue
+ ctx.in_queue.fragment = 0;
+
+ sslsession->add_session(ctx.session);
+
+ if(SSL_DEBUG)
+ log("ssl3: add session to session database done\n");
+}
+
+install_client_xkey(a: array of byte, keyx: ref KeyExAlg): (array of byte, string)
+{
+ pmaster, x : array of byte;
+ err := "";
+ pick kx := keyx {
+ DH =>
+ i := 0;
+ (kx.peer_pk, i) = dh_params_decode(a);
+ if(kx.peer_pk != nil)
+ pmaster = pkcs->computeDHAgreedKey(kx.sk.param, kx.sk.sk, kx.peer_pk.pk);
+ else
+ err = "decode dh params failed";
+ RSA =>
+ (err, x) = pkcs->rsa_decrypt(a, kx.sk, 2);
+ if(err != "" || len x != 48) {
+ err = "impl error";
+ }
+ else {
+ if(x[0] != SSL_VERSION_3_0[0] && x[1] != SSL_VERSION_3_0[1])
+ err = "version wrong: possible version attack";
+ else
+ pmaster = x[2:];
+ }
+ FORTEZZA_KEA =>
+ err = "Fortezza unsupported";
+ }
+ return (pmaster, err);
+}
+
+install_server_xkey(a: array of byte, keyx: ref KeyExAlg): (string, int)
+{
+ err := "";
+ i := 0;
+
+ pick kx := keyx {
+ DH =>
+ (kx.peer_pk, i) = dh_params_decode(a);
+ if(kx.peer_pk != nil)
+ kx.peer_params = kx.peer_pk.param;
+ RSA =>
+ peer_tmp: ref RSAParams;
+ (peer_tmp, i, err) = rsa_params_decode(a);
+ if(err == "") {
+ modlen := len peer_tmp.modulus.iptobebytes();
+ kx.peer_pk = ref RSAKey(peer_tmp.modulus, modlen, peer_tmp.exponent);
+ }
+ FORTEZZA_KEA =>
+ return ("Fortezza unsupported", i);
+ }
+
+ return (err, i);
+}
+
+verify_server_xkey(crand, srand: array of byte, a: array of byte, i : int, sign: ref SigAlg)
+ : string
+{
+ pick sg := sign {
+ anon =>
+ RSA =>
+ lb := a[0:i]::crand::srand::nil;
+ (exp, nil, nil) := md5_sha_hash(lb, nil, nil);
+ ok := pkcs->rsa_verify(exp, a[i+2:], sg.peer_pk, PKCS->MD5_WithRSAEncryption);
+ if(!ok)
+ return "RSA sigature verification failed";
+ DSS =>
+ lb := a[0:i]::crand::srand::nil;
+ (exp, nil) := sha_hash(lb, nil);
+ ok := pkcs->dss_verify(exp, a[i+2:], sg.peer_pk);
+ if(!ok)
+ return "DSS sigature verification failed";
+ }
+
+ return "";
+}
+
+calc_client_xkey(keyx: ref KeyExAlg): (array of byte, array of byte, string)
+{
+ pm, x : array of byte;
+ err := "";
+ pick kx := keyx {
+ DH =>
+ # generate our own DH keys based on DH params of peer side
+ (kx.sk, kx.exch_pk) = pkcs->setupDHAgreement(kx.peer_params);
+ # TODO: need check type of client cert if(!ctx.status & CLIENT_AUTH)
+ # for implicit case
+ (x, err) = dh_exchpub_encode(kx.exch_pk);
+ pm = pkcs->computeDHAgreedKey(kx.sk.param, kx.sk.sk, kx.peer_pk.pk);
+ RSA =>
+ pm = array [48] of byte;
+ pm[0:] = SSL_VERSION_3_0; # against version attack
+ pm[2:] = random->randombuf(Random->NotQuiteRandom, 46);
+ (err, x) = pkcs->rsa_encrypt(pm, kx.peer_pk, 2);
+ FORTEZZA_KEA =>
+ err = "Fortezza unsupported";
+ }
+ if(SSL_DEBUG)
+ log("ssl3: calc_client_xkey: " + bastr(x));
+ return (x, pm, err);
+}
+
+calc_server_xkey(keyx: ref KeyExAlg): (array of byte, string)
+{
+ params: array of byte;
+ err: string;
+ pick kx := keyx {
+ DH =>
+ (kx.sk, kx.exch_pk) = pkcs->setupDHAgreement(kx.params);
+ (params, err) = dh_params_encode(kx.exch_pk);
+ RSA =>
+ tmp := ref RSAParams(kx.export_key.modulus, kx.export_key.exponent);
+ (params, err) = rsa_params_encode(tmp);
+
+ FORTEZZA_KEA =>
+ err = "Fortezza unsupported";
+ }
+ return (params, err);
+}
+
+sign_server_xkey(sign: ref SigAlg, params, cr, sr: array of byte): (array of byte, string)
+{
+ signed_params: array of byte;
+ err: string;
+ pick sg := sign {
+ anon =>
+ RSA =>
+ lb := cr::sr::params::nil;
+ (hashes, nil, nil) := md5_sha_hash(lb, nil, nil);
+ (err, signed_params) = pkcs->rsa_sign(hashes, sg.sk, PKCS->MD5_WithRSAEncryption);
+ DSS =>
+ lb := cr::sr::params::nil;
+ (hashes, nil) := sha_hash(lb, nil);
+ (err, signed_params) = pkcs->dss_sign(hashes, sg.sk);
+ }
+ return (signed_params, err);
+}
+
+# ssl encoding of DH exchange public key
+
+dh_exchpub_encode(dh: ref DHPublicKey): (array of byte, string)
+{
+ if(dh != nil) {
+ yb := dh.pk.iptobebytes();
+ if(yb != nil) {
+ n := 2 + len yb;
+ a := array [n] of byte;
+ i := 0;
+ a[i:] = int_encode(len yb, 2);
+ i += 2;
+ a[i:] = yb;
+ return (a, nil);
+ }
+ }
+ return (nil, "nil dh params");
+}
+
+dh_params_encode(dh: ref DHPublicKey): (array of byte, string)
+{
+ if(dh != nil && dh.param != nil) {
+ pb := dh.param.prime.iptobebytes();
+ gb := dh.param.base.iptobebytes();
+ yb := dh.pk.iptobebytes();
+ if(pb != nil && gb != nil && yb != nil) {
+ n := 6 + len pb + len gb + len yb;
+ a := array [n] of byte;
+ i := 0;
+ a[i:] = int_encode(len pb, 2);
+ i += 2;
+ a[i:] = pb;
+ i += len pb;
+ a[i:] = int_encode(len gb, 2);
+ i += 2;
+ a[i:] = gb;
+ i += len gb;
+ a[i:] = int_encode(len yb, 2);
+ i += 2;
+ a[i:] = yb;
+ i += len yb;
+ return (a, nil);
+ }
+ }
+ return (nil, "nil dh public key");
+}
+
+dh_params_decode(a: array of byte): (ref DHPublicKey, int)
+{
+ i := 0;
+ for(;;) {
+ n := int_decode(a[i:i+2]);
+ i += 2;
+ if(i+n > len a)
+ break;
+ p := a[i:i+n];
+ i += n;
+ n = int_decode(a[i:i+2]);
+ i += 2;
+ if(i+n > len a)
+ break;
+ g := a[i:i+n];
+ i += n;
+ n = int_decode(a[i:i+2]);
+ i += 2;
+ if(i+n > len a)
+ break;
+ Ys := a[i:i+n];
+ i += n;
+
+ if(SSL_DEBUG)
+ log("ssl3: dh_params_decode:" + "\n\tp =\n\t\t" + bastr(p)
+ + "\n\tg =\n\t\t" + bastr(g) + "\n\tYs =\n\t\t" + bastr(Ys) + "\n");
+
+ # don't care privateValueLength
+ param := ref DHParams(IPint.bebytestoip(p), IPint.bebytestoip(g), 0);
+ return (ref DHPublicKey(param, IPint.bebytestoip(Ys)), i);
+ }
+ return (nil, i);
+}
+
+rsa_params_encode(rsa_params: ref RSAParams): (array of byte, string)
+{
+ if(rsa_params != nil) {
+ mod := rsa_params.modulus.iptobebytes();
+ exp := rsa_params.exponent.iptobebytes();
+ if(mod != nil || exp != nil) {
+ n := 4 + len mod + len exp;
+ a := array [n] of byte;
+ i := 0;
+ a[i:] = int_encode(len mod, 2);
+ i += 2;
+ a[i:] = mod;
+ i += len mod;
+ a[i:] = int_encode(len exp, 2);
+ i += 2;
+ a[i:] = exp;
+ i += len exp;
+ return (a, nil);
+ }
+ }
+ return (nil, "nil rsa params");
+}
+
+rsa_params_decode(a: array of byte): (ref RSAParams, int, string)
+{
+ i := 0;
+ for(;;) {
+ if(len a < 2)
+ break;
+ n := int_decode(a[i:i+2]);
+ i += 2;
+ if(n < 0 || n + i > len a)
+ break;
+ mod := a[i:i+n];
+ i += n;
+ n = int_decode(a[i:i+2]);
+ i += 2;
+ if(n < 0 || n + i > len a)
+ break;
+ exp := a[i:i+n];
+ i += n;
+ m := i;
+ modulus := IPint.bebytestoip(mod);
+ exponent := IPint.bebytestoip(exp);
+
+ if(SSL_DEBUG)
+ log("ssl3: decode RSA params\n\tmodulus = \n\t\t" + bastr(mod)
+ + "\n\texponent = \n\t\t" + bastr(exp) + "\n");
+
+ if(len a < i+2)
+ break;
+ n = int_decode(a[i:i+2]);
+ i += 2;
+ if(len a != i + n)
+ break;
+ return (ref RSAParams(modulus, exponent), m, nil);
+ }
+ return (nil, i, "encoding error");
+}
+
+# md5_hash MD5(master_secret + pad2 +
+# MD5(handshake_messages + Sender +
+# master_secret + pad1));
+# sha_hash SHA(master_secret + pad2 +
+# SHA(handshake_messages + Sender +
+# master_secret + pad1));
+#
+# handshake_messages All of the data from all handshake messages
+# up to but not including this message. This
+# is only data visible at the handshake layer
+# and does not include record layer headers.
+#
+# sender [4], master_secret [48]
+# pad1 and pad2, 48 bytes for md5, 40 bytes for sha
+
+calc_finished(sender, master_secret: array of byte, sha_state, md5_state: ref DigestState)
+ : (array of byte, array of byte)
+{
+ sha_value := array [Keyring->SHA1dlen] of byte;
+ md5_value := array [Keyring->MD5dlen] of byte;
+ sha_inner := array [Keyring->SHA1dlen] of byte;
+ md5_inner := array [Keyring->MD5dlen] of byte;
+
+ lb := master_secret::SSL_MAC_PAD1[0:48]::nil;
+ if(sender != nil)
+ lb = sender::lb;
+ (md5_inner, nil) = md5_hash(lb, md5_state);
+
+ lb = master_secret::SSL_MAC_PAD1[0:40]::nil;
+ if(sender != nil)
+ lb = sender::lb;
+ (sha_inner, nil) = sha_hash(lb, sha_state);
+
+ (md5_value, nil) = md5_hash(master_secret::SSL_MAC_PAD2[0:48]::md5_inner::nil, nil);
+ (sha_value, nil) = sha_hash(master_secret::SSL_MAC_PAD2[0:40]::sha_inner::nil, nil);
+
+ # if(SSL_DEBUG)
+ # log("ssl3: calc_finished:"
+ # + "\n\tmd5_inner = \n\t\t" + bastr(md5_inner)
+ # + "\n\tsha_inner = \n\t\t" + bastr(sha_inner)
+ # + "\n\tmd5_value = \n\t\t" + bastr(md5_value)
+ # + "\n\tsha_value = \n\t\t" + bastr(sha_value)
+ # + "\n");
+
+ return (md5_value, sha_value);
+}
+
+
+# master_secret =
+# MD5(premaster_secret + SHA('A' + premaster_secret +
+# ClientHello.random + ServerHello.random)) +
+# MD5(premaster_secret + SHA('BB' + premaster_secret +
+# ClientHello.random + ServerHello.random)) +
+# MD5(premaster_secret + SHA('CCC' + premaster_secret +
+# ClientHello.random + ServerHello.random));
+
+calc_master_secret(pm, cr, sr: array of byte): array of byte
+{
+ ms := array [48] of byte;
+ sha_value := array [Keyring->SHA1dlen] of byte;
+ leader := array [3] of byte;
+
+ j := 0;
+ lb := pm::cr::sr::nil;
+ for(i := 1; i <= 3; i++) {
+ leader[0] = leader[1] = leader[2] = byte (16r40 + i);
+ (sha_value, nil) = sha_hash(leader[0:i]::lb, nil);
+ (ms[j:], nil) = md5_hash(pm::sha_value::nil, nil);
+ j += 16; # Keyring->MD5dlen
+ }
+
+ if(SSL_DEBUG)
+ log("ssl3: calc_master_secret:\n\tmaster_secret = \n\t\t" + bastr(ms) + "\n");
+
+ return ms;
+}
+
+
+# key_block =
+# MD5(master_secret + SHA(`A' + master_secret +
+# ServerHello.random + ClientHello.random)) +
+# MD5(master_secret + SHA(`BB' + master_secret +
+# ServerHello.random + ClientHello.random)) +
+# MD5(master_secret + SHA(`CCC' + master_secret +
+# ServerHello.random + ClientHello.random)) +
+# [...];
+
+calc_key_material(n: int, ms, cr, sr: array of byte): array of byte
+{
+ key_block := array [n] of byte;
+ sha_value := array [Keyring->SHA1dlen] of byte; # [20]
+ md5_value := array [Keyring->MD5dlen] of byte; # [16]
+ leader := array [10] of byte;
+
+ if(n > 16*(len leader)) {
+ if(SSL_DEBUG)
+ log(sys->sprint("ssl3: calc key block: key size too long [%d]", n));
+ return nil;
+ }
+
+ m := n;
+ i, j, consumed, next : int = 0;
+ lb := ms::sr::cr::nil;
+ for(i = 0; m > 0; i++) {
+ for(j = 0; j <= i; j++)
+ leader[j] = byte (16r41 + i); # 'A', 'BB', 'CCC', etc.
+
+ (sha_value, nil) = sha_hash(leader[0:i+1]::lb, nil);
+ (md5_value, nil) = md5_hash(ms::sha_value::nil, nil);
+
+ consumed = Keyring->MD5dlen;
+ if(m < Keyring->MD5dlen)
+ consumed = m;
+ m -= consumed;
+
+ key_block[next:] = md5_value[0:consumed];
+ next += consumed;
+ }
+
+ if(SSL_DEBUG)
+ log("ssl3: calc_key_material:" + "\n\tkey_block = \n\t\t" + bastr(key_block) + "\n");
+
+ return key_block;
+}
+
+# Then the key_block is partitioned as follows.
+#
+# client_write_MAC_secret[CipherSpec.hash_size]
+# server_write_MAC_secret[CipherSpec.hash_size]
+# client_write_key[CipherSpec.key_material]
+# server_write_key[CipherSpec.key_material]
+# client_write_IV[CipherSpec.IV_size] /* non-export ciphers */
+# server_write_IV[CipherSpec.IV_size] /* non-export ciphers */
+#
+# Any extra key_block material is discarded.
+#
+# Exportable encryption algorithms (for which
+# CipherSpec.is_exportable is true) require additional processing as
+# follows to derive their final write keys:
+#
+# final_client_write_key = MD5(client_write_key +
+# ClientHello.random +
+# ServerHello.random);
+# final_server_write_key = MD5(server_write_key +
+# ServerHello.random +
+# ClientHello.random);
+#
+# Exportable encryption algorithms derive their IVs from the random
+# messages:
+#
+# client_write_IV = MD5(ClientHello.random + ServerHello.random);
+# server_write_IV = MD5(ServerHello.random + ClientHello.random);
+
+calc_keys(ciph: ref CipherSpec, ms, cr, sr: array of byte)
+ : (array of byte, array of byte, array of byte, array of byte, array of byte, array of byte)
+{
+ cw_mac, sw_mac, cw_key, sw_key, cw_IV, sw_IV: array of byte;
+
+ n := ciph.key_material + ciph.hash_size;
+ if(ciph.is_exportable == SSL_EXPORT_FALSE)
+ n += ciph.IV_size;
+ n *= 2;
+
+ key_block := calc_key_material(n, ms, cr, sr);
+
+ i := 0;
+ if(ciph.hash_size != 0) {
+ cw_mac = key_block[i:i+ciph.hash_size];
+ i += ciph.hash_size;
+ sw_mac = key_block[i:i+ciph.hash_size];
+ i += ciph.hash_size;
+ }
+
+ if(ciph.is_exportable == SSL_EXPORT_FALSE) {
+ if(ciph.key_material != 0) {
+ cw_key = key_block[i:i+ciph.key_material];
+ i += ciph.key_material;
+ sw_key = key_block[i:i+ciph.key_material];
+ i += ciph.key_material;
+ }
+ if(ciph.IV_size != 0) {
+ cw_IV = key_block[i:i+ciph.IV_size];
+ i += ciph.IV_size;
+ sw_IV = key_block[i:i+ciph.IV_size];
+ i += ciph.IV_size;
+ }
+ }
+ else {
+ if(ciph.key_material != 0) {
+ cw_key = key_block[i:i+ciph.key_material];
+ i += ciph.key_material;
+ sw_key = key_block[i:i+ciph.key_material];
+ i += ciph.key_material;
+ (cw_key, nil) = md5_hash(cw_key::cr::sr::nil, nil);
+ (sw_key, nil) = md5_hash(sw_key::sr::cr::nil, nil);
+ }
+ if(ciph.IV_size != 0) {
+ (cw_IV, nil) = md5_hash(cr::sr::nil, nil);
+ (sw_IV, nil) = md5_hash(sr::cr::nil, nil);
+ }
+ }
+
+ if(SSL_DEBUG)
+ log("ssl3: calc_keys:"
+ + "\n\tclient_write_mac = \n\t\t" + bastr(cw_mac)
+ + "\n\tserver_write_mac = \n\t\t" + bastr(sw_mac)
+ + "\n\tclient_write_key = \n\t\t" + bastr(cw_key)
+ + "\n\tserver_write_key = \n\t\t" + bastr(sw_key)
+ + "\n\tclient_write_IV = \n\t\t" + bastr(cw_IV)
+ + "\n\tserver_write_IV = \n\t\t" + bastr(sw_IV) + "\n");
+
+ return (cw_mac, sw_mac, cw_key, sw_key, cw_IV, sw_IV);
+}
+
+#
+# decode protocol message
+#
+Protocol.decode(r: ref Record, ctx: ref Context): (ref Protocol, string)
+{
+ p : ref Protocol;
+
+ case r.content_type {
+ SSL_ALERT =>
+ if(len r.data != 2)
+ return (nil, "alert decode failed");
+
+ p = ref Protocol.pAlert(ref Alert(int r.data[0], int r.data[1]));
+
+ SSL_CHANGE_CIPHER_SPEC =>
+ if(len r.data != 1 || r.data[0] != byte 1)
+ return (nil, "ChangeCipherSpec decode failed");
+
+ p = ref Protocol.pChangeCipherSpec(ref ChangeCipherSpec(1));
+
+ SSL_HANDSHAKE =>
+ (hm, e) := Handshake.decode(r.data);
+ if(e != nil)
+ return (nil, e);
+
+ pick h := hm {
+ Finished =>
+ exp_sender := SSL_CLIENT_SENDER;
+ if(ctx.status & CLIENT_SIDE)
+ exp_sender = SSL_SERVER_SENDER;
+
+ (md5_hash, sha_hash) := calc_finished(exp_sender,
+ ctx.session.master_secret, ctx.sha_state, ctx.md5_state);
+
+ if(SSL_DEBUG)
+ log("ssl3: handshake_decode: finished"
+ + "\n\texpected_md5_hash = \n\t\t" + bastr(md5_hash)
+ + "\n\tgot_md5_hash = \n\t\t" + bastr(h.md5_hash)
+ + "\n\texpected_sha_hash = \n\t\t" + bastr(sha_hash)
+ + "\n\tgot_sha_hash = \n\t\t" + bastr(h.sha_hash) + "\n");
+
+ #if(string md5_hash != string h.md5_hash || string sha_hash != string h.sha_hash)
+ if(bytes_cmp(md5_hash, h.md5_hash) < 0 || bytes_cmp(sha_hash, h.sha_hash) < 0)
+ return (nil, "finished: sender mismatch");
+
+ e = update_handshake_hash(ctx, r);
+ if(e != nil)
+ return (nil, e);
+
+ CertificateVerify =>
+
+ e = update_handshake_hash(ctx, r);
+ if(e != nil)
+ return (nil, e);
+
+ * =>
+ e = update_handshake_hash(ctx, r);
+ if(e != nil)
+ return (nil, e);
+ }
+
+ p = ref Protocol.pHandshake(hm);
+
+ SSL_V2HANDSHAKE =>
+
+ (hm, e) := V2Handshake.decode(r.data);
+ if(e != "")
+ return (nil, e);
+
+ p = ref Protocol.pV2Handshake(hm);
+
+ * =>
+ return (nil, "protocol read: unknown protocol");
+ }
+
+ return (p, nil);
+
+}
+
+
+# encode protocol message and return tuple of data record and error message,
+# may be v2 or v3 record depending on vers.
+
+Protocol.encode(protocol: self ref Protocol, vers: array of byte): (ref Record, string)
+{
+ r: ref Record;
+ e: string;
+
+ pick p := protocol {
+ pAlert =>
+ r = ref Record(
+ SSL_ALERT,
+ vers,
+ array [] of {byte p.alert.level, byte p.alert.description}
+ );
+
+ pChangeCipherSpec =>
+ r = ref Record(
+ SSL_CHANGE_CIPHER_SPEC,
+ vers,
+ array [] of {byte p.change_cipher_spec.value}
+ );
+
+ pHandshake =>
+ data: array of byte;
+ (data, e) = p.handshake.encode();
+ if(e != "")
+ break;
+ r = ref Record(
+ SSL_HANDSHAKE,
+ vers,
+ data
+ );
+
+ pV2Handshake =>
+ data: array of byte;
+ (data, e) = p.handshake.encode();
+ if(e != "")
+ break;
+ r = ref Record(
+ SSL_V2HANDSHAKE,
+ vers,
+ data
+ );
+
+ * =>
+ e = "unknown protocol";
+ }
+
+ if(SSL_DEBUG)
+ log("ssl3: protocol encode\n" + protocol.tostring());
+
+ return (r, e);
+}
+
+#
+# protocol message description
+#
+Protocol.tostring(protocol: self ref Protocol): string
+{
+ info : string;
+
+ pick p := protocol {
+ pAlert =>
+ info = "\tAlert\n" + p.alert.tostring();
+
+ pChangeCipherSpec =>
+ info = "\tChangeCipherSpec\n";
+
+ pHandshake =>
+ info = "\tHandshake\n" + p.handshake.tostring();
+
+ pV2Handshake =>
+ info = "\tV2Handshake\n" + p.handshake.tostring();
+
+ pApplicationData =>
+ info = "\tApplicationData\n";
+
+ * =>
+ info = "\tUnknownProtocolType\n";
+ }
+
+ return "ssl3: Protocol:\n" + info;
+}
+
+Handshake.decode(buf: array of byte): (ref Handshake, string)
+{
+ m : ref Handshake;
+ e : string;
+
+ a := buf[4:]; # ignore msg length
+
+ i := 0;
+ case int buf[0] {
+ SSL_HANDSHAKE_HELLO_REQUEST =>
+ m = ref Handshake.HelloRequest();
+
+ SSL_HANDSHAKE_CLIENT_HELLO =>
+ if(len a < 38) {
+ e = "client hello: unexpected message";
+ break;
+ }
+ cv := a[i:i+2];
+ i += 2;
+ rd := a[i:i+32];
+ i += 32;
+ lsi := int a[i++];
+ if(len a < 38 + lsi) {
+ e = "client hello: unexpected message";
+ break;
+ }
+ sid: array of byte;
+ if(lsi != 0) {
+ sid = a[i:i+lsi];
+ i += lsi;
+ }
+ else
+ sid = nil;
+ lcs := int_decode(a[i:i+2]);
+ i += 2;
+ if((lcs & 1) || lcs < 2 || len a < 40 + lsi + lcs) {
+ e = "client hello: unexpected message";
+ break;
+ }
+ cs := array [lcs/2] of byte;
+ cs = a[i:i+lcs];
+ i += lcs;
+ lcm := int a[i++];
+ cr := a[i:i+lcm];
+ i += lcm;
+ # In the interest of forward compatibility, it is
+ # permitted for a client hello message to include
+ # extra data after the compression methods. This
+ # data must be included in the handshake hashes,
+ # but otherwise be ignored.
+ # if(i != len a) {
+ # e = "client hello: unexpected message";
+ # break;
+ # }
+ m = ref Handshake.ClientHello(cv, rd, sid, cs, cr);
+
+ SSL_HANDSHAKE_SERVER_HELLO =>
+ if(len a < 38) {
+ e = "server hello: unexpected message";
+ break;
+ }
+ sv := a[i:i+2];
+ i += 2;
+ rd := a[i:i+32];
+ i += 32;
+ lsi := int a[i++];
+ if(len a < 38 + lsi) {
+ e = "server hello: unexpected message";
+ break;
+ }
+ sid : array of byte;
+ if(lsi != 0) {
+ sid = a[i:i+lsi];
+ i += lsi;
+ }
+ else
+ sid = nil;
+ cs := a[i:i+2];
+ i += 2;
+ cr := a[i++];
+ if(i != len a) {
+ e = "server hello: unexpected message";
+ break;
+ }
+ m = ref Handshake.ServerHello(sv, rd, sid, cs, cr);
+
+ SSL_HANDSHAKE_CERTIFICATE =>
+ n := int_decode(a[i:i+3]);
+ i += 3;
+ if(len a != n + 3) {
+ e = "certificate: unexpected message";
+ break;
+ }
+ cl : list of array of byte;
+ k : int;
+ while(i < n) {
+ k = int_decode(a[i:i+3]);
+ i += 3;
+ if(k < 0 || i + k > len a) {
+ e = "certificate: unexpected message";
+ break;
+ }
+ cl = a[i:i+k] :: cl;
+ i += k;
+ }
+ if(e != nil)
+ break;
+ m = ref Handshake.Certificate(cl);
+
+ SSL_HANDSHAKE_SERVER_KEY_EXCHANGE =>
+
+ m = ref Handshake.ServerKeyExchange(a[i:]);
+
+ SSL_HANDSHAKE_CERTIFICATE_REQUEST =>
+ ln := int_decode(a[i:i+2]);
+ i += 2;
+ types := a[i:i+ln];
+ i += ln;
+ ln = int_decode(a[i:i+2]);
+ i += 2;
+ auths : list of array of byte;
+ for(j := 0; j < ln; j++) {
+ ln = int_decode(a[i:i+2]);
+ i += 2;
+ auths = a[i:i+ln]::auths;
+ i += ln;
+ }
+ m = ref Handshake.CertificateRequest(types, auths);
+
+ SSL_HANDSHAKE_SERVER_HELLO_DONE =>
+ if(len a != 0) {
+ e = "server hello done: unexpected message";
+ break;
+ }
+ m = ref Handshake.ServerHelloDone();
+
+ SSL_HANDSHAKE_CERTIFICATE_VERIFY =>
+ ln := int_decode(a[i:i+2]);
+ i +=2;
+ sig := a[i:];
+ i += ln;
+ if(i != len a) {
+ e = "certificate verify: unexpected message";
+ break;
+ }
+ m = ref Handshake.CertificateVerify(sig);
+
+ SSL_HANDSHAKE_CLIENT_KEY_EXCHANGE =>
+ m = ref Handshake.ClientKeyExchange(a);
+
+ SSL_HANDSHAKE_FINISHED =>
+ if(len a != Keyring->MD5dlen + Keyring->SHA1dlen) { # 16+20
+ e = "finished: unexpected message";
+ break;
+ }
+ md5_hash := a[i:i+Keyring->MD5dlen];
+ i += Keyring->MD5dlen;
+ sha_hash := a[i:i+Keyring->SHA1dlen];
+ i += Keyring->SHA1dlen;
+ if(i != len a) {
+ e = "finished: unexpected message";
+ break;
+ }
+ m = ref Handshake.Finished(md5_hash, sha_hash);
+
+ * =>
+ e = "unknown message";
+ }
+
+ if(e != nil)
+ return (nil, "Handshake decode: " + e);
+
+ return (m, nil);
+}
+
+Handshake.encode(hm: self ref Handshake): (array of byte, string)
+{
+ a : array of byte;
+ n : int;
+ e : string;
+
+ i := 0;
+ pick m := hm {
+ HelloRequest =>
+ a = array [4] of byte;
+ a[i++] = byte SSL_HANDSHAKE_HELLO_REQUEST;
+ a[i:] = int_encode(n, 3);
+ i += 3;
+ if(i != 4)
+ e = "hello request: wrong message length";
+
+ ClientHello =>
+ lsi := len m.session_id;
+ lcs := len m.suites;
+ if((lcs &1) || lcs < 2) {
+ e = "client hello: cipher suites is not multiple of 2 bytes";
+ break;
+ }
+ lcm := len m.compressions;
+ n = 38 + lsi + lcs + lcm; # 2+32+1+2+1
+ a = array[n+4] of byte;
+ a[i++] = byte SSL_HANDSHAKE_CLIENT_HELLO;
+ a[i:] = int_encode(n, 3);
+ i += 3;
+ a[i:] = m.version;
+ i += 2;
+ a[i:] = m.random;
+ i += 32;
+ a[i++] = byte lsi;
+ if(lsi != 0) {
+ a[i:] = m.session_id;
+ i += lsi;
+ }
+ a[i:] = int_encode(lcs, 2);
+ i += 2;
+ a[i:] = m.suites; # not nil
+ i += lcs;
+ a[i++] = byte lcm;
+ a[i:] = m.compressions; # not nil
+ i += lcm;
+ if(i != n+4)
+ e = "client hello: wrong message length";
+
+ ServerHello =>
+ lsi := len m.session_id;
+ n = 38 + lsi; # 2+32+1+2+1
+ a = array [n+4] of byte;
+ a[i++] = byte SSL_HANDSHAKE_SERVER_HELLO;
+ a[i:] = int_encode(n, 3);
+ i += 3;
+ a[i:] = m.version;
+ i += 2;
+ a[i:] = m.random;
+ i += 32;
+ a[i++] = byte lsi;
+ if(lsi != 0) {
+ a[i:] = m.session_id;
+ i += lsi;
+ }
+ a[i:] = m.suite; # should be verified, not nil
+ i += 2;
+ a[i++] = m.compression; # should be verified, not nil
+ if(i != n+4)
+ e = "server hello: wrong message length";
+
+ Certificate =>
+ cl := m.cert_list;
+ while(cl != nil) {
+ n += 3 + len hd cl;
+ cl = tl cl;
+ }
+ a = array [n+7] of byte;
+ a[i++] = byte SSL_HANDSHAKE_CERTIFICATE;
+ a[i:] = int_encode(n+3, 3); # length of record
+ i += 3;
+ a[i:] = int_encode(n, 3); # total length of cert chain
+ i += 3;
+ cl = m.cert_list;
+ while(cl != nil) {
+ a[i:] = int_encode(len hd cl, 3);
+ i += 3;
+ a[i:] = hd cl;
+ i += len hd cl;
+ cl = tl cl;
+ }
+ if(i != n+7)
+ e = "certificate: wrong message length";
+
+ ServerKeyExchange =>
+ n = len m.xkey;
+ a = array [n+4] of byte;
+ a[i++] = byte SSL_HANDSHAKE_SERVER_KEY_EXCHANGE;
+ a[i:] = int_encode(n, 3);
+ i += 3;
+ a[i:] = m.xkey;
+ i += len m.xkey;
+ if(i != n+4)
+ e = "server key exchange: wrong message length";
+
+ CertificateRequest =>
+ ntypes := len m.cert_types;
+ nauths := len m.dn_list;
+ n = 1 + ntypes;
+ dl := m.dn_list;
+ while(dl != nil) {
+ n += 2 + len hd dl;
+ dl = tl dl;
+ }
+ n += 2;
+ a = array [n+4] of byte;
+ a[i++] = byte SSL_HANDSHAKE_CERTIFICATE_REQUEST;
+ a[i:] = int_encode(n, 3);
+ i += 3;
+ a[i++] = byte ntypes;
+ a[i:] = m.cert_types;
+ i += ntypes;
+ a[i:] = int_encode(nauths, 2);
+ i += 2;
+ dl = m.dn_list;
+ while(dl != nil) {
+ a[i:] = int_encode(len hd dl, 2);
+ i += 2;
+ a[i:] = hd dl;
+ i += len hd dl;
+ dl = tl dl;
+ }
+ if(i != n+4)
+ e = "certificate request: wrong message length";
+
+ ServerHelloDone =>
+ n = 0;
+ a = array[n+4] of byte;
+ a[i++] = byte SSL_HANDSHAKE_SERVER_HELLO_DONE;
+ a[i:] = int_encode(0, 3); # message has 0 length
+ i += 3;
+ if(i != n+4)
+ e = "server hello done: wrong message length";
+
+ CertificateVerify =>
+ n = 2 + len m.signature;
+ a = array [n+4] of byte;
+ a[i++] = byte SSL_HANDSHAKE_CERTIFICATE_VERIFY;
+ a[i:] = int_encode(n, 3);
+ i += 3;
+ a[i:] = int_encode(n-2, 2);
+ i += 2;
+ a[i:] = m.signature;
+ i += n-2;
+ if(i != n+4)
+ e = "certificate verify: wrong message length";
+
+ ClientKeyExchange =>
+ n = len m.xkey;
+ a = array [n+4] of byte;
+ a[i++] = byte SSL_HANDSHAKE_CLIENT_KEY_EXCHANGE;
+ a[i:] = int_encode(n, 3);
+ i += 3;
+ a[i:] = m.xkey;
+ i += n;
+ if(i != n+4)
+ e = "client key exchange: wrong message length";
+
+ Finished =>
+ n = len m.md5_hash + len m.sha_hash;
+ a = array [n+4] of byte;
+ a[i++] = byte SSL_HANDSHAKE_FINISHED;
+ a[i:] = int_encode(n, 3);
+ i += 3;
+ a[i:] = m.md5_hash;
+ i += len m.md5_hash;
+ a[i:] = m.sha_hash;
+ i += len m.sha_hash;
+ if(i != n+4)
+ e = "finished: wrong message length";
+
+ * =>
+ e = "unknown message";
+ }
+
+ if(e != nil)
+ return (nil, "Handshake encode: " + e);
+
+ return (a, e);
+}
+
+Handshake.tostring(handshake: self ref Handshake): string
+{
+ info: string;
+
+ pick m := handshake {
+ HelloRequest =>
+ info = "\tHelloRequest\n";
+
+ ClientHello =>
+ info = "\tClientHello\n" +
+ "\tversion = \n\t\t" + bastr(m.version) + "\n" +
+ "\trandom = \n\t\t" + bastr(m.random) + "\n" +
+ "\tsession_id = \n\t\t" + bastr(m.session_id) + "\n" +
+ "\tsuites = \n\t\t" + bastr(m.suites) + "\n" +
+ "\tcomperssion_methods = \n\t\t" + bastr(m.compressions) +"\n";
+
+ ServerHello =>
+ info = "\tServerHello\n" +
+ "\tversion = \n\t\t" + bastr(m.version) + "\n" +
+ "\trandom = \n\t\t" + bastr(m.random) + "\n" +
+ "\tsession_id = \n\t\t" + bastr(m.session_id) + "\n" +
+ "\tsuite = \n\t\t" + bastr(m.suite) + "\n" +
+ "\tcomperssion_method = \n\t\t" + string m.compression +"\n";
+
+ Certificate =>
+ info = "\tCertificate\n" +
+ "\tcert_list = \n\t\t" + lbastr(m.cert_list) + "\n";
+
+ ServerKeyExchange =>
+ info = "\tServerKeyExchange\n" +
+ "\txkey = \n\t\t" + bastr(m.xkey) +"\n";
+
+ CertificateRequest =>
+ info = "\tCertificateRequest\n" +
+ "\tcert_types = \n\t\t" + bastr(m.cert_types) + "\n" +
+ "\tdn_list = \n\t\t" + lbastr(m.dn_list) + "\n";
+
+ ServerHelloDone =>
+ info = "\tServerDone\n";
+
+ CertificateVerify =>
+ info = "\tCertificateVerify\n" +
+ "\tsignature = \n\t\t" + bastr(m.signature) + "\n";
+
+ ClientKeyExchange =>
+ info = "\tClientKeyExchange\n" +
+ "\txkey = \n\t\t" + bastr(m.xkey) +"\n";
+
+ Finished =>
+ info = "\tFinished\n" +
+ "\tmd5_hash = \n\t\t" + bastr(m.md5_hash) + "\n" +
+ "\tsha_hash = \n\t\t" + bastr(m.sha_hash) + "\n";
+ }
+
+ return info;
+}
+
+Alert.tostring(alert: self ref Alert): string
+{
+ info: string;
+
+ case alert.level {
+ SSL_WARNING =>
+ info += "\t\twarning: ";
+
+ SSL_FATAL =>
+ info += "\t\tfatal: ";
+
+ * =>
+ info += sys->sprint("unknown alert level[%d]: ", alert.level);
+ }
+
+ case alert.description {
+ SSL_CLOSE_NOTIFY =>
+ info += "close notify";
+
+ SSL_NO_CERTIFICATE =>
+ info += "no certificate";
+
+ SSL_BAD_CERTIFICATE =>
+ info += "bad certificate";
+
+ SSL_UNSUPPORTED_CERTIFICATE =>
+ info += "unsupported certificate";
+
+ SSL_CERTIFICATE_REVOKED =>
+ info += "certificate revoked";
+
+ SSL_CERTIFICATE_EXPIRED =>
+ info += "certificate expired";
+
+ SSL_CERTIFICATE_UNKNOWN =>
+ info += "certificate unknown";
+
+ SSL_UNEXPECTED_MESSAGE =>
+ info += "unexpected message";
+
+ SSL_BAD_RECORD_MAC =>
+ info += "bad record mac";
+
+ SSL_DECOMPRESSION_FAILURE =>
+ info += "decompression failure";
+
+ SSL_HANDSHAKE_FAILURE =>
+ info += "handshake failure";
+
+ SSL_ILLEGAL_PARAMETER =>
+ info += "illegal parameter";
+
+ * =>
+ info += sys->sprint("unknown alert description[%d]", alert.description);
+ }
+
+ return info;
+}
+
+find_cipher_suite(s, suites: array of byte) : array of byte
+{
+ i, j : int;
+ a, b : array of byte;
+
+ n := len s;
+ if((n & 1) || n < 2)
+ return nil;
+
+ m := len suites;
+ if((m & 1) || m < 2)
+ return nil;
+
+ for(i = 0; i < n; ) {
+ a = s[i:i+2];
+ i += 2;
+ for(j = 0; j < m; ) {
+ b = suites[j:j+2];
+ j += 2;
+ if(a[0] == b[0] && a[1] == b[1])
+ return b;
+ }
+ }
+
+ return nil;
+}
+
+#
+# cipher suites and specs
+#
+suite_to_spec(cs: array of byte, cipher_suites: array of array of byte)
+ : (ref CipherSpec, ref KeyExAlg, ref SigAlg, string)
+{
+ cip : ref CipherSpec;
+ kex : ref KeyExAlg;
+ sig : ref SigAlg;
+
+ n := len cipher_suites;
+ i : int;
+ found := array [2] of byte;
+ for(i = 0; i < n; i++) {
+ found = cipher_suites[i];
+ if(found[0]==cs[0] && found[1]==cs[1]) break;
+ }
+
+ if(i == n)
+ return (nil, nil, nil, "fail to find a matched spec");
+
+ case i {
+ NULL_WITH_NULL_NULL =>
+ cip = ref CipherSpec(SSL_EXPORT_TRUE, SSL_NULL_CIPHER,
+ SSL_STREAM_CIPHER, 0, 0, SSL_NULL_MAC, 0);
+ kex = ref KeyExAlg.NULL();
+ sig = ref SigAlg.anon();
+
+ RSA_WITH_NULL_MD5 => # sign only certificate
+ cip = ref CipherSpec(SSL_EXPORT_TRUE, SSL_NULL_CIPHER,
+ SSL_STREAM_CIPHER, 0, 0, SSL_MD5, Keyring->MD5dlen);
+ kex = ref KeyExAlg.RSA(nil, nil, nil);
+ sig = ref SigAlg.anon();
+
+ RSA_WITH_NULL_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_TRUE, SSL_NULL_CIPHER,
+ SSL_STREAM_CIPHER, 0, 0, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.RSA(nil, nil, nil);
+ sig = ref SigAlg.anon();
+
+ RSA_EXPORT_WITH_RC4_40_MD5 =>
+ cip = ref CipherSpec(SSL_EXPORT_TRUE, SSL_RC4,
+ SSL_STREAM_CIPHER, 5, 0, SSL_MD5, Keyring->MD5dlen);
+ kex = ref KeyExAlg.RSA(nil, nil, nil);
+ sig = ref SigAlg.anon();
+
+ RSA_WITH_RC4_128_MD5 =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_RC4,
+ SSL_STREAM_CIPHER, 16, 0, SSL_MD5, Keyring->MD5dlen);
+ kex = ref KeyExAlg.RSA(nil, nil, nil);
+ sig = ref SigAlg.anon();
+
+ RSA_WITH_RC4_128_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_RC4,
+ SSL_STREAM_CIPHER, 16, 0, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.RSA(nil, nil, nil);
+ sig = ref SigAlg.anon();
+
+ RSA_EXPORT_WITH_RC2_CBC_40_MD5 =>
+ cip = ref CipherSpec(SSL_EXPORT_TRUE, SSL_RC2_CBC,
+ SSL_BLOCK_CIPHER, 5, 8, SSL_MD5, Keyring->MD5dlen);
+ kex = ref KeyExAlg.RSA(nil, nil, nil);
+ sig = ref SigAlg.RSA(nil, nil);
+
+ RSA_WITH_IDEA_CBC_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_IDEA_CBC,
+ SSL_BLOCK_CIPHER, 16, 8, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.RSA(nil, nil, nil);
+ sig = ref SigAlg.anon();
+
+ RSA_EXPORT_WITH_DES40_CBC_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_TRUE, SSL_DES_CBC,
+ SSL_BLOCK_CIPHER, 5, 8, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.RSA(nil, nil, nil);
+ sig = ref SigAlg.RSA(nil, nil);
+
+ RSA_WITH_DES_CBC_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_DES_CBC,
+ SSL_BLOCK_CIPHER, 8, 8, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.RSA(nil, nil, nil);
+ sig = ref SigAlg.anon();
+
+ RSA_WITH_3DES_EDE_CBC_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_3DES_EDE_CBC,
+ SSL_BLOCK_CIPHER, 24, 8, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.RSA(nil, nil, nil);
+ sig = ref SigAlg.anon();
+
+ DH_DSS_EXPORT_WITH_DES40_CBC_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_TRUE, SSL_DES_CBC,
+ SSL_BLOCK_CIPHER, 5, 8, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.DH(nil, nil, nil, nil, nil);
+ sig = ref SigAlg.DSS(nil, nil);
+
+ DH_DSS_WITH_DES_CBC_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_DES_CBC,
+ SSL_BLOCK_CIPHER, 8, 8, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.DH(nil, nil, nil, nil, nil);
+ sig = ref SigAlg.DSS(nil, nil);
+
+ DH_DSS_WITH_3DES_EDE_CBC_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_3DES_EDE_CBC,
+ SSL_BLOCK_CIPHER, 24, 8, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.DH(nil, nil, nil, nil, nil);
+ sig = ref SigAlg.DSS(nil, nil);
+
+ DH_RSA_EXPORT_WITH_DES40_CBC_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_TRUE, SSL_DES_CBC,
+ SSL_BLOCK_CIPHER, 5, 8, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.DH(nil, nil, nil, nil, nil);
+ sig = ref SigAlg.RSA(nil, nil);
+
+ DH_RSA_WITH_DES_CBC_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_DES_CBC,
+ SSL_STREAM_CIPHER, 8, 8, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.DH(nil, nil, nil, nil, nil);
+ sig = ref SigAlg.RSA(nil, nil);
+
+ DH_RSA_WITH_3DES_EDE_CBC_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_3DES_EDE_CBC,
+ SSL_BLOCK_CIPHER, 24, 8, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.DH(nil, nil, nil, nil, nil);
+ sig = ref SigAlg.RSA(nil, nil);
+
+ DHE_DSS_EXPORT_WITH_DES40_CBC_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_DES_CBC,
+ SSL_BLOCK_CIPHER, 5, 8, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.DH(nil, nil, nil, nil, nil);
+ sig = ref SigAlg.DSS(nil, nil);
+
+ DHE_DSS_WITH_DES_CBC_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_DES_CBC,
+ SSL_BLOCK_CIPHER, 8, 8, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.DH(nil, nil, nil, nil, nil);
+ sig = ref SigAlg.DSS(nil, nil);
+
+ DHE_DSS_WITH_3DES_EDE_CBC_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_3DES_EDE_CBC,
+ SSL_BLOCK_CIPHER, 24, 8, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.DH(nil, nil, nil, nil, nil);
+ sig = ref SigAlg.DSS(nil, nil);
+
+ DHE_RSA_EXPORT_WITH_DES40_CBC_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_TRUE, SSL_DES_CBC,
+ SSL_BLOCK_CIPHER, 5, 8, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.DH(nil, nil, nil, nil, nil);
+ sig = ref SigAlg.RSA(nil, nil);
+
+ DHE_RSA_WITH_DES_CBC_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_DES_CBC,
+ SSL_BLOCK_CIPHER, 8, 8, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.DH(nil, nil, nil, nil, nil);
+ sig = ref SigAlg.RSA(nil, nil);
+
+ DHE_RSA_WITH_3DES_EDE_CBC_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_3DES_EDE_CBC,
+ SSL_BLOCK_CIPHER, 24, 8, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.DH(nil, nil, nil, nil, nil);
+ sig = ref SigAlg.RSA(nil, nil);
+
+ DH_anon_EXPORT_WITH_RC4_40_MD5 =>
+ cip = ref CipherSpec(SSL_EXPORT_TRUE, SSL_RC4,
+ SSL_STREAM_CIPHER, 5, 0, SSL_MD5, Keyring->MD5dlen);
+ kex = ref KeyExAlg.DH(nil, nil, nil, nil, nil);
+ sig = ref SigAlg.anon();
+
+ DH_anon_WITH_RC4_128_MD5 =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_RC4,
+ SSL_STREAM_CIPHER, 16, 0, SSL_MD5, Keyring->MD5dlen);
+ kex = ref KeyExAlg.DH(nil, nil, nil, nil, nil);
+ sig = ref SigAlg.anon();
+
+ DH_anon_EXPORT_WITH_DES40_CBC_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_TRUE, SSL_DES_CBC,
+ SSL_BLOCK_CIPHER, 5, 8, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.DH(nil, nil, nil, nil, nil);
+ sig = ref SigAlg.anon();
+
+ DH_anon_WITH_DES_CBC_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_DES_CBC,
+ SSL_BLOCK_CIPHER, 8, 8, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.DH(nil, nil, nil, nil, nil);
+ sig = ref SigAlg.anon();
+
+ DH_anon_WITH_3DES_EDE_CBC_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_3DES_EDE_CBC,
+ SSL_BLOCK_CIPHER, 24, 8, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.DH(nil, nil, nil, nil, nil);
+ sig = ref SigAlg.anon();
+
+ FORTEZZA_KEA_WITH_NULL_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_NULL_CIPHER,
+ SSL_STREAM_CIPHER, 0, 0, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.FORTEZZA_KEA();
+ sig = ref SigAlg.FORTEZZA_KEA();
+
+ FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_FORTEZZA_CBC,
+ SSL_BLOCK_CIPHER, 0, 0, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.FORTEZZA_KEA();
+ sig = ref SigAlg.FORTEZZA_KEA();
+
+ FORTEZZA_KEA_WITH_RC4_128_SHA =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_RC4,
+ SSL_STREAM_CIPHER, 16, 0, SSL_SHA, Keyring->SHA1dlen);
+ kex = ref KeyExAlg.FORTEZZA_KEA();
+ sig = ref SigAlg.FORTEZZA_KEA();
+
+ }
+
+ return (cip, kex, sig, nil);
+}
+
+#
+# use suites as default SSL3_Suites
+#
+cipher_suite_info(cs: array of byte, suites: array of array of byte) : string
+{
+ tag : string;
+
+ a := array [2] of byte;
+ n := len suites;
+ for(i := 0; i < n; i++) {
+ a = suites[i];
+ if(a[0]==cs[0] && a[1]==cs[1]) break;
+ }
+
+ if(i == n)
+ return "unknown cipher suite [" + string cs + "]";
+
+ case i {
+ NULL_WITH_NULL_NULL =>
+ tag = "NULL_WITH_NULL_NULL";
+
+ RSA_WITH_NULL_MD5 =>
+ tag = "RSA_WITH_NULL_MD5";
+
+ RSA_WITH_NULL_SHA =>
+ tag = "RSA_WITH_NULL_SHA";
+
+ RSA_EXPORT_WITH_RC4_40_MD5 =>
+ tag = "RSA_EXPORT_WITH_RC4_40_MD5";
+
+ RSA_WITH_RC4_128_MD5 =>
+ tag = "RSA_WITH_RC4_128_MD5";
+
+ RSA_WITH_RC4_128_SHA =>
+ tag = "RSA_WITH_RC4_128_SHA";
+
+ RSA_EXPORT_WITH_RC2_CBC_40_MD5 =>
+ tag = "RSA_EXPORT_WITH_RC2_CBC_40_MD5";
+
+ RSA_WITH_IDEA_CBC_SHA =>
+ tag = "RSA_WITH_IDEA_CBC_SHA";
+
+ RSA_EXPORT_WITH_DES40_CBC_SHA =>
+ tag ="RSA_EXPORT_WITH_DES40_CBC_SHA";
+
+ RSA_WITH_DES_CBC_SHA =>
+ tag = "RSA_WITH_DES_CBC_SHA";
+
+ RSA_WITH_3DES_EDE_CBC_SHA =>
+ tag = "RSA_WITH_3DES_EDE_CBC_SHA";
+
+ DH_DSS_EXPORT_WITH_DES40_CBC_SHA =>
+ tag = "DH_DSS_EXPORT_WITH_DES40_CBC_SHA";
+
+ DH_DSS_WITH_DES_CBC_SHA =>
+ tag = "DH_DSS_WITH_DES_CBC_SHA";
+
+ DH_DSS_WITH_3DES_EDE_CBC_SHA =>
+ tag = "DH_DSS_WITH_3DES_EDE_CBC_SHA";
+
+ DH_RSA_EXPORT_WITH_DES40_CBC_SHA =>
+ tag = "DH_RSA_EXPORT_WITH_DES40_CBC_SHA";
+
+ DH_RSA_WITH_DES_CBC_SHA =>
+ tag = "DH_RSA_WITH_DES_CBC_SHA";
+
+ DH_RSA_WITH_3DES_EDE_CBC_SHA =>
+ tag = "DH_RSA_WITH_3DES_EDE_CBC_SHA";
+
+ DHE_DSS_EXPORT_WITH_DES40_CBC_SHA =>
+ tag = "DHE_DSS_EXPORT_WITH_DES40_CBC_SHA";
+
+ DHE_DSS_WITH_DES_CBC_SHA =>
+ tag = "DHE_DSS_WITH_DES_CBC_SHA";
+
+ DHE_DSS_WITH_3DES_EDE_CBC_SHA =>
+ tag = "DHE_DSS_WITH_3DES_EDE_CBC_SHA";
+
+ DHE_RSA_EXPORT_WITH_DES40_CBC_SHA =>
+ tag = "DHE_RSA_EXPORT_WITH_DES40_CBC_SHA";
+
+ DHE_RSA_WITH_DES_CBC_SHA =>
+ tag = "DHE_RSA_WITH_DES_CBC_SHA";
+
+ DHE_RSA_WITH_3DES_EDE_CBC_SHA =>
+ tag = "DHE_RSA_WITH_3DES_EDE_CBC_SHA";
+
+ DH_anon_EXPORT_WITH_RC4_40_MD5 =>
+ tag = "DH_anon_EXPORT_WITH_RC4_40_MD5";
+
+ DH_anon_WITH_RC4_128_MD5 =>
+ tag = "DH_anon_WITH_RC4_128_MD5";
+
+ DH_anon_EXPORT_WITH_DES40_CBC_SHA =>
+ tag = "DH_anon_EXPORT_WITH_DES40_CBC_SHA";
+
+ DH_anon_WITH_DES_CBC_SHA =>
+ tag = "DH_anon_WITH_DES_CBC_SHA";
+
+ DH_anon_WITH_3DES_EDE_CBC_SHA =>
+ tag = "DH_anon_WITH_3DES_EDE_CBC_SHA";
+
+ FORTEZZA_KEA_WITH_NULL_SHA =>
+ tag = "FORTEZZA_KEA_WITH_NULL_SHA";
+
+ FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA =>
+ tag = "FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA";
+
+ FORTEZZA_KEA_WITH_RC4_128_SHA =>
+ tag = "FORTEZZA_KEA_WITH_RC4_128_SHA";
+ }
+
+ return "cipher suite = [" + tag + "]";
+}
+
+#################################
+## FOR SSLv2 BACKWARD COMPATIBLE
+#################################
+
+# Protocol Version Codes
+SSL2_CLIENT_VERSION := array [] of {byte 0, byte 16r02};
+SSL2_SERVER_VERSION := array [] of {byte 0, byte 16r02};
+
+# Protocol Message Codes
+
+SSL2_MT_ERROR,
+ SSL2_MT_CLIENT_HELLO,
+ SSL2_MT_CLIENT_MASTER_KEY,
+ SSL2_MT_CLIENT_FINISHED,
+ SSL2_MT_SERVER_HELLO,
+ SSL2_MT_SERVER_VERIFY,
+ SSL2_MT_SERVER_FINISHED,
+ SSL2_MT_REQUEST_CERTIFICATE,
+ SSL2_MT_CLIENT_CERTIFICATE : con iota;
+
+# Error Message Codes
+
+SSL2_PE_NO_CIPHER := array [] of {byte 0, byte 16r01};
+SSL2_PE_NO_CERTIFICATE := array [] of {byte 0, byte 16r02};
+SSL2_PE_BAD_CERTIFICATE := array [] of {byte 0, byte 16r04};
+SSL2_PE_UNSUPPORTED_CERTIFICATE_TYPE := array [] of {byte 0, byte 16r06};
+
+# Cipher Kind Values
+
+SSL2_CK_RC4_128_WITH_MD5,
+ SSL2_CK_RC4_128_EXPORT40_WITH_MD5,
+ SSL2_CK_RC2_CBC_128_CBC_WITH_MD5,
+ SSL2_CK_RC2_CBC_128_CBC_EXPORT40_WITH_MD5,
+ SSL2_CK_IDEA_128_CBC_WITH_MD5,
+ SSL2_CK_DES_64_CBC_WITH_MD5,
+ SSL2_CK_DES_192_EDE3_CBC_WITH_MD5 : con iota;
+
+SSL2_Cipher_Kinds := array [] of {
+ SSL2_CK_RC4_128_WITH_MD5 => array [] of {byte 16r01, byte 0, byte 16r80},
+ SSL2_CK_RC4_128_EXPORT40_WITH_MD5 => array [] of {byte 16r02, byte 0, byte 16r80},
+ SSL2_CK_RC2_CBC_128_CBC_WITH_MD5 => array [] of {byte 16r03, byte 0, byte 16r80},
+ SSL2_CK_RC2_CBC_128_CBC_EXPORT40_WITH_MD5 =>
+ array [] of {byte 16r04, byte 0, byte 16r80},
+ SSL2_CK_IDEA_128_CBC_WITH_MD5 => array [] of {byte 16r05, byte 0, byte 16r80},
+ SSL2_CK_DES_64_CBC_WITH_MD5 => array [] of {byte 16r06, byte 0, byte 16r40},
+ SSL2_CK_DES_192_EDE3_CBC_WITH_MD5 => array [] of {byte 16r07, byte 0, byte 16rC0},
+};
+
+# Certificate Type Codes
+
+SSL2_CT_X509_CERTIFICATE : con 16r01; # encode as one byte
+
+# Authentication Type Codes
+
+SSL2_AT_MD5_WITH_RSA_ENCRYPTION : con byte 16r01;
+
+# Upper/Lower Bounds
+
+SSL2_MAX_MASTER_KEY_LENGTH_IN_BITS : con 256;
+SSL2_MAX_SESSION_ID_LENGTH_IN_BYTES : con 16;
+SSL2_MIN_RSA_MODULUS_LENGTH_IN_BYTES : con 64;
+SSL2_MAX_RECORD_LENGTH_2_BYTE_HEADER : con 32767;
+SSL2_MAX_RECORD_LENGTH_3_BYTE_HEADER : con 16383;
+
+# Handshake Internal State
+
+SSL2_STATE_CLIENT_HELLO,
+ SSL2_STATE_SERVER_HELLO,
+ SSL2_STATE_CLIENT_MASTER_KEY,
+ SSL2_STATE_SERVER_VERIFY,
+ SSL2_STATE_REQUEST_CERTIFICATE,
+ SSL2_STATE_CLIENT_CERTIFICATE,
+ SSL2_STATE_CLIENT_FINISHED,
+ SSL2_STATE_SERVER_FINISHED,
+ SSL2_STATE_ERROR : con iota;
+
+# The client's challenge to the server for the server to identify itself is a
+# (near) arbitary length random. The v3 server will right justify the challenge
+# data to become the ClientHello.random data (padding with leading zeros, if
+# necessary). If the length of the challenge is greater than 32 bytes, then only
+# the last 32 bytes are used. It is legitimate (but not necessary) for a v3
+# server to reject a v2 ClientHello that has fewer than 16 bytes of challenge
+# data.
+
+SSL2_CHALLENGE_LENGTH : con 16;
+
+V2Handshake: adt {
+ pick {
+ Error =>
+ code : array of byte; # [2];
+ ClientHello =>
+ version : array of byte; # [2]
+ cipher_specs : array of byte; # [3] x
+ session_id : array of byte;
+ challenge : array of byte;
+ ServerHello =>
+ session_id_hit : int;
+ certificate_type : int;
+ version : array of byte; # [2]
+ certificate : array of byte; # only user certificate
+ cipher_specs : array of byte; # [3] x
+ connection_id : array of byte;
+ ClientMasterKey =>
+ cipher_kind : array of byte; # [3]
+ clear_key : array of byte;
+ encrypt_key : array of byte;
+ key_arg : array of byte;
+ ServerVerify =>
+ challenge : array of byte;
+ RequestCertificate =>
+ authentication_type : int;
+ certificate_challenge : array of byte;
+ ClientCertificate =>
+ certificate_type : int;
+ certificate : array of byte; # only user certificate
+ response : array of byte;
+ ClientFinished =>
+ connection_id : array of byte;
+ ServerFinished =>
+ session_id : array of byte;
+ }
+
+ encode: fn(hm: self ref V2Handshake): (array of byte, string);
+ decode: fn(a: array of byte): (ref V2Handshake, string);
+ tostring: fn(h: self ref V2Handshake): string;
+};
+
+
+V2Handshake.tostring(handshake: self ref V2Handshake): string
+{
+ info := "";
+
+ pick m := handshake {
+ ClientHello =>
+ info += "\tClientHello\n" +
+ "\tversion = \n\t\t" + bastr(m.version) + "\n" +
+ "\tcipher_specs = \n\t\t" + bastr(m.cipher_specs) + "\n" +
+ "\tsession_id = \n\t\t" + bastr(m.session_id) + "\n" +
+ "\tchallenge = \n\t\t" + bastr(m.challenge) + "\n";
+
+ ServerHello =>
+ info += "\tServerHello\n" +
+ "\tsession_id_hit = \n\t\t" + string m.session_id_hit + "\n" +
+ "\tcertificate_type = \n\t\t" + string m.certificate_type + "\n" +
+ "\tversion = \n\t\t" + bastr(m.version) + "\n" +
+ "\tcertificate = \n\t\t" + bastr(m.certificate) + "\n" +
+ "\tcipher_specs = \n\t\t" + bastr(m.cipher_specs) + "\n" +
+ "\tconnection_id = \n\t\t" + bastr(m.connection_id) + "\n";
+
+ ClientMasterKey =>
+ info += "\tClientMasterKey\n" +
+ "\tcipher_kind = \n\t\t" + bastr(m.cipher_kind) + "\n" +
+ "\tclear_key = \n\t\t" + bastr(m.clear_key) + "\n" +
+ "\tencrypt_key = \n\t\t" + bastr(m.encrypt_key) + "\n" +
+ "\tkey_arg = \n\t\t" + bastr(m.key_arg) + "\n";
+
+ ServerVerify =>
+ info += "\tServerVerify\n" +
+ "\tchallenge = \n\t\t" + bastr(m.challenge) + "\n";
+
+ RequestCertificate =>
+ info += "\tRequestCertificate\n" +
+ "\tauthentication_type = \n\t\t" + string m.authentication_type + "\n" +
+ "\tcertificate_challenge = \n\t\t" + bastr(m.certificate_challenge) + "\n";
+
+ ClientCertificate =>
+ info += "ClientCertificate\n" +
+ "\tcertificate_type = \n\t\t" + string m.certificate_type + "\n" +
+ "\tcertificate = \n\t\t" + bastr(m.certificate) + "\n" +
+ "\tresponse = \n\t\t" + bastr(m.response) + "\n";
+
+ ClientFinished =>
+ info += "\tClientFinished\n" +
+ "\tconnection_id = \n\t\t" + bastr(m.connection_id) + "\n";
+
+ ServerFinished =>
+ info += "\tServerFinished\n" +
+ "\tsession_id = \n\t\t" + bastr(m.session_id) + "\n";
+ }
+
+ return info;
+}
+
+
+# v2 handshake protocol - message driven, v2 and v3 sharing the same context stack
+
+do_v2handshake(v2hs: ref V2Handshake, ctx: ref Context): string
+{
+ e: string = nil;
+
+ pick h := v2hs {
+ Error =>
+ do_v2error(h, ctx);
+
+ ClientHello =>
+ if((ctx.status & CLIENT_SIDE) || ctx.state != SSL2_STATE_CLIENT_HELLO) {
+ e = "V2ClientHello";
+ break;
+ }
+ do_v2client_hello(h, ctx);
+
+ ServerHello =>
+ if(!(ctx.status & CLIENT_SIDE) || ctx.state != SSL2_STATE_SERVER_HELLO) {
+ e = "V2ServerHello";
+ break;
+ }
+ do_v2server_hello(h, ctx);
+
+ ClientMasterKey =>
+ if((ctx.status & CLIENT_SIDE) || ctx.state != SSL2_STATE_CLIENT_MASTER_KEY) {
+ e = "V2ClientMasterKey";
+ break;
+ }
+ do_v2client_master_key(h, ctx);
+
+ ServerVerify =>
+ if(!(ctx.status & CLIENT_SIDE) || ctx.state != SSL2_STATE_SERVER_VERIFY) {
+ e = "V2ServerVerify";
+ break;
+ }
+ do_v2server_verify(h, ctx);
+
+ RequestCertificate =>
+ if(!(ctx.status & CLIENT_SIDE) || ctx.state != SSL2_STATE_SERVER_VERIFY) {
+ e = "V2RequestCertificate";
+ break;
+ }
+ do_v2req_cert(h, ctx);
+
+ ClientCertificate =>
+ if((ctx.status & CLIENT_SIDE) || ctx.state != SSL2_STATE_CLIENT_CERTIFICATE) {
+ e = "V2ClientCertificate";
+ break;
+ }
+ do_v2client_certificate(h, ctx);
+
+ ClientFinished =>
+ if((ctx.status & CLIENT_SIDE) || ctx.state != SSL2_STATE_CLIENT_FINISHED) {
+ e = "V2ClientFinished";
+ break;
+ }
+ do_v2client_finished(h, ctx);
+
+ ServerFinished =>
+ if(!(ctx.status & CLIENT_SIDE) || ctx.state != SSL2_STATE_SERVER_FINISHED) {
+ e = "V2ServerFinished";
+ break;
+ }
+ do_v2server_finished(h, ctx);
+ }
+
+ return e;
+}
+
+do_v2error(v2hs: ref V2Handshake.Error, ctx: ref Context)
+{
+ if(SSL_DEBUG)
+ log("do_v2error: " + string v2hs.code);
+ ctx.state = STATE_EXIT;
+}
+
+# [server side]
+do_v2client_hello(v2hs: ref V2Handshake.ClientHello, ctx: ref Context)
+{
+ if(v2hs.version[0] != SSL2_CLIENT_VERSION[0] || v2hs.version[1] != SSL2_CLIENT_VERSION[1]) {
+ # promote this message to v3 handshake protocol
+ ctx.state = STATE_CLIENT_HELLO;
+ return;
+ }
+
+ # trying to resume
+ s: ref Session;
+ if((v2hs.session_id != nil) && (ctx.status & SESSION_RESUMABLE))
+ s = sslsession->get_session_byid(v2hs.session_id);
+ if(s != nil) { # found a hit
+ # prepare and send v2 handshake hello message
+ v2handshake_enque(
+ ref V2Handshake.ServerHello(
+ 1, # hit found
+ 0, # no certificate required
+ SSL2_SERVER_VERSION,
+ nil, # no authetication required
+ s.suite, # use hit session cipher kind
+ ctx.server_random # connection_id
+ ),
+ ctx
+ );
+ # TODO: should in supported cipher_kinds
+ err: string;
+ (ctx.sel_ciph, ctx.sel_keyx, ctx.sel_sign, err)
+ = v2suite_to_spec(ctx.session.suite, SSL2_Cipher_Kinds);
+ if(err != "") {
+ if(SSL_DEBUG)
+ log("do_v2client_hello: " + err);
+ ctx.state = SSL2_STATE_ERROR;
+ return;
+ }
+ ctx.state = SSL2_STATE_SERVER_FINISHED;
+ }
+ else {
+ # find matching cipher kinds
+ n := len v2hs.cipher_specs;
+ matchs := array [n] of byte;
+ j, k: int = 0;
+ while(j < n) {
+ # ignore those not in SSL2_Cipher_Kinds
+ matchs[k:] = v2hs.cipher_specs[j:j+3];
+ for(i := 0; i < len SSL2_Cipher_Kinds; i++) {
+ ck := SSL2_Cipher_Kinds[i];
+ if(matchs[k] == ck[0] && matchs[k+1] == ck[1] && matchs[k+2] == ck[2])
+ k +=3;
+ }
+ j += 3;
+ }
+ if(k == 0) {
+ if(SSL_DEBUG)
+ log("do_v2client_hello: No matching cipher kind");
+ ctx.state = SSL2_STATE_ERROR;
+ }
+ else {
+ matchs = matchs[0:k];
+
+ # Note:
+ # v2 challenge -> v3 client_random
+ # v2 connection_id -> v3 server_random
+
+ chlen := len v2hs.challenge;
+ if(chlen > 32)
+ chlen = 32;
+ ctx.client_random = array [chlen] of byte;
+ if(chlen > 32)
+ ctx.client_random[0:] = v2hs.challenge[chlen-32:];
+ else
+ ctx.client_random[0:] = v2hs.challenge;
+ ctx.server_random = random->randombuf(Random->NotQuiteRandom, 16);
+ s.session_id = random->randombuf (
+ Random->NotQuiteRandom,
+ SSL2_MAX_SESSION_ID_LENGTH_IN_BYTES
+ );
+ s.suite = matchs;
+ ctx.session = s;
+ v2handshake_enque(
+ ref V2Handshake.ServerHello(
+ 0, # no hit - not resumable
+ SSL2_CT_X509_CERTIFICATE,
+ SSL2_SERVER_VERSION,
+ hd ctx.local_info.certs, # the first is user certificate
+ ctx.session.suite, # matched cipher kinds
+ ctx.server_random # connection_id
+ ),
+ ctx
+ );
+ ctx.state = SSL2_STATE_CLIENT_MASTER_KEY;
+ }
+ }
+}
+
+# [client side]
+
+do_v2server_hello(v2hs: ref V2Handshake.ServerHello, ctx: ref Context)
+{
+ # must be v2 server hello otherwise it will be v3 server hello
+ # determined by auto record layer version detection
+ if(v2hs.version[0] != SSL2_SERVER_VERSION[0]
+ || v2hs.version[1] != SSL2_SERVER_VERSION[1]) {
+ if(SSL_DEBUG)
+ log("do_v2server_hello: not a v2 version");
+ ctx.state = SSL2_STATE_ERROR;
+ return;
+ }
+
+ ctx.session.version = SSL2_SERVER_VERSION;
+ ctx.server_random = v2hs.connection_id;
+
+ # check if a resumable session is found
+ if(v2hs.session_id_hit != 0) { # resume ok
+ err: string;
+ # TODO: should in supported cipher_kinds
+ (ctx.sel_ciph, nil, nil, err) = v2suite_to_spec(ctx.session.suite, SSL2_Cipher_Kinds);
+ if(err != "") {
+ if(SSL_DEBUG)
+ log("do_v2server_hello: " + err);
+ ctx.state = SSL2_STATE_ERROR;
+ return;
+ }
+ }
+ else { # not resumable session
+
+ # use the first matched cipher kind; install cipher spec
+ if(len v2hs.cipher_specs < 3) {
+ if(SSL_DEBUG)
+ log("do_v2server_hello: too few bytes");
+ ctx.state = SSL2_STATE_ERROR;
+ return;
+ }
+ ctx.session.suite = array [3] of byte;
+ ctx.session.suite[0:] = v2hs.cipher_specs[0:3];
+ err: string;
+ (ctx.sel_ciph, nil, nil, err) = v2suite_to_spec(ctx.session.suite, SSL2_Cipher_Kinds);
+ if(err != "") {
+ if(SSL_DEBUG)
+ log("do_v2server_hello: " + err);
+ return;
+ }
+
+ # decode x509 certificates, authenticate server and extract
+ # public key from server certificate
+ if(v2hs.certificate_type != int SSL2_CT_X509_CERTIFICATE) {
+ if(SSL_DEBUG)
+ log("do_v2server_hello: not x509 certificate");
+ ctx.state = SSL2_STATE_ERROR;
+ return;
+ }
+ ctx.session.peer_certs = v2hs.certificate :: nil;
+ # TODO: decode v2hs.certificate as list of certificate
+ # verify the list of certificate
+ (e, signed) := x509->Signed.decode(v2hs.certificate);
+ if(e != "") {
+ if(SSL_DEBUG)
+ log("do_v2server_hello: " + e);
+ ctx.state = SSL2_STATE_ERROR;
+ return;
+ }
+ certificate: ref Certificate;
+ (e, certificate) = x509->Certificate.decode(signed.tobe_signed);
+ if(e != "") {
+ if(SSL_DEBUG)
+ log("do_v2server_hello: " + e);
+ ctx.state = SSL2_STATE_ERROR;
+ return;
+ }
+ id: int;
+ peer_pk: ref X509->PublicKey;
+ (e, id, peer_pk) = certificate.subject_pkinfo.getPublicKey();
+ if(e != nil) {
+ ctx.state = SSL2_STATE_ERROR; # protocol error
+ return;
+ }
+ pk: ref RSAKey;
+ pick key := peer_pk {
+ RSA =>
+ pk = key.pk;
+ * =>
+ }
+ # prepare and send client master key message
+ # TODO: change CipherSpec adt for more key info
+ # Temporary solution
+ # mkey (master key), ckey (clear key), skey(secret key)
+ mkey, ckey, skey, keyarg: array of byte;
+ (mkeylen, ckeylen, keyarglen) := v2suite_more(ctx.sel_ciph);
+ mkey = random->randombuf(Random->NotQuiteRandom, mkeylen);
+ if(ckeylen != 0)
+ ckey = mkey[0:ckeylen];
+ if(mkeylen > ckeylen)
+ skey = mkey[ckeylen:];
+ if(keyarglen > 0)
+ keyarg = random->randombuf(Random->NotQuiteRandom, keyarglen);
+ ekey: array of byte;
+ (e, ekey) = pkcs->rsa_encrypt(skey, pk, 2);
+ if(e != nil) {
+ if(SSL_DEBUG)
+ log("do_v2server_hello: " + e);
+ ctx.state = SSL2_STATE_ERROR;
+ return;
+ }
+ ctx.session.master_secret = mkey;
+ v2handshake_enque(
+ ref V2Handshake.ClientMasterKey(ctx.session.suite, ckey, ekey, keyarg),
+ ctx
+ );
+ }
+
+ # clean up out_queue before switch cipher
+ record_write_queue(ctx);
+ ctx.out_queue.data = nil;
+
+ # install keys onto ctx that will be pushed on ssl record when ready
+ (ctx.cw_mac, ctx.sw_mac, ctx.cw_key, ctx.sw_key, ctx.cw_IV, ctx.sw_IV)
+ = v2calc_keys(ctx.sel_ciph, ctx.session.master_secret,
+ ctx.client_random, ctx.server_random);
+ e := set_queues(ctx);
+ if(e != "") {
+ if(SSL_DEBUG)
+ log("do_v2server_finished: " + e);
+ ctx.state = SSL2_STATE_ERROR;
+ return;
+ }
+ ctx.status |= IN_READY;
+ ctx.status |= OUT_READY;
+
+ # prepare and send client finished message
+ v2handshake_enque(
+ ref V2Handshake.ClientFinished(ctx.server_random), # as connection_id
+ ctx
+ );
+
+ ctx.state = SSL2_STATE_SERVER_VERIFY;
+}
+
+# [server side]
+
+do_v2client_master_key(v2hs: ref V2Handshake.ClientMasterKey, ctx: ref Context)
+{
+ #if(cmk.cipher == -1 || cipher_info[cmk.cipher].cryptalg == -1) {
+ # # return ("protocol error: bad cipher in masterkey", nullc);
+ # ctx.state = SSL2_STATE_ERROR; # protocol error
+ # return;
+ #}
+
+ ctx.session.suite = v2hs.cipher_kind;
+
+ # TODO:
+ # someplace shall be able to install the key
+ # need further encapsulate encrypt and decrypt functions from KeyExAlg adt
+ master_key_length: int;
+ secret_key: array of byte;
+ pick alg := ctx.sel_keyx {
+ RSA =>
+ e: string;
+ (e, secret_key) = pkcs->rsa_decrypt(v2hs.encrypt_key, alg.sk, 0);
+ if(e != "") {
+ if(SSL_DEBUG)
+ log("do_v2client_master_key: " + e);
+ ctx.state = SSL2_STATE_ERROR;
+ return;
+ }
+ master_key_length = len v2hs.clear_key + len secret_key;
+ * =>
+ if(SSL_DEBUG)
+ log("do_v2client_master_key: unknown public key algorithm");
+ ctx.state = SSL2_STATE_ERROR;
+ return;
+ }
+ #TODO: do the following lines after modifying the CipherSpec adt
+ #if(master_key_length != ci.keylen) {
+ # ctx.state = SSL2_STATE_ERROR; # protocol error
+ # return;
+ #}
+
+ ctx.session.master_secret = array [master_key_length] of byte;
+ ctx.session.master_secret[0:] = v2hs.clear_key;
+ ctx.session.master_secret[len v2hs.clear_key:] = secret_key;
+
+ # install keys onto ctx that will be pushed on ssl record when ready
+ (ctx.cw_mac, ctx.sw_mac, ctx.cw_key, ctx.sw_key, ctx.cw_IV, ctx.sw_IV)
+ = v2calc_keys(ctx.sel_ciph, ctx.session.master_secret,
+ ctx.client_random, ctx.server_random);
+ v2handshake_enque(
+ ref V2Handshake.ServerVerify(ctx.client_random[16:]),
+ ctx
+ );
+ v2handshake_enque(
+ ref V2Handshake.ServerFinished(ctx.session.session_id),
+ ctx
+ );
+ ctx.state = SSL2_STATE_CLIENT_FINISHED;
+}
+
+# used by client side
+do_v2server_verify(v2hs: ref V2Handshake.ServerVerify, ctx: ref Context)
+{
+ # TODO:
+ # the challenge length may not be 16 bytes
+ if(bytes_cmp(v2hs.challenge, ctx.client_random[32-SSL2_CHALLENGE_LENGTH:]) < 0) {
+ if(SSL_DEBUG)
+ log("do_v2server_verify: challenge mismatch");
+ ctx.state = SSL2_STATE_ERROR;
+ return;
+ }
+
+ ctx.state = SSL2_STATE_SERVER_FINISHED;
+}
+
+# [client side]
+
+do_v2req_cert(v2hs: ref V2Handshake.RequestCertificate, ctx: ref Context)
+{
+ # not supported until v3
+ if(SSL_DEBUG)
+ log("do_v2req_cert: authenticate client not supported");
+ v2hs = nil;
+ ctx.state = SSL2_STATE_ERROR;
+}
+
+# [server side]
+
+do_v2client_certificate(v2hs: ref V2Handshake.ClientCertificate, ctx: ref Context)
+{
+ # not supported until v3
+ if(SSL_DEBUG)
+ log("do_v2client_certificate: authenticate client not supported");
+ v2handshake_enque (
+ ref V2Handshake.Error(SSL2_PE_NO_CERTIFICATE),
+ ctx
+ );
+ v2hs = nil;
+ ctx.state = SSL2_STATE_ERROR;
+}
+
+# [server side]
+
+do_v2client_finished(v2hs: ref V2Handshake.ClientFinished, ctx: ref Context)
+{
+ if(bytes_cmp(ctx.server_random, v2hs.connection_id) < 0) {
+ ctx.session.session_id = nil;
+ if(SSL_DEBUG)
+ log("do_v2client_finished: connection id mismatch");
+ ctx.state = SSL2_STATE_ERROR;
+ }
+ # TODO:
+ # the challenge length may not be 16 bytes
+ v2handshake_enque(
+ ref V2Handshake.ServerVerify(ctx.client_random[32-SSL2_CHALLENGE_LENGTH:]),
+ ctx
+ );
+ if(ctx.session.session_id == nil)
+ ctx.session.session_id = random->randombuf(Random->NotQuiteRandom, 16);
+ v2handshake_enque(
+ ref V2Handshake.ServerFinished(ctx.session.session_id),
+ ctx
+ );
+ e := set_queues(ctx);
+ if(e != "") {
+ if(SSL_DEBUG)
+ log("do_v2client_finished: " + e);
+ ctx.state = SSL2_STATE_ERROR;
+ return;
+ }
+ ctx.status |= IN_READY;
+ ctx.status |= OUT_READY;
+ sslsession->add_session(ctx.session);
+
+ ctx.state = STATE_EXIT;
+}
+
+# [client side]
+
+do_v2server_finished(v2hs: ref V2Handshake.ServerFinished, ctx: ref Context)
+{
+ if(ctx.session.session_id == nil)
+ ctx.session.session_id = array [16] of byte;
+ ctx.session.session_id[0:] = v2hs.session_id[0:];
+
+ sslsession->add_session(ctx.session);
+
+ ctx.state = STATE_EXIT;
+}
+
+
+# Note:
+# the key partitioning for v2 is different from v3
+
+v2calc_keys(ciph: ref CipherSpec, ms, cr, sr: array of byte)
+ : (array of byte, array of byte, array of byte, array of byte, array of byte, array of byte)
+{
+ cw_mac, sw_mac, cw_key, sw_key, cw_IV, sw_IV: array of byte;
+
+ # TODO: check the size of key block if IV exists
+ (mkeylen, ckeylen, keyarglen) := v2suite_more(ciph);
+ kblen := 2*mkeylen;
+ if(kblen%Keyring->MD5dlen != 0) {
+ if(SSL_DEBUG)
+ log("v2calc_keys: key block length is not multiple of MD5 hash length");
+ }
+ else {
+ key_block := array [kblen] of byte;
+
+ challenge := cr[32-SSL2_CHALLENGE_LENGTH:32]; # TODO: if challenge length != 16 ?
+ connection_id := sr[0:16]; # TODO: if connection_id length != 16 ?
+ var := array [1] of byte;
+ var[0] = byte 16r30;
+ i := 0;
+ while(i < kblen) {
+ (hash, nil) := md5_hash(ms::var::challenge::connection_id::nil, nil);
+ key_block[i:] = hash;
+ i += Keyring->MD5dlen;
+ ++var[0];
+ }
+
+ if(SSL_DEBUG)
+ log("ssl3: calc_keys:"
+ + "\n\tmaster key = \n\t\t" + bastr(ms)
+ + "\n\tchallenge = \n\t\t" + bastr(challenge)
+ + "\n\tconnection id = \n\t\t" + bastr(connection_id)
+ + "\n\tkey block = \n\t\t" + bastr(key_block) + "\n");
+
+ i = 0;
+ # server write key == client read key
+ # server write mac == server write key
+ sw_key = array [mkeylen] of byte;
+ sw_key[0:] = key_block[i:mkeylen];
+ sw_mac = array [mkeylen] of byte;
+ sw_mac[0:] = key_block[i:mkeylen];
+ # client write key == server read key
+ # client write mac == client write key
+ i += mkeylen;
+ cw_key = array [mkeylen] of byte;
+ cw_key[0:] = key_block[i:i+mkeylen];
+ cw_mac = array [mkeylen] of byte;
+ cw_mac[0:] = key_block[i:i+mkeylen];
+ # client IV == server IV
+ # Note:
+ # IV is a part of writing or reading key for ssl device
+ # this is composed again in setctl
+ cw_IV = array [keyarglen] of byte;
+ cw_IV[0:] = ms[mkeylen:mkeylen+keyarglen];
+ sw_IV = array [keyarglen] of byte;
+ sw_IV[0:] = ms[mkeylen:mkeylen+keyarglen];
+ }
+
+ if(SSL_DEBUG)
+ log("ssl3: calc_keys:"
+ + "\n\tclient_write_mac = \n\t\t" + bastr(cw_mac)
+ + "\n\tserver_write_mac = \n\t\t" + bastr(sw_mac)
+ + "\n\tclient_write_key = \n\t\t" + bastr(cw_key)
+ + "\n\tserver_write_key = \n\t\t" + bastr(sw_key)
+ + "\n\tclient_write_IV = \n\t\t" + bastr(cw_IV)
+ + "\n\tserver_write_IV = \n\t\t" + bastr(sw_IV) + "\n");
+
+ return (cw_mac, sw_mac, cw_key, sw_key, cw_IV, sw_IV);
+}
+
+v3tov2specs(suites: array of byte): array of byte
+{
+ # v3 suite codes are 2 bytes each, v2 codes are 3 bytes
+ n := len suites / 2;
+ kinds := array [n*3*2] of byte;
+ k := 0;
+ for(i := 0; i < n;) {
+ a := suites[i:i+2];
+ i += 2;
+ m := len SSL3_Suites;
+ for(j := 0; j < m; j++) {
+ b := SSL3_Suites[j];
+ if(a[0]==b[0] && a[1]==b[1])
+ break;
+ }
+ if (j == m) {
+ if(SSL_DEBUG)
+ log("ssl3: unknown v3 suite");
+ continue;
+ }
+ case j {
+ RSA_EXPORT_WITH_RC4_40_MD5 =>
+ kinds[k:] = SSL2_Cipher_Kinds[SSL2_CK_RC4_128_EXPORT40_WITH_MD5];
+ k += 3;
+ RSA_WITH_RC4_128_MD5 =>
+ kinds[k:] = SSL2_Cipher_Kinds[SSL2_CK_RC4_128_WITH_MD5];
+ k += 3;
+ RSA_WITH_IDEA_CBC_SHA =>
+ kinds[k:] = SSL2_Cipher_Kinds[SSL2_CK_IDEA_128_CBC_WITH_MD5];
+ k += 3;
+ RSA_WITH_DES_CBC_SHA =>
+ ;
+ * =>
+ if(SSL_DEBUG)
+ log("ssl3: unable to convert v3 suite to v2 kind");
+ }
+ # append v3 code in v2-safe manner
+ # (suite[0] == 0) => will be ignored by v2 server, picked up by v3 server
+ kinds[k] = byte 16r00;
+ kinds[k+1:] = SSL3_Suites[j];
+ k += 3;
+ }
+ return kinds[0:k];
+}
+
+v2suite_to_spec(cs: array of byte, cipher_kinds: array of array of byte)
+ : (ref CipherSpec, ref KeyExAlg, ref SigAlg, string)
+{
+ cip : ref CipherSpec;
+ kex : ref KeyExAlg;
+ sig : ref SigAlg;
+
+ n := len cipher_kinds;
+ for(i := 0; i < n; i++) {
+ found := cipher_kinds[i];
+ if(found[0]==cs[0] && found[1]==cs[1] && found[2]==cs[2]) break;
+ }
+
+ if(i == n)
+ return (nil, nil, nil, "fail to find a matched spec");
+
+ case i {
+ SSL2_CK_RC4_128_WITH_MD5 =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_RC4, SSL_STREAM_CIPHER,
+ 16, 0, SSL_MD5, Keyring->MD4dlen);
+
+ SSL2_CK_RC4_128_EXPORT40_WITH_MD5 =>
+ cip = ref CipherSpec(SSL_EXPORT_TRUE, SSL_RC4, SSL_STREAM_CIPHER,
+ 5, 0, SSL_MD5, Keyring->MD4dlen);
+
+ SSL2_CK_RC2_CBC_128_CBC_WITH_MD5 =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_RC2_CBC, SSL_BLOCK_CIPHER,
+ 16, 8, SSL_MD5, Keyring->MD4dlen);
+
+ SSL2_CK_RC2_CBC_128_CBC_EXPORT40_WITH_MD5 =>
+ cip = ref CipherSpec(SSL_EXPORT_TRUE, SSL_RC2_CBC, SSL_BLOCK_CIPHER,
+ 5, 8, SSL_MD5, Keyring->MD4dlen);
+
+ SSL2_CK_IDEA_128_CBC_WITH_MD5 =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_IDEA_CBC, SSL_BLOCK_CIPHER,
+ 16, 8, SSL_MD5, Keyring->MD4dlen);
+
+ SSL2_CK_DES_64_CBC_WITH_MD5 =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_DES_CBC, SSL_BLOCK_CIPHER,
+ 8, 8, SSL_MD5, Keyring->MD4dlen);
+
+ SSL2_CK_DES_192_EDE3_CBC_WITH_MD5 =>
+ cip = ref CipherSpec(SSL_EXPORT_FALSE, SSL_3DES_EDE_CBC, SSL_BLOCK_CIPHER,
+ 24, 8, SSL_MD5, Keyring->MD4dlen);
+ }
+
+ kex = ref KeyExAlg.RSA(nil, nil, nil);
+ sig = ref SigAlg.RSA(nil, nil);
+
+ return (cip, kex, sig, nil);
+}
+
+v2suite_more(ciph: ref CipherSpec): (int, int, int)
+{
+ mkeylen, ckeylen, keyarglen: int;
+
+ case ciph.bulk_cipher_algorithm {
+ SSL_RC4 =>
+ mkeylen = 16;
+ if(ciph.key_material == 5)
+ ckeylen = 16 - 5;
+ else
+ ckeylen = 0;
+ keyarglen = 0;
+
+ SSL_RC2_CBC =>
+ mkeylen = 16;
+ if(ciph.key_material == 5)
+ ckeylen = 16 - 5;
+ else
+ ckeylen = 0;
+ keyarglen = 8;
+
+ SSL_IDEA_CBC =>
+ mkeylen = 16;
+ ckeylen = 0;
+ keyarglen = 8;
+
+ SSL_DES_CBC =>
+ mkeylen = 8;
+ if(ciph.key_material == 5)
+ ckeylen = 8 - 5;
+ else
+ ckeylen = 0;
+ keyarglen = 8;
+
+ SSL_3DES_EDE_CBC =>
+ mkeylen = 24;
+ ckeylen = 0;
+ keyarglen = 8;
+ }
+
+ return (mkeylen, ckeylen, keyarglen);
+}
+
+v2handshake_enque(h: ref V2Handshake, ctx: ref Context)
+{
+ p := ref Protocol.pV2Handshake(h);
+
+ protocol_write(p, ctx);
+}
+
+V2Handshake.encode(hm: self ref V2Handshake): (array of byte, string)
+{
+ a : array of byte;
+ n : int;
+ e : string;
+
+ i := 0;
+ pick m := hm {
+ Error =>
+ n = 3;
+ a = array[n] of byte;
+ a[i++] = byte SSL2_MT_ERROR;
+ a[i:] = m.code;
+
+ ClientHello =>
+ specslen := len m.cipher_specs;
+ sidlen := len m.session_id;
+ challen := len m.challenge;
+ n = 9+specslen + sidlen + challen;
+ a = array[n] of byte;
+ a[i++] = byte SSL2_MT_CLIENT_HELLO;
+ a[i:] = m.version;
+ i += 2;
+ a[i:] = int_encode(specslen, 2);
+ i += 2;
+ a[i:] = int_encode(sidlen, 2);
+ i += 2;
+ a[i:] = int_encode(challen, 2);
+ i += 2;
+ a[i:] = m.cipher_specs;
+ i += specslen;
+ if(sidlen != 0) {
+ a[i:] = m.session_id;
+ i += sidlen;
+ }
+ if(challen != 0) {
+ a[i:] = m.challenge;
+ i += challen;
+ }
+
+ ServerHello =>
+ # use only the user certificate
+ certlen := len m.certificate;
+# specslen := 3*len m.cipher_specs;
+ specslen := len m.cipher_specs;
+ cidlen := len m.connection_id;
+ n = 11 + certlen + specslen + cidlen;
+ a = array[n] of byte;
+ a[i++] = byte SSL2_MT_SERVER_HELLO;
+ a[i++] = byte m.session_id_hit;
+ a[i++] = byte m.certificate_type;
+ a[i:] = m.version;
+ i += 2;
+ a[i:] = int_encode(certlen, 2);
+ i += 2;
+ a[i:] = int_encode(specslen, 2);
+ i += 2;
+ a[i:] = int_encode(cidlen, 2);
+ i += 2;
+ a[i:] = m.certificate;
+ i += certlen;
+ a[i:] = m.cipher_specs;
+ i += specslen;
+ a[i:] = m.connection_id;
+ i += cidlen;
+
+ ClientMasterKey =>
+ ckeylen := len m.clear_key;
+ ekeylen := len m.encrypt_key;
+ karglen := len m.key_arg;
+ n = 10 + ckeylen + ekeylen + karglen;
+ a = array[n] of byte;
+ a[i++] = byte SSL2_MT_CLIENT_MASTER_KEY;
+ a[i:] = m.cipher_kind;
+ i += 3;
+ a[i:] = int_encode(ckeylen, 2);
+ i += 2;
+ a[i:] = int_encode(ekeylen, 2);
+ i += 2;
+ a[i:] = int_encode(karglen, 2);
+ i += 2;
+ a[i:] = m.clear_key;
+ i += ckeylen;
+ a[i:] = m.encrypt_key;
+ i += ekeylen;
+ a[i:] = m.key_arg;
+ i += karglen;
+
+ ServerVerify =>
+ challen := len m.challenge;
+ n = 1 + challen;
+ a = array[n] of byte;
+ a[i++] = byte SSL2_MT_SERVER_VERIFY;
+ a[i:] = m.challenge;
+
+ RequestCertificate =>
+ cclen := len m.certificate_challenge;
+ n = 2 + cclen;
+ a = array[n] of byte;
+ a[i++] = byte SSL2_MT_REQUEST_CERTIFICATE;
+ a[i++] = byte m.authentication_type;
+ a[i:] = m.certificate_challenge;
+ i += cclen;
+
+ ClientCertificate =>
+ # use only the user certificate
+ certlen := len m.certificate;
+ resplen := len m.response;
+ n = 6 + certlen + resplen;
+ a = array[n] of byte;
+ a[i++] = byte SSL2_MT_CLIENT_CERTIFICATE;
+ a[i++] = byte m.certificate_type;
+ a[i:] = int_encode(certlen, 2);
+ i += 2;
+ a[i:] = int_encode(resplen, 2);
+ i += 2;
+ a[i:] = m.certificate;
+ i += certlen;
+ a[i:] = m.response;
+ i += resplen;
+
+ ClientFinished =>
+ cidlen := len m.connection_id;
+ n = 1 + cidlen;
+ a = array[n] of byte;
+ a[i++] = byte SSL2_MT_CLIENT_FINISHED;
+ a[i:] = m.connection_id;
+ i += cidlen;
+
+ ServerFinished =>
+ sidlen := len m.session_id;
+ n = 1 + sidlen;
+ a = array[n] of byte;
+ a[i++] = byte SSL2_MT_SERVER_FINISHED;
+ a[i:] = m.session_id;
+ i += sidlen;
+ }
+
+ return (a, e);
+}
+
+V2Handshake.decode(a: array of byte): (ref V2Handshake, string)
+{
+ m : ref V2Handshake;
+ e : string;
+
+ n := len a;
+ i := 1;
+ case int a[0] {
+ SSL2_MT_ERROR =>
+ if(n != 3)
+ break;
+ code := a[i:i+2];
+ i += 2;
+ m = ref V2Handshake.Error(code);
+
+ SSL2_MT_CLIENT_HELLO =>
+ if(n < 9) {
+ e = "client hello: message too short";
+ break;
+ }
+ ver := a[i:i+2];
+ i += 2;
+ specslen := int_decode(a[i:i+2]);
+ i += 2;
+ sidlen := int_decode(a[i:i+2]);
+ i += 2;
+ challen := int_decode(a[i:i+2]);
+ i += 2;
+ if(n != 9+specslen+sidlen+challen) {
+ e = "client hello: length mismatch";
+ break;
+ }
+ if(specslen%3 != 0) {
+ e = "client hello: must multiple of 3 bytes";
+ break;
+ }
+ specs: array of byte;
+ if(specslen != 0) {
+ specs = a[i:i+specslen];
+ i += specslen;
+ }
+ sid: array of byte;
+ if(sidlen != 0) {
+ sid = a[i:i+sidlen];
+ i += sidlen;
+ }
+ chal: array of byte;
+ if(challen != 0) {
+ chal = a[i:i+challen];
+ i += challen;
+ }
+ m = ref V2Handshake.ClientHello(ver, specs, sid, chal);
+
+ SSL2_MT_CLIENT_MASTER_KEY =>
+ if(n < 10) {
+ e = "client master key: message too short";
+ break;
+ }
+ kind := a[i:i+3];
+ i += 3;
+ ckeylen := int_decode(a[i:i+2]);
+ i += 2;
+ ekeylen := int_decode(a[i:i+2]);
+ i += 2;
+ karglen := int_decode(a[i:i+2]);
+ i += 2;
+ if(n != 10 + ckeylen + ekeylen + karglen) {
+ e = "client master key: length mismatch";
+ break;
+ }
+ ckey := a[i:i+ckeylen];
+ i += ckeylen;
+ ekey := a[i:i+ekeylen];
+ i += ekeylen;
+ karg := a[i:i+karglen];
+ i += karglen;
+ m = ref V2Handshake.ClientMasterKey(kind, ckey, ekey, karg);
+
+ SSL2_MT_CLIENT_FINISHED =>
+ cid := a[i:n];
+ i = n;
+ m = ref V2Handshake.ClientFinished(cid);
+
+ SSL2_MT_SERVER_HELLO =>
+ if(n < 11) {
+ e = "server hello: messsage too short";
+ break;
+ }
+ sidhit := int a[i++];
+ certtype := int a[i++];
+ ver := a[i:i+2];
+ i += 2;
+ certlen := int_decode(a[i:i+2]);
+ i += 2;
+ specslen := int_decode(a[i:i+2]);
+ i += 2;
+ cidlen := int_decode(a[i:i+2]);
+ i += 2;
+ if(n != 11+certlen+specslen+cidlen) {
+ e = "server hello: length mismatch";
+ break;
+ }
+ cert := a[i:i+certlen];
+ i += certlen;
+ if(specslen%3 != 0) {
+ e = "server hello: must be multiple of 3 bytes";
+ break;
+ }
+ specs := a[i:i+specslen];
+ i += specslen;
+ if(cidlen < 16 || cidlen > 32) {
+ e = "server hello: connection id length out of range";
+ break;
+ }
+ cid := a[i:i+cidlen];
+ i += cidlen;
+ m = ref V2Handshake.ServerHello(sidhit, certtype, ver, cert, specs, cid);
+
+ SSL2_MT_SERVER_VERIFY =>
+ chal := a[i:n];
+ i = n;
+ m = ref V2Handshake.ServerVerify(chal);
+
+ SSL2_MT_SERVER_FINISHED =>
+ sid := a[i:n];
+ m = ref V2Handshake.ServerFinished(sid);
+
+ SSL2_MT_REQUEST_CERTIFICATE =>
+ if(n < 2) {
+ e = "request certificate: message too short";
+ break;
+ }
+ authtype := int a[i++];
+ certchal := a[i:n];
+ i = n;
+ m = ref V2Handshake.RequestCertificate(authtype, certchal);
+
+ SSL2_MT_CLIENT_CERTIFICATE =>
+ if(n < 6) {
+ e = "client certificate: message too short";
+ break;
+ }
+ certtype := int a[i++];
+ certlen := int_decode(a[i:i+2]);
+ i += 2;
+ resplen := int_decode(a[i:i+2]);
+ i += 2;
+ if(n != 6+certlen+resplen) {
+ e = "client certificate: length mismatch";
+ break;
+ }
+ cert := a[i:i+certlen];
+ i += certlen;
+ resp := a[i:i+resplen];
+ m = ref V2Handshake.ClientCertificate(certtype, cert, resp);
+
+ * =>
+ e = "unknown message [" + string a[0] + "]";
+ }
+
+ return (m, e);
+}
+
+# utilities
+
+md5_hash(input: list of array of byte, md5_ds: ref DigestState): (array of byte, ref DigestState)
+{
+ hash_value := array [Keyring->MD5dlen] of byte;
+ ds : ref DigestState;
+
+ if(md5_ds != nil)
+ ds = md5_ds.copy();
+
+ lab := input;
+ for(i := 0; i < len input - 1; i++) {
+ ds = keyring->md5(hd lab, len hd lab, nil, ds);
+ lab = tl lab;
+ }
+ ds = keyring->md5(hd lab, len hd lab, hash_value, ds);
+
+ return (hash_value, ds);
+}
+
+sha_hash(input: list of array of byte, sha_ds: ref DigestState): (array of byte, ref DigestState)
+{
+ hash_value := array [Keyring->SHA1dlen] of byte;
+ ds : ref DigestState;
+
+ if(sha_ds != nil)
+ ds = sha_ds.copy();
+
+ lab := input;
+ for(i := 0; i < len input - 1; i++) {
+ ds = keyring->sha1(hd lab, len hd lab, nil, ds);
+ lab = tl lab;
+ }
+ ds = keyring->sha1(hd lab, len hd lab, hash_value, ds);
+
+ return (hash_value, ds);
+}
+
+md5_sha_hash(input: list of array of byte, md5_ds, sha_ds: ref DigestState)
+ : (array of byte, ref DigestState, ref DigestState)
+{
+ buf := array [Keyring->MD5dlen+Keyring->SHA1dlen] of byte;
+
+ (buf[0:], md5_ds) = md5_hash(input, md5_ds);
+ (buf[Keyring->MD5dlen:], sha_ds) = sha_hash(input, sha_ds);
+
+ return (buf, md5_ds, sha_ds);
+}
+
+int_decode(buf: array of byte): int
+{
+ val := 0;
+ for(i := 0; i < len buf; i++)
+ val = (val << 8) | (int buf[i]);
+
+ return val;
+}
+
+int_encode(value, length: int): array of byte
+{
+ buf := array [length] of byte;
+
+ while(length--) {
+ buf[length] = byte value;
+ value >>= 8;
+ }
+
+ return buf;
+}
+
+
+bastr(a: array of byte) : string
+{
+ ans : string = "";
+
+ for(i := 0; i < len a; i++) {
+ if(i < len a - 1 && i != 0 && i%10 == 0)
+ ans += "\n\t\t";
+ if(i == len a -1)
+ ans += sys->sprint("%2x", int a[i]);
+ else
+ ans += sys->sprint("%2x ", int a[i]);
+ }
+
+ return ans;
+}
+
+bbastr(a: array of array of byte) : string
+{
+ info := "";
+
+ for(i := 0; i < len a; i++)
+ info += bastr(a[i]);
+
+ return info;
+}
+
+lbastr(a: list of array of byte) : string
+{
+ info := "";
+
+ l := a;
+ while(l != nil) {
+ info += bastr(hd l) + "\n\t\t";
+ l = tl l;
+ }
+
+ return info;
+}
+
+# need to fix (string a == string b)
+bytes_cmp(a, b: array of byte): int
+{
+ if(len a != len b)
+ return -1;
+
+ n := len a;
+ for(i := 0; i < n; i++) {
+ if(a[i] != b[i])
+ return -1;
+ }
+
+ return 0;
+}
+
+putn(a: array of byte, i, value, n: int): int
+{
+ j := n;
+ while(j--) {
+ a[i+j] = byte value;
+ value >>= 8;
+ }
+ return i+n;
+}
+
+INVALID_SUITE : con "invalid suite list";
+ILLEGAL_SUITE : con "illegal suite list";
+
+cksuites(suites : array of byte) : string
+{
+ m := len suites;
+ if (m == 0 || (m&1))
+ return INVALID_SUITE;
+ n := len SSL3_Suites;
+ ssl3s := array [2] of byte;
+ for (j := 0; j < m; j += 2) {
+ for( i := 0; i < n; i++) {
+ ssl3s = SSL3_Suites[i];
+ if(suites[j] == ssl3s[0] && suites[j+1] == ssl3s[1])
+ break;
+ }
+ if (i == n)
+ return ILLEGAL_SUITE;
+ }
+ return nil;
+}
diff --git a/appl/lib/crypt/sslsession.b b/appl/lib/crypt/sslsession.b
new file mode 100644
index 00000000..cce8399e
--- /dev/null
+++ b/appl/lib/crypt/sslsession.b
@@ -0,0 +1,176 @@
+#
+# SSL Session Cache
+#
+implement SSLsession;
+
+include "sys.m";
+ sys : Sys;
+
+include "daytime.m";
+ daytime : Daytime;
+
+include "sslsession.m";
+
+
+# default session id timeout
+TIMEOUT_SECS : con 5*60; # sec
+
+SessionCache: adt {
+ db : list of ref Session;
+ time_out : int;
+};
+
+# The shared session cache by all ssl contexts is available for efficiently resumming
+# sessions for different run time contexts.
+
+Session_Cache : ref SessionCache;
+
+
+init(): string
+{
+ sys = load Sys Sys->PATH;
+ if(sys == nil)
+ return "sslsession: load sys module failed";
+
+ daytime = load Daytime Daytime->PATH;
+ if(daytime == nil)
+ return "sslsession: load Daytime module failed";
+
+ Session_Cache = ref SessionCache(nil, TIMEOUT_SECS);
+
+ return "";
+}
+
+Session.new(peer: string, time: int, ver: array of byte): ref Session
+{
+ s := ref Session;
+
+ s.peer = peer;
+ s.connection_time = time;
+ s.version = array [2] of byte;
+ s.version[0:] = ver;
+ s.session_id = nil;
+ s.suite = nil;
+ s.master_secret = nil;
+ s.peer_certs = nil;
+
+ return s;
+}
+
+Session.duplicate(s: self ref Session): ref Session
+{
+ new := ref Session;
+
+ new.peer = s.peer;
+ new.connection_time = s.connection_time;
+ new.version = array [len s.version] of byte;
+ new.version[0:] = s.version;
+ new.session_id = array [len s.session_id] of byte;
+ new.session_id[0:] = s.session_id;
+ new.suite = array [len s.suite] of byte;
+ new.suite[0:] = s.suite;
+ new.master_secret = array [len s.master_secret] of byte;
+ new.master_secret[0:] = s.master_secret;
+ l: list of array of byte;
+ pcs := s.peer_certs;
+ while(pcs != nil) {
+ a := hd pcs;
+ b := array [len a] of byte;
+ b[0:] = a;
+ l = b :: l;
+ pcs = tl pcs;
+ }
+ while(l != nil) {
+ new.peer_certs = (hd l) :: new.peer_certs;
+ l = tl l;
+ }
+ return new;
+}
+
+# Each request process should get a copy of a session. A session will be
+# removed from database if it is expired. The garbage
+# collector will finally remove it from memory if there are no more
+# references to it.
+
+get_session_byname(peer: string): ref Session
+{
+ s: ref Session;
+ now := daytime->now(); # not accurate but more efficient
+
+ l := Session_Cache.db;
+ while(l != nil) {
+ if((hd l).peer == peer) {
+ s = hd l;
+ # TODO: remove expired session
+ if(now > s.connection_time+Session_Cache.time_out)
+ s = nil;
+ break;
+ }
+ l = tl l;
+ }
+ if(s == nil)
+ s = Session.new(peer, now, nil);
+ else
+ s = s.duplicate();
+
+ return s;
+}
+
+# replace the old by the new one
+add_session(s: ref Session)
+{
+ #old : ref Session;
+
+ #ls := Session_Cache.db;
+ #while(ls != nil) {
+ # old = hd ls;
+ # if(s.session_id == old.session_id) {
+ # # old = s;
+ # return;
+ # }
+ #}
+
+ # always resume the most recent
+ if(s != nil)
+ Session_Cache.db = s :: Session_Cache.db;
+}
+
+get_session_byid(session_id: array of byte): ref Session
+{
+ s: ref Session;
+ now := daytime->now(); # not accurate but more efficient
+ l := Session_Cache.db;
+ while(l != nil) {
+ if(bytes_cmp((hd l).session_id, session_id) == 0) {
+ s = hd l;
+ # replace expired session
+ if(now > s.connection_time+Session_Cache.time_out)
+ s = Session.new(s.peer, now, nil);
+ else
+ s = s.duplicate();
+ break;
+ }
+ l = tl l;
+ }
+ return s;
+}
+
+set_timeout(t: int)
+{
+ Session_Cache.time_out = t;
+}
+
+bytes_cmp(a, b: array of byte): int
+{
+ if(len a != len b)
+ return -1;
+
+ n := len a;
+ for(i := 0; i < n; i++) {
+ if(a[i] != b[i])
+ return -1;
+ }
+
+ return 0;
+}
+
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);
+}