using AIStudio.Tools; using Microsoft.Extensions.Logging.Abstractions; namespace AIStudio.Tests.Tools; /// /// Checks the encryption which the app shares with the runtime, for the secrets which travel /// between both. /// /// /// The runtime encrypts and decrypts the same values in Rust, with a password and a salt which it /// makes anew at every start. Both sides therefore have to derive exactly the same key and IV. The /// values here were built without AI Studio, the way the runtime builds them: PBKDF2 with SHA-512 and /// 100 iterations derives 48 bytes from the password and the salt, the first 32 being the AES-256 key /// and the last 16 the IV. openssl encrypted the text with AES-256-CBC, and the salt precedes the /// ciphertext. /// [TestFixture] public sealed class EncryptionTests { private static readonly byte[] SECRET_PASSWORD = Enumerable.Range(0, 512).Select(index => (byte)index).ToArray(); private static readonly byte[] SECRET_KEY_SALT = Enumerable.Range(0xA0, 16).Select(index => (byte)index).ToArray(); private static async Task CreateEncryptionAsync() { var encryption = new Encryption(NullLogger.Instance, SECRET_PASSWORD, SECRET_KEY_SALT); await encryption.Initialize(); return encryption; } [TestCase("oKGio6SlpqeoqaqrrK2ur3lNRzNAfoSYKyC70CpjwS8=", "", TestName = "An empty text")] [TestCase("oKGio6SlpqeoqaqrrK2urwUfAfn5LbbIjbOC9nSvtUU=", "sk-imported-key", TestName = "A text within one block")] [TestCase("oKGio6SlpqeoqaqrrK2urwbjgbJCNk6Zw1Z9HyIiJdZJ+E7czzLYlM/undXUIsDr", "Schlüssel für Größe 🔑", TestName = "A text beyond ASCII")] public async Task TheAppEncryptsLikeTheRuntime(string expectedEncryptedValue, string text) { var encryption = await CreateEncryptionAsync(); var encryptedText = await encryption.Encrypt(text); Assert.That(encryptedText.EncryptedData, Is.EqualTo(expectedEncryptedValue)); } [TestCase("oKGio6SlpqeoqaqrrK2ur3lNRzNAfoSYKyC70CpjwS8=", "", TestName = "An empty encrypted text")] [TestCase("oKGio6SlpqeoqaqrrK2urwUfAfn5LbbIjbOC9nSvtUU=", "sk-imported-key", TestName = "An encrypted text within one block")] [TestCase("oKGio6SlpqeoqaqrrK2urwbjgbJCNk6Zw1Z9HyIiJdZJ+E7czzLYlM/undXUIsDr", "Schlüssel für Größe 🔑", TestName = "An encrypted text beyond ASCII")] public async Task TheAppDecryptsWhatTheRuntimeEncrypted(string encryptedValue, string expectedText) { var encryption = await CreateEncryptionAsync(); var text = await encryption.Decrypt(new EncryptedText(encryptedValue)); Assert.That(text, Is.EqualTo(expectedText)); } }