#pragma once #include namespace nall::Beat::Archive { struct Container { Container(array_view = {}); ~Container(); auto isCompressed() const -> bool { return (bool)compression.type; } auto isSigned() const -> bool { return (bool)signature.type; } auto isEncrypted() const -> bool { return (bool)encryption.type; } auto compressLZSA() -> void; auto signEd25519(uint256_t privateKey) -> void; auto encryptXChaCha20(uint256_t privateKey, uint192_t nonce = 0) -> void; auto validate() -> bool; auto decryptXChaCha20(uint256_t privateKey) -> bool; auto verifyEd25519(uint256_t publicKey) -> bool; auto decompressLZSA() -> bool; auto append(string name, string location) -> shared_pointer; auto appendPath(string name) -> shared_pointer; auto appendFile(string name, array_view memory) -> shared_pointer; auto remove(string name) -> bool; auto find(string name) -> shared_pointer; auto sort() -> void; auto begin() { return nodes.begin(); } auto end() { return nodes.end(); } auto begin() const { return nodes.begin(); } auto end() const { return nodes.end(); } auto rbegin() { return nodes.rbegin(); } auto rend() { return nodes.rend(); } auto rbegin() const { return nodes.rbegin(); } auto rend() const { return nodes.rend(); } vector> nodes; vector memory; string metadata; struct Compression { string type; } compression; struct Signature { string type; uint256_t privateKey = 0; uint256_t publicKey = 0; uint512_t value = 0; } signature; struct Encryption { string type; uint256_t privateKey = 0; uint192_t nonce = 0; } encryption; }; Container::Container(array_view memory) { this->memory.resize(memory.size()); nall::memory::copy(this->memory.data(), memory.data(), memory.size()); } Container::~Container() { metadata = {}; signature = {}; encryption = {}; } // auto Container::compressLZSA() -> void { compression.type = "lzsa"; } auto Container::signEd25519(uint256_t privateKey) -> void { signature.type = "ed25519"; signature.privateKey = privateKey; } auto Container::encryptXChaCha20(uint256_t privateKey, uint192_t nonce) -> void { if(!nonce) { CSPRNG::XChaCha20 csprng; nonce = csprng.random(); } encryption.type = "xchacha20"; encryption.privateKey = privateKey; encryption.nonce = nonce; } // auto Container::validate() -> bool { array_view memory = this->memory; if(memory.size() < 44) return false; //8 (metadata size) + 32 (SHA256) + 4 (signature) if(memory[memory.size() - 4] != 'B') return false; if(memory[memory.size() - 3] != 'P') return false; if(memory[memory.size() - 2] != 'A') return false; if(memory[memory.size() - 1] != '1') return false; auto sha256 = memory.readl(memory.size() - 36, 32); if(Hash::SHA256({memory.data(), memory.size() - 36}).value() != sha256) return false; auto size = memory.readl(memory.size() - 44, 8); if(size & 1ull << 63) { size -= 1ull << 63; metadata = memory.view(memory.size() - 44 - size, size); uint64_t offset = memory.size() - 44 - size; for(auto& byte : metadata) byte ^= offset++; } else { metadata = memory.view(memory.size() - 44 - size, size); } auto document = BML::unserialize(metadata); if(auto node = document["archive/encryption"]) { if(node.text() == "xchacha20") { encryption.type = node.text(); encryption.nonce = Decode::Base<57, uint192_t>(node["nonce"].text()); } } if(auto node = document["archive/signature"]) { if(node.text() == "ed25519") { signature.type = node.text(); signature.publicKey = Decode::Base<57, uint256_t>(node["publicKey"].text()); signature.value = Decode::Base<57, uint512_t>(node["value"].text()); } } if(auto node = document["archive/compression"]) { compression.type = node.text(); } return true; } auto Container::decryptXChaCha20(uint256_t privateKey) -> bool { encryption.privateKey = privateKey; Cipher::XChaCha20 xchacha20{encryption.privateKey, encryption.nonce}; auto size = memory.readl(memory.size() - 44, 8); memory = xchacha20.decrypt(memory.view(0, memory.size() - 44 - size)); return true; } auto Container::verifyEd25519(uint256_t publicKey) -> bool { EllipticCurve::Ed25519 ed25519; auto size = memory.readl(memory.size() - 44, 8); return ed25519.verify(memory.view(0, memory.size() - 44 - size), signature.value, publicKey); } auto Container::decompressLZSA() -> bool { memory = Decode::LZSA(memory); return (bool)memory; } // auto Container::append(string name, string location) -> shared_pointer { for(auto& node : nodes) if(node->name == name) return {}; if(auto node = Node::create(name, location)) return nodes.append(node), node; return {}; } auto Container::appendPath(string name) -> shared_pointer { for(auto& node : nodes) if(node->name == name) return {}; if(auto node = Node::createPath(name)) return nodes.append(node), node; return {}; } auto Container::appendFile(string name, array_view memory) -> shared_pointer { for(auto& node : nodes) if(node->name == name) return {}; if(auto node = Node::createFile(name, memory)) return nodes.append(node), node; return {}; } auto Container::remove(string name) -> bool { if(auto offset = nodes.find([&](auto& node) { return node->name == name; })) return nodes.remove(*offset), true; return false; } auto Container::find(string name) -> shared_pointer { if(auto offset = nodes.find([&](auto& node) { return node->name == name; })) return nodes[*offset]; return {}; } auto Container::sort() -> void { nodes.sort([&](auto& lhs, auto& rhs) { return string::icompare(lhs->name, rhs->name) < 0; }); } }