Files
2026-08-31 12:20:27 +02:00

404 lines
10 KiB
C++

#include "pvault.h"
#include "cryptographic_operations.h"
namespace pvault {
bool get_key(
const char* path,
const String& password,
uint8_t* key
) {
File file = SD.open(path, FILE_READ);
if(!file) { return false; }
// get master salt and nonce
header hdr{};
pvault_cryptography::read_header(file, hdr);
// obtain key candidate
pvault_cryptography::derive_key(password, hdr.master_salt, key);
uint32_t len = hdr.ciphertext_length;
// get tag
uint8_t tag[pvault::tag_size];
uint32_t file_size = file.size();
file.seek(file_size - pvault::tag_size);
file.read(tag, pvault::tag_size);
bool decryption = pvault_cryptography::decrypt_verify(key, hdr.nonce, file, len, tag, sizeof(pvault::header));
file.close();
return decryption;
}
bool init_vault(
const char* path,
const String& password,
const device_settings& settings,
const vault& data
) {
// input validation
if(!path || password.length() == 0) { return false; }
// open file
if(SD.exists(path)) { SD.remove(path); }
File file = SD.open(path, FILE_WRITE);
if(!file) { return false; }
// buffers
header hdr{};
uint8_t salt[salt_size];
uint8_t nonce[nonce_size];
uint8_t key[key_size];
uint32_t len = sizeof(data);
// generate parameters
pvault_cryptography::generate_salt(salt);
pvault_cryptography::generate_nonce(nonce);
pvault_cryptography::derive_key(password, salt, key);
// build header
hdr.magic[0] = 'C';
hdr.magic[1] = 'R';
hdr.magic[2] = 'Y';
hdr.magic[3] = 'P';
hdr.version = file_version;
hdr.iterations = iterations;
memcpy(hdr.master_salt, salt, salt_size);
memcpy(hdr.nonce, nonce, nonce_size);
hdr.settings = settings;
hdr.ciphertext_length = len;
pvault_cryptography::write_header(file, hdr);
// encrypt
bool encryption = pvault_cryptography::encrypt_chunked(
key,
nonce,
reinterpret_cast<const uint8_t*>(&data),
len,
file
);
// cleanup
pvault_cryptography::secure_zero(salt, salt_size);
pvault_cryptography::secure_zero(nonce, nonce_size);
pvault_cryptography::secure_zero(key, key_size);
file.close();
return encryption;
}
bool update_vault(
const char* path,
const uint8_t* key,
const device_settings& settings,
const vault& data
) {
// input validation
if(!path || !key) { return false; }
// open file
File read = SD.open(path, FILE_READ);
if(!read) { return false; }
// read header
header hdr{};
if(!pvault_cryptography::read_header(read, hdr)) {
read.close();
return false;
}
// close file (we have everything needed)
read.close();
// write header and encrypted data
SD.remove(path);
File write = SD.open(path, FILE_WRITE);
if(!write) {
return false;
}
// generate new nonce
pvault_cryptography::generate_nonce(hdr.nonce);
// modify header
hdr.settings = settings;
pvault_cryptography::write_header(write, hdr);
// encrypt
uint32_t len = sizeof(data);
bool encryption = pvault_cryptography::encrypt_chunked(
key,
hdr.nonce,
reinterpret_cast<const uint8_t*>(&data),
len,
write
);
// cleanup
write.close();
return true;
}
bool load_vault(
const char* path,
const uint8_t* key,
device_settings& settings,
vault& data
) {
// input validation
if(!path || !key) { return false; }
// open file
File file = SD.open(path, FILE_READ);
if(!file) { return false; }
// read header
header hdr{};
if(!pvault_cryptography::read_header(file, hdr)) {
file.close();
return false;
}
// header validation
if(hdr.magic[0] != 'C' || hdr.magic[1] != 'R' || hdr.magic[2] != 'Y' || hdr.magic[3] != 'P') {
file.close();
return false;
}
if(hdr.version != file_version) {
file.close();
return false;
}
if(hdr.ciphertext_length == 0) {
file.close();
return false;
}
settings = hdr.settings;
// buffers
uint32_t len = hdr.ciphertext_length;
uint8_t nonce[nonce_size];
// parameters
memcpy(nonce, hdr.nonce, nonce_size);
bool decryption = pvault_cryptography::decrypt_chunked(
key,
nonce,
file,
len,
reinterpret_cast<uint8_t*>(&data)
);
// cleanup
file.close();
return decryption;
}
bool update_config(
const char* path,
const device_settings& settings
) {
// open the old file
File old_file = SD.open(path, FILE_READ);
if(!old_file) { return false; }
// read its header
header hdr{};
if(!pvault_cryptography::read_header(old_file, hdr)) {
old_file.close();
return false;
}
// update settings in header
hdr.settings = settings;
// obtain length and add tag size to it
uint32_t len = hdr.ciphertext_length;
len += pvault::tag_size;
// open temp file
String temp_path = String(path);
temp_path += ".temp";
if(SD.exists(temp_path)) { SD.remove(temp_path); }
File new_file = SD.open(temp_path, FILE_WRITE);
if(!new_file) {
old_file.close();
return false;
}
// rewrite header and remaining bytes
if(!pvault_cryptography::write_header(new_file, hdr)) {
new_file.close();
old_file.close();
return false;
}
uint32_t to_write = len;
uint32_t write_size;
uint8_t buffer[CHUNK_SIZE];
while(to_write > 0) {
if(to_write > CHUNK_SIZE) {
write_size = CHUNK_SIZE;
} else {
write_size = to_write;
}
old_file.read(buffer, write_size);
new_file.write(buffer, write_size);
to_write -= write_size;
}
old_file.close();
new_file.close();
// delete old file and replace with new
SD.remove(path);
if(SD.exists(temp_path)) { SD.rename(temp_path, path); }
// exit
return true;
}
bool read_config(
const char* path,
device_settings& settings
) {
File file = SD.open(path, FILE_READ);
if(!file) { return false; }
header hdr{};
pvault_cryptography::read_header(file, hdr);
file.close();
settings = hdr.settings;
return true;
}
bool get_salt(
const char* path,
uint8_t* salt
) {
if (!path) { return false; }
File file = SD.open(path, FILE_READ);
if(!file) { return false; }
header hdr{};
if(!pvault_cryptography::read_header(file, hdr)) {
file.close();
return false;
}
memcpy(salt, hdr.master_salt, pvault::salt_size);
file.close();
return true;
}
bool replace_vault(
const char* path,
const device_settings& settings,
const uint8_t* key,
const uint8_t* salt,
const char* import_path
) {
File import_file = SD.open(import_path, FILE_READ);
// read file header
// magic 8 bytes
uint8_t magic[8];
import_file.read(magic, 8);
if(memcmp(magic, "PWIMPORT", 8) != 0) {
return false;
}
// nonce 12 bytes
uint8_t nonce[pvault::nonce_size];
import_file.read(nonce, pvault::nonce_size);
// length 4 bytes
uint32_t len;
import_file.read(reinterpret_cast<uint8_t*>(&len), sizeof(uint32_t));
// read tag
uint32_t file_size = import_file.size();
uint8_t tag[pvault::tag_size];
import_file.seek(file_size - pvault::tag_size);
import_file.read(tag, pvault::tag_size);
// check if key that we have can decrypt the imported vault. Otherwise there's no point doing it.
bool decryption = pvault_cryptography::decrypt_verify(key, nonce, import_file, len, tag, 24);
if(!decryption) { return false; }
// overwrite vault with provided settings
header hdr{};
hdr.magic[0] = 'C';
hdr.magic[1] = 'R';
hdr.magic[2] = 'Y';
hdr.magic[3] = 'P';
hdr.iterations = pvault::iterations;
hdr.settings = settings;
hdr.ciphertext_length = len;
hdr.version = pvault::file_version;
memcpy(hdr.master_salt, salt, pvault::salt_size);
memcpy(hdr.nonce, nonce, pvault::nonce_size);
if(SD.exists(path)) { SD.remove(path); }
File new_file = SD.open(path, FILE_WRITE);
pvault_cryptography::write_header(new_file, hdr);
// copy ciphertext from old file to new
uint32_t to_copy = len;
uint32_t copy_size;
uint8_t copy_buffer[CHUNK_SIZE];
import_file.seek(24);
while(to_copy > 0) {
if(to_copy > CHUNK_SIZE) {
copy_size = CHUNK_SIZE;
} else {
copy_size = to_copy;
}
import_file.read(copy_buffer, copy_size);
new_file.write(copy_buffer, copy_size);
to_copy -= copy_size;
}
pvault_cryptography::write_tag(new_file, tag);
import_file.close();
new_file.close();
// remove import file
SD.remove(import_path);
return true;
}
}