Manifest.vala 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. using Invercargill;
  2. using InvercargillJson;
  3. namespace Binman {
  4. public errordomain ManifestError {
  5. KEY_MISMATCH,
  6. INVALID_SIGNATURE
  7. }
  8. public class ManifestFile {
  9. public File file { get; private set; }
  10. public ManifestHeader read_header() throws Error {
  11. return new JsonlInputStream(get_stream())
  12. .as_property_groups()
  13. .try_map_with<ManifestHeader>(ManifestHeader.get_mapper())
  14. .first()
  15. .unwrap();
  16. }
  17. public Attempts<ManifestEntry> get_entries() throws Error {
  18. return new JsonlInputStream(get_stream())
  19. .as_property_groups()
  20. .skip(1)
  21. .until(o => o.has("signature"))
  22. .try_map_with<ManifestEntry>(ManifestEntry.get_mapper());
  23. }
  24. public bool verify_signature() throws Error {
  25. var stream = new DataInputStream(get_stream());
  26. var checksum = new Checksum(ChecksumType.SHA512);
  27. ManifestSignature signature;
  28. ManifestHeader header = null;
  29. while (true) {
  30. var line = stream.read_line();
  31. var obj = new JsonElement.from_string(line).as<JsonObject>();
  32. // Don't include signature object in checksum
  33. if(obj.has("signature")) {
  34. signature = ManifestSignature.get_mapper().materialise(obj);
  35. break;
  36. }
  37. checksum.update(line.data, line.data.length);
  38. if(header == null) {
  39. header = ManifestHeader.get_mapper().materialise(obj);
  40. }
  41. }
  42. // Calculate checksum
  43. var checksum_bytes = new uint8[ChecksumType.SHA512.get_length()];
  44. size_t size = checksum_bytes.length;
  45. checksum.get_digest(checksum_bytes, ref size);
  46. // Verify signature
  47. var signed_checksum = Sodium.Asymmetric.Signing.verify(signature.signature.to_array(), header.key.to_array());
  48. return new BinaryData.from_byte_array(signed_checksum).equals(new BinaryData.from_byte_array(checksum_bytes));
  49. }
  50. private InputStream get_stream() throws Error {
  51. var decompressor = new ZlibDecompressor(ZlibCompressorFormat.GZIP);
  52. return new ConverterInputStream(file.read(), decompressor);
  53. }
  54. public ManifestFile(File file) {
  55. this.file = file;
  56. }
  57. public ManifestFile.from_name(string name, ApplicationType type, BinaryData? key = null) throws Error {
  58. this.file = File.new_build_filename("/var/binman", name, ApplicationType.get_mapper().map_from(type));
  59. if(!this.file.query_exists()) {
  60. throw new FileError.NOENT("Could not find the specified manifest");
  61. }
  62. if(key != null && !read_header().key.equals(key)) {
  63. throw new ManifestError.KEY_MISMATCH("The key in the manifest file does not match the key expected");
  64. }
  65. }
  66. }
  67. public class ManifestHeader {
  68. public string name { get; set; }
  69. public string description { get; set; }
  70. public int serial { get; set; }
  71. public string post_exec { get; set; }
  72. public BinaryData key { get; set; }
  73. public Vector<string> remotes { get; set; }
  74. public static PropertyMapper<ManifestHeader> get_mapper() {
  75. return PropertyMapper.build_for<ManifestHeader>(cfg => {
  76. cfg.map<string>("name", o => o.name, (o, v) => o.name = v);
  77. cfg.map<string>("desc", o => o.description, (o, v) => o.description = v);
  78. cfg.map<int>("serial", o => o.serial, (o, v) => o.serial = v);
  79. cfg.map<string>("post-exec", o => o.post_exec, (o, v) => o.post_exec = v);
  80. cfg.map<string>("key", o => o.key.to_base64(), (o, v) => o.key = new BinaryData.from_base64(v));
  81. cfg.map_many<string>("remotes", o => o.remotes, (o, v) => o.remotes = v.to_vector());
  82. cfg.set_constructor(() => new ManifestHeader());
  83. });
  84. }
  85. }
  86. public class ManifestEntry {
  87. public string path { get; set; }
  88. public int user { get; set; }
  89. public int group { get; set; }
  90. public Posix.mode_t mode { get; set; }
  91. public string? target { get; set; }
  92. public string? licence { get; set; }
  93. public ManifestFileDescriptor? binary { get; set; }
  94. public ManifestFileDescriptor? source { get; set; }
  95. public static PropertyMapper<ManifestEntry> get_mapper() {
  96. return PropertyMapper.build_for<ManifestEntry>(cfg => cfg
  97. .map<string>("path", o => o.path, (o, v) => o.path = v)
  98. .map<int>("uid", o => o.user, (o, v) => o.user = v)
  99. .map<int>("gid", o => o.group, (o, v) => o.group = v)
  100. .map<int>("mod", o => (int)o.mode, (o, v) => o.mode = v)
  101. .map<string?>("target", o => o.target, (o, v) => o.target = v, false)
  102. .map<string?>("licence", o => o.licence, (o, v) => o.licence = v, false)
  103. .map_properties_with<ManifestFileDescriptor?>("bin", o => o.binary, (o, v) => o.binary = v, ManifestFileDescriptor.get_mapper(), false)
  104. .map_properties_with<ManifestFileDescriptor?>("ccs", o => o.source, (o, v) => o.source = v, ManifestFileDescriptor.get_mapper(), false)
  105. .set_constructor(() => new ManifestEntry())
  106. );
  107. }
  108. }
  109. public class ManifestFileDescriptor {
  110. public BinaryData checksum { get; set; }
  111. public uint64 size { get; set; }
  112. public string remote_path { get; set; }
  113. public static PropertyMapper<ManifestFileDescriptor> get_mapper() {
  114. return PropertyMapper.build_for<ManifestFileDescriptor>(cfg => cfg
  115. .map<string>("checksum", o => o.checksum.to_base64(), (o, v) => o.checksum = new BinaryData.from_base64(v))
  116. .map<uint64?>("size", o => o.size, (o, v) => o.size = v)
  117. .map<string>("path", o => o.remote_path, (o, v) => o.remote_path = v)
  118. .set_constructor(() => new ManifestFileDescriptor())
  119. );
  120. }
  121. }
  122. public class ManifestSignature {
  123. public BinaryData signature { get; set; }
  124. public static PropertyMapper<ManifestSignature> get_mapper() {
  125. return PropertyMapper.build_for<ManifestSignature>(cfg => cfg
  126. .map<string>("signature", o => o.signature.to_base64(), (o, v) => o.signature = new BinaryData.from_base64(v))
  127. .set_constructor(() => new ManifestSignature())
  128. );
  129. }
  130. }
  131. }