/// <summary> /// Validate a file with a MinisignSignature and a MinisignPublicKey object. /// </summary> /// <param name="message">The message to validate.</param> /// <param name="signature">A valid MinisignSignature object.</param> /// <param name="publicKey">A valid MinisignPublicKey object.</param> /// <returns><c>true</c> if valid; otherwise, <c>false</c>.</returns> /// <exception cref="OverflowException"></exception> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentException"></exception> /// <exception cref="ArgumentOutOfRangeException"></exception> public static bool ValidateSignature(byte[] message, MinisignSignature signature, MinisignPublicKey publicKey) { if (message == null) throw new ArgumentException("missing signature input", "message"); if (signature == null) throw new ArgumentException("missing signature input", "signature"); if (publicKey == null) throw new ArgumentException("missing publicKey input", "publicKey"); if (!ArrayHelpers.ConstantTimeEquals(signature.KeyId, publicKey.KeyId)) return false; // verify the signature if (PublicKeyAuth.VerifyDetached(signature.Signature, message, publicKey.PublicKey)) { // verify the trusted comment return PublicKeyAuth.VerifyDetached(signature.GlobalSignature, ArrayHelpers.ConcatArrays(signature.Signature, signature.TrustedComment), publicKey.PublicKey); } return false; }
/// <summary> /// Generate a new Minisign key pair. /// </summary> /// <param name="password">The password to protect the secret key.</param> /// <param name="writeOutputFiles">If false, no files will be written.</param> /// <param name="outputFolder">The folder to write the files (optional).</param> /// <param name="keyPairFileName">The name of the files to write (optional).</param> /// <returns>A MinisignKeyPair object.</returns> /// <exception cref="ArgumentException"></exception> /// <exception cref="OverflowException"></exception> /// <exception cref="DirectoryNotFoundException"></exception> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="IOException"></exception> /// <exception cref="UnauthorizedAccessException"></exception> /// <exception cref="PathTooLongException"></exception> /// <exception cref="SecurityException"></exception> /// <exception cref="NotSupportedException"></exception> /// <exception cref="ArgumentOutOfRangeException"></exception> public static MinisignKeyPair GenerateKeyPair(string password, bool writeOutputFiles = false, string outputFolder = "", string keyPairFileName = "minisign") { if (string.IsNullOrEmpty(password)) { throw new ArgumentNullException("password", "password can not be null"); } if (writeOutputFiles) { //validate the outputFolder if (string.IsNullOrEmpty(outputFolder) || !Directory.Exists(outputFolder)) { throw new DirectoryNotFoundException("outputFolder must exist"); } if (outputFolder.IndexOfAny(Path.GetInvalidPathChars()) > -1) throw new ArgumentException("The given path to the output folder contains invalid characters!"); //validate the keyPairFileName if (string.IsNullOrEmpty(keyPairFileName)) { throw new ArgumentNullException("keyPairFileName", "keyPairFileName can not be empty"); } } var minisignKeyPair = new MinisignKeyPair(); var minisignPrivateKey = new MinisignPrivateKey(); var keyPair = PublicKeyAuth.GenerateKeyPair(); var keyId = SodiumCore.GetRandomBytes(KeyNumBytes); var kdfSalt = SodiumCore.GetRandomBytes(32); minisignPrivateKey.PublicKey = keyPair.PublicKey; minisignPrivateKey.KdfSalt = kdfSalt; minisignPrivateKey.SignatureAlgorithm = Encoding.UTF8.GetBytes(Sigalg); minisignPrivateKey.ChecksumAlgorithm = Encoding.UTF8.GetBytes(Chkalg); minisignPrivateKey.KdfAlgorithm = Encoding.UTF8.GetBytes(Kdfalg); minisignPrivateKey.KdfMemLimit = 1073741824; //currently unused minisignPrivateKey.KdfOpsLimit = 33554432; //currently unused var checksum = GenericHash.Hash( ArrayHelpers.ConcatArrays(minisignPrivateKey.SignatureAlgorithm, keyId, keyPair.PrivateKey), null, 32); minisignPrivateKey.KeyId = keyId; minisignPrivateKey.SecretKey = keyPair.PrivateKey; minisignPrivateKey.Checksum = checksum; var dataToProtect = ArrayHelpers.ConcatArrays(keyId, keyPair.PrivateKey, checksum); var encryptionKey = PasswordHash.ScryptHashBinary(Encoding.UTF8.GetBytes(password), minisignPrivateKey.KdfSalt, PasswordHash.Strength.Sensitive, 104); var encryptedKeyData = EncryptionHelpers.Xor(dataToProtect, encryptionKey); // set up the public key var minisignPublicKey = new MinisignPublicKey(); minisignPublicKey.KeyId = keyId; minisignPublicKey.PublicKey = keyPair.PublicKey; minisignPublicKey.SignatureAlgorithm = Encoding.UTF8.GetBytes(Sigalg); keyPair.Dispose(); if (writeOutputFiles) { var privateKeyOutputFileName = Path.Combine(outputFolder, keyPairFileName + PrivateKeyFileSuffix); var publicKeyOutputFileName = Path.Combine(outputFolder, keyPairFileName + PublicKeyFileSuffix); var binaryPublicKey = ArrayHelpers.ConcatArrays( minisignPublicKey.SignatureAlgorithm, minisignPublicKey.KeyId, minisignPublicKey.PublicKey ); var publicFileContent = new[] { CommentPrefix + "minisign public key " + Utilities.BinaryToHex(minisignPublicKey.KeyId, Utilities.HexFormat.None, Utilities.HexCase.Upper), Convert.ToBase64String(binaryPublicKey) }; var binaryPrivateKey = ArrayHelpers.ConcatArrays( minisignPrivateKey.SignatureAlgorithm, minisignPrivateKey.KdfAlgorithm, minisignPrivateKey.ChecksumAlgorithm, minisignPrivateKey.KdfSalt, BitConverter.GetBytes(minisignPrivateKey.KdfOpsLimit), BitConverter.GetBytes(minisignPrivateKey.KdfMemLimit), encryptedKeyData ); var privateFileContent = new[] { CommentPrefix + PrivateKeyDefaultComment, Convert.ToBase64String(binaryPrivateKey) }; // files will be overwritten! File.WriteAllLines(publicKeyOutputFileName, publicFileContent); File.WriteAllLines(privateKeyOutputFileName, privateFileContent); minisignKeyPair.MinisignPublicKeyFilePath = publicKeyOutputFileName; minisignKeyPair.MinisignPrivateKeyFilePath = privateKeyOutputFileName; } minisignKeyPair.MinisignPublicKey = minisignPublicKey; minisignKeyPair.MinisignPrivateKey = minisignPrivateKey; return minisignKeyPair; }
/// <summary> /// Validate a file with a MinisignSignature and a MinisignPublicKey object. /// </summary> /// <param name="filePath">The full path to the file.</param> /// <param name="signature">A valid MinisignSignature object.</param> /// <param name="publicKey">A valid MinisignPublicKey object.</param> /// <returns><c>true</c> if valid; otherwise, <c>false</c>.</returns> /// <exception cref="FileNotFoundException"></exception> /// <exception cref="OverflowException"></exception> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentException"></exception> /// <exception cref="ArgumentOutOfRangeException"></exception> public static bool ValidateSignature(string filePath, MinisignSignature signature, MinisignPublicKey publicKey) { if (filePath != null && !File.Exists(filePath)) { throw new FileNotFoundException("could not find filePath"); } if (signature == null) throw new ArgumentException("missing signature input", "signature"); if (publicKey == null) throw new ArgumentException("missing publicKey input", "publicKey"); if (!ArrayHelpers.ConstantTimeEquals(signature.KeyId, publicKey.KeyId)) return false; // load the file into memory var file = LoadMessageFile(filePath); // verify the signature if (PublicKeyAuth.VerifyDetached(signature.Signature, file, publicKey.PublicKey)) { // verify the trusted comment return PublicKeyAuth.VerifyDetached(signature.GlobalSignature, ArrayHelpers.ConcatArrays(signature.Signature, signature.TrustedComment), publicKey.PublicKey); } return false; }
/// <summary> /// Load a public key into a MinisignPublicKey object. /// </summary> /// <param name="publicKey">A valid public key.</param> /// <returns>A MinisignPublicKey object.</returns> /// <exception cref="ArgumentException"></exception> /// <exception cref="OverflowException"></exception> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentOutOfRangeException"></exception> public static MinisignPublicKey LoadPublicKey(byte[] publicKey) { if (publicKey == null) throw new ArgumentException("missing publicKey input", "publicKey"); var minisignPublicKey = new MinisignPublicKey { SignatureAlgorithm = ArrayHelpers.SubArray(publicKey, 0, 2), KeyId = ArrayHelpers.SubArray(publicKey, 2, 8), PublicKey = ArrayHelpers.SubArray(publicKey, 10) }; return minisignPublicKey; }