CertifiedNameInfo.vala 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. using Invercargill;
  2. using GnuTLS.X509;
  3. namespace Riddle {
  4. errordomain CertificateError {
  5. FAILURE
  6. }
  7. public class CertifiedNameInfo : NameInfo {
  8. private uint8[] raw_data { get; protected set; }
  9. private uint8[] data;
  10. private uint8[] signature;
  11. public override string get_encoded() {
  12. return Base64.encode(raw_data);
  13. }
  14. public CertifiedNameInfo.from_string(string encoded_name_info) throws NameInfoError{
  15. raw_data = Base64.decode(encoded_name_info);
  16. var stream = new MemoryInputStream.from_data(raw_data);
  17. var dis = new DataInputStream(stream);
  18. try{
  19. deserialise_data(dis);
  20. }
  21. catch (CertificateError e) {
  22. throw new NameInfoError.INVALID("A step in the X.509 verification step failed: " + e.message);
  23. }
  24. catch (Error e) {
  25. throw new NameInfoError.BAD_DATA("Error reading certified name information: " + e.message);
  26. }
  27. }
  28. public CertifiedNameInfo(string domain, PrivateKey key, Certificate cert, Certificate[] ca_chain, Enumerable<NameProperty> properties) throws Error {
  29. name = domain;
  30. effective = new DateTime.now_utc();
  31. expires = new DateTime.from_unix_utc(cert.get_expiration_time());
  32. this.properties = properties;
  33. var base_info = stringify_base_info();
  34. var base_data = as_datum(base_info.data);
  35. var signature = new uint8[5120];
  36. size_t sig_size = signature.length;
  37. check_result(key.sign_data(GnuTLS.DigestAlgorithm.SHA512, 0, ref base_data, signature, ref sig_size));
  38. signature.length = (int)sig_size;
  39. this.signature = signature;
  40. this.data = base_info.data;
  41. raw_data = serialise_data(cert, ca_chain);
  42. }
  43. private static GnuTLS.Datum as_datum(uint8[] data) {
  44. return GnuTLS.Datum() {
  45. data = data,
  46. size = data.length
  47. };
  48. }
  49. private uint8[] serialise_data(Certificate certificate, Certificate[] chain) {
  50. var stream = new MemoryOutputStream(null, GLib.realloc, GLib.free);
  51. var dos = new DataOutputStream(stream);
  52. dos.put_uint32(data.length);
  53. dos.put_uint32(signature.length);
  54. var cert = new uint8[5120];
  55. size_t cert_size = cert.length;
  56. check_result(certificate.export(CertificateFormat.DER, cert, ref cert_size));
  57. cert.length = (int)cert_size;
  58. dos.put_uint32(cert.length);
  59. dos.put_uint32(chain.length);
  60. dos.write(data);
  61. dos.write(signature);
  62. dos.write(cert);
  63. for(int i = 0; i < chain.length; i++) {
  64. cert = new uint8[5120];
  65. cert_size = cert.length;
  66. check_result(chain[i].export(CertificateFormat.DER, cert, ref cert_size));
  67. cert.length = (int)cert_size;
  68. dos.put_uint32(cert.length);
  69. dos.write(cert);
  70. }
  71. dos.flush();
  72. dos.close();
  73. var buffer = stream.steal_data();
  74. buffer.length = (int)stream.get_data_size();
  75. return buffer;
  76. }
  77. private void deserialise_data(DataInputStream stream) throws Error {
  78. var data_length = stream.read_uint32();
  79. var sig_length = stream.read_uint32();
  80. var cert_length = stream.read_uint32();
  81. var chain_count = stream.read_uint32();
  82. data = new uint8[data_length];
  83. stream.read(data);
  84. signature = new uint8[sig_length];
  85. stream.read(signature);
  86. var certificate_data = new uint8[cert_length];
  87. stream.read(certificate_data);
  88. var certificate_datum = as_datum(certificate_data);
  89. var certificate = Certificate.create();
  90. check_result(certificate.import(ref certificate_datum, CertificateFormat.DER));
  91. // TODO Idk how I'm supposed to handle chains yet
  92. // TODO verify
  93. var data_datum = as_datum(data);
  94. var sig_datum = as_datum(signature);
  95. check_result(certificate.verify_data2(get_algorithm(certificate), 0, ref data_datum, ref sig_datum));
  96. }
  97. private void check_result(int result) throws Error {
  98. if(result != 0) {
  99. throw new CertificateError.FAILURE(@"GnuTLS reported an error: $((GnuTLS.ErrorCode)result)");
  100. }
  101. }
  102. private static GnuTLS.SignAlgorithm get_algorithm(Certificate cert) {
  103. uint bits = 0;
  104. switch (cert.get_pk_algorithm(out bits)) {
  105. case GnuTLS.PKAlgorithm.DSA:
  106. return GnuTLS.SignAlgorithm.DSA_SHA512;
  107. case GnuTLS.PKAlgorithm.ECDSA:
  108. return GnuTLS.SignAlgorithm.ECDSA_SHA512;
  109. case GnuTLS.PKAlgorithm.RSA:
  110. return GnuTLS.SignAlgorithm.RSA_SHA512;
  111. default:
  112. return GnuTLS.SignAlgorithm.UNKNOWN;
  113. }
  114. }
  115. }
  116. }