using Invercargill; using GnuTLS.X509; namespace Riddle { errordomain CertificateError { FAILURE } public class CertifiedNameInfo : NameInfo { private uint8[] raw_data { get; protected set; } private uint8[] data; private uint8[] signature; public override string get_encoded() { return Base64.encode(raw_data); } public CertifiedNameInfo.from_string(string encoded_name_info) throws NameInfoError{ raw_data = Base64.decode(encoded_name_info); var stream = new MemoryInputStream.from_data(raw_data); var dis = new DataInputStream(stream); try{ deserialise_data(dis); } catch (CertificateError e) { throw new NameInfoError.INVALID("A step in the X.509 verification step failed: " + e.message); } catch (Error e) { throw new NameInfoError.BAD_DATA("Error reading certified name information: " + e.message); } } public CertifiedNameInfo(string domain, PrivateKey key, Certificate cert, Certificate[] ca_chain, Enumerable properties) throws Error { name = domain; effective = new DateTime.now_utc(); expires = new DateTime.from_unix_utc(cert.get_expiration_time()); this.properties = properties; var base_info = stringify_base_info(); var base_data = as_datum(base_info.data); var signature = new uint8[5120]; size_t sig_size = signature.length; check_result(key.sign_data(GnuTLS.DigestAlgorithm.SHA512, 0, ref base_data, signature, ref sig_size)); signature.length = (int)sig_size; this.signature = signature; this.data = base_info.data; raw_data = serialise_data(cert, ca_chain); } private static GnuTLS.Datum as_datum(uint8[] data) { return GnuTLS.Datum() { data = data, size = data.length }; } private uint8[] serialise_data(Certificate certificate, Certificate[] chain) { var stream = new MemoryOutputStream(null, GLib.realloc, GLib.free); var dos = new DataOutputStream(stream); dos.put_uint32(data.length); dos.put_uint32(signature.length); var cert = new uint8[5120]; size_t cert_size = cert.length; check_result(certificate.export(CertificateFormat.DER, cert, ref cert_size)); cert.length = (int)cert_size; dos.put_uint32(cert.length); dos.put_uint32(chain.length); dos.write(data); dos.write(signature); dos.write(cert); for(int i = 0; i < chain.length; i++) { cert = new uint8[5120]; cert_size = cert.length; check_result(chain[i].export(CertificateFormat.DER, cert, ref cert_size)); cert.length = (int)cert_size; dos.put_uint32(cert.length); dos.write(cert); } dos.flush(); dos.close(); var buffer = stream.steal_data(); buffer.length = (int)stream.get_data_size(); return buffer; } private void deserialise_data(DataInputStream stream) throws Error { var data_length = stream.read_uint32(); var sig_length = stream.read_uint32(); var cert_length = stream.read_uint32(); var chain_count = stream.read_uint32(); data = new uint8[data_length]; stream.read(data); signature = new uint8[sig_length]; stream.read(signature); var certificate_data = new uint8[cert_length]; stream.read(certificate_data); var certificate_datum = as_datum(certificate_data); var certificate = Certificate.create(); check_result(certificate.import(ref certificate_datum, CertificateFormat.DER)); // TODO Idk how I'm supposed to handle chains yet // TODO verify var data_datum = as_datum(data); var sig_datum = as_datum(signature); check_result(certificate.verify_data2(get_algorithm(certificate), 0, ref data_datum, ref sig_datum)); } private void check_result(int result) throws Error { if(result != 0) { throw new CertificateError.FAILURE(@"GnuTLS reported an error: $((GnuTLS.ErrorCode)result)"); } } private static GnuTLS.SignAlgorithm get_algorithm(Certificate cert) { uint bits = 0; switch (cert.get_pk_algorithm(out bits)) { case GnuTLS.PKAlgorithm.DSA: return GnuTLS.SignAlgorithm.DSA_SHA512; case GnuTLS.PKAlgorithm.ECDSA: return GnuTLS.SignAlgorithm.ECDSA_SHA512; case GnuTLS.PKAlgorithm.RSA: return GnuTLS.SignAlgorithm.RSA_SHA512; default: return GnuTLS.SignAlgorithm.UNKNOWN; } } } }