AwsEncryptionSDK/runtimes/net/TestVectorsNative/TestVectorGenerator/Generator.cs (182 lines of code) (raw):

// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 using System.CommandLine; using System.Security.Cryptography; using AWS.Cryptography.EncryptionSDK; using AWS.Cryptography.MaterialProviders; namespace TestVectors { class Generator { private const string OutputKeysManifestUri = "file://keys.json"; static void Main(string[] args) { var encryptManifestFileOpt = new Option<FileInfo>(name: "--encrypt-manifest") { IsRequired = true }; var outputDirOpt = new Option<DirectoryInfo>(name: "--output-dir") { IsRequired = true }; var quietOpt = new Option<bool>(name: "--quiet"); var rootCommand = new RootCommand { encryptManifestFileOpt, outputDirOpt, quietOpt }; rootCommand.SetHandler((FileInfo encryptManifestFile, DirectoryInfo outputDir, bool quiet) => { new Generator(encryptManifestFile, outputDir, quiet).Run(); }, encryptManifestFileOpt, outputDirOpt, quietOpt); rootCommand.Invoke(args); } private readonly EncryptManifest _encryptManifest; private readonly KeyManifest _keyManifest; private readonly DirectoryInfo _outputDir; private readonly DirectoryInfo _plaintextDir; private readonly DirectoryInfo _ciphertextDir; private readonly bool _quiet; private RandomNumberGenerator _randomNumberGenerator; private Generator(FileInfo encryptManifestFile, DirectoryInfo outputDir, bool quiet) { _randomNumberGenerator = RandomNumberGenerator.Create(); _encryptManifest = Utils.LoadObjectFromPath<EncryptManifest>(encryptManifestFile.FullName); Console.Error.WriteLine( $"Loaded {_encryptManifest.VectorMap.Count} vectors from {encryptManifestFile.FullName}"); string keysPath = Utils.ManifestUriToPath(_encryptManifest.KeysUri, encryptManifestFile.FullName); _keyManifest = Utils.LoadObjectFromPath<KeyManifest>(keysPath); if (!outputDir.Exists) { outputDir.Create(); } if (outputDir.GetFileSystemInfos().Length != 0) { throw new ArgumentException("Output directory not empty"); } _outputDir = outputDir; _plaintextDir = outputDir.CreateSubdirectory("plaintexts"); _ciphertextDir = outputDir.CreateSubdirectory("ciphertexts"); _quiet = quiet; } private void Run() { var plaintexts = GeneratePlaintexts(_encryptManifest.PlaintextSizes); Utils.WriteNamedDataMap(_plaintextDir, plaintexts); Console.Error.WriteLine($"Wrote {plaintexts.Count} plaintext files"); var targetedVectors = _encryptManifest.VectorMap .Where(pair => ShouldTargetVector(pair.Key, pair.Value)) .ToList(); var totalVectorCount = _encryptManifest.VectorMap.Count; Console.Error.WriteLine($"Targeting {targetedVectors.Count} out of {totalVectorCount} vectors"); GenerateAndWriteVectors(targetedVectors, plaintexts); Console.Error.WriteLine($"Wrote {targetedVectors.Count} ciphertexts"); DecryptManifest decryptManifest = GenerateDecryptManifest(targetedVectors, plaintexts); string decryptManifestPath = Path.Combine(_outputDir.FullName, "manifest.json"); Utils.WriteObjectToPath(decryptManifest, decryptManifestPath); Console.Error.WriteLine("Wrote decrypt vector manifest"); // TODO resolve KMS aliases to ARNs, instead of copying them over var keysPath = Utils.ManifestUriToPath(OutputKeysManifestUri, decryptManifestPath); Utils.WriteObjectToPath(_keyManifest, keysPath); Console.Error.WriteLine("Wrote key manifest"); } private Dictionary<string, MemoryStream> GeneratePlaintexts(Dictionary<string, uint> sizes) { var plaintexts = new Dictionary<string, MemoryStream>(); foreach (var entry in sizes) { if (entry.Value > int.MaxValue) { throw new ArgumentException($"Can't generate a {entry.Value}-byte plaintext"); } var bytes = new byte[(int)entry.Value]; _randomNumberGenerator.GetBytes(bytes); var buffer = new MemoryStream(bytes); plaintexts.Add(entry.Key, buffer); } return plaintexts; } private static readonly ESDKAlgorithmSuiteId[] COMMITTING_ALGORITHM_SUITES = { ESDKAlgorithmSuiteId.ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY, ESDKAlgorithmSuiteId.ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY_ECDSA_P384, }; /// <summary> /// Returns whether the generator should attempt to encrypt the given vector. /// Useful for selecting particular vectors when debugging. /// </summary> private bool ShouldTargetVector(string id, EncryptVector vector) { // We don't support encrypting unframed messages. return vector.Scenario.FrameSize != 0; } private void GenerateAndWriteVectors( IList<KeyValuePair<string, EncryptVector>> targetedVectors, Dictionary<string, MemoryStream> plaintexts ) { foreach (var entry in targetedVectors) { try { var ciphertext = GenerateDecryptVector(entry.Value, plaintexts); Utils.WriteBinaryFile(_ciphertextDir, entry.Key, ciphertext); if (!_quiet) { Console.Error.WriteLine($"Wrote ciphertext file for vector {entry.Key}"); } } catch (AwsEncryptionSdkException ex) { throw new ApplicationException($"Failed to encrypt vector {entry.Key}", ex); } } } private DecryptManifest GenerateDecryptManifest( IList<KeyValuePair<string, EncryptVector>> targetedVectors, Dictionary<string, MemoryStream> plaintexts) { var decryptVectors = new Dictionary<string, DecryptVector>(); foreach (var entry in targetedVectors) { var plaintextPath = "file://" + Path.Combine(_plaintextDir.Name, entry.Value.Scenario.PlaintextName); var ciphertextPath = "file://" + Path.Combine(_ciphertextDir.Name, entry.Key); decryptVectors[entry.Key] = new DecryptVector { Ciphertext = ciphertextPath, MasterKeys = entry.Value.Scenario.MasterKeys, CMM = entry.Value.Scenario.CMM, EncryptionContext = entry.Value.Scenario.EncryptionContext, Result = new DecryptResult { Output = new DecryptOutput { Plaintext = plaintextPath } } }; } return new DecryptManifest { Meta = new ManifestMeta { Type = "awses-decrypt", Version = 4 }, Client = new Client { Name = "ESDK-NET", // TODO pass this by env var Version = "4.0.0" }, KeysUri = OutputKeysManifestUri, VectorMap = decryptVectors }; } private MemoryStream GenerateDecryptVector( EncryptVector vector, Dictionary<string, MemoryStream> plaintexts) { EncryptScenario scenario = vector.Scenario; ESDKAlgorithmSuiteId algSuiteId = new ESDKAlgorithmSuiteId("0x" + scenario.Algorithm); ESDKCommitmentPolicy commitmentPolicy = COMMITTING_ALGORITHM_SUITES.Contains(algSuiteId) ? ESDKCommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT : ESDKCommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT; AwsEncryptionSdkConfig config = new AwsEncryptionSdkConfig { CommitmentPolicy = commitmentPolicy }; ESDK encryptionSdk = new ESDK(config); ICryptographicMaterialsManager cmm = MaterialProviderFactory.CreateEncryptCmm(vector, _keyManifest.Keys); EncryptInput encryptInput = new EncryptInput { AlgorithmSuiteId = algSuiteId, EncryptionContext = scenario.EncryptionContext, FrameLength = scenario.FrameSize, MaterialsManager = cmm, Plaintext = plaintexts[scenario.PlaintextName] }; return encryptionSdk.Encrypt(encryptInput).Ciphertext; } } }