/// <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", nameof(message)); } if (signature == null) { throw new ArgumentException("missing signature input", nameof(signature)); } if (publicKey == null) { throw new ArgumentException("missing publicKey input", nameof(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> /// 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", nameof(signature)); } if (publicKey == null) { throw new ArgumentException("missing publicKey input", nameof(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="privateKey">A valid private key.</param> /// <param name="password">The password to decrypt the private key.</param> /// <returns>A MinisignPrivateKey object.</returns> /// <exception cref="OverflowException"></exception> /// <exception cref="CorruptPrivateKeyException"></exception> /// <exception cref="ArgumentOutOfRangeException"></exception> /// <exception cref="ArgumentException"></exception> /// <exception cref="ArgumentNullException"></exception> public static MinisignPrivateKey LoadPrivateKey(byte[] privateKey, byte[] password) { if (privateKey == null) { throw new ArgumentException("missing privateKey input", nameof(privateKey)); } if (password == null) { throw new ArgumentException("missing password input", nameof(password)); } var minisignPrivateKey = new MinisignPrivateKey { SignatureAlgorithm = ArrayHelpers.SubArray(privateKey, 0, 2), KdfAlgorithm = ArrayHelpers.SubArray(privateKey, 2, 2), ChecksumAlgorithm = ArrayHelpers.SubArray(privateKey, 4, 2), KdfSalt = ArrayHelpers.SubArray(privateKey, 6, 32), KdfOpsLimit = BitConverter.ToInt64(ArrayHelpers.SubArray(privateKey, 38, 8), 0), //currently unused KdfMemLimit = BitConverter.ToInt64(ArrayHelpers.SubArray(privateKey, 46, 8), 0) //currently unused }; if (!minisignPrivateKey.SignatureAlgorithm.SequenceEqual(Encoding.UTF8.GetBytes(Sigalg))) { throw new CorruptPrivateKeyException("bad SignatureAlgorithm"); } if (!minisignPrivateKey.ChecksumAlgorithm.SequenceEqual(Encoding.UTF8.GetBytes(Chkalg))) { throw new CorruptPrivateKeyException("bad ChecksumAlgorithm"); } if (!minisignPrivateKey.KdfAlgorithm.SequenceEqual(Encoding.UTF8.GetBytes(Kdfalg))) { throw new CorruptPrivateKeyException("bad KdfAlgorithm"); } if (minisignPrivateKey.KdfSalt.Length != KeySaltBytes) { throw new CorruptPrivateKeyException("bad KdfSalt length"); } var encryptedKeyData = ArrayHelpers.SubArray(privateKey, 54, 104); var decryptionKey = PasswordHash.ScryptHashBinary(password, minisignPrivateKey.KdfSalt, PasswordHash.Strength.Sensitive, 104); var decryptedKeyData = EncryptionHelpers.Xor(encryptedKeyData, decryptionKey); minisignPrivateKey.KeyId = ArrayHelpers.SubArray(decryptedKeyData, 0, 8); minisignPrivateKey.SecretKey = ArrayHelpers.SubArray(decryptedKeyData, 8, 64); minisignPrivateKey.Checksum = ArrayHelpers.SubArray(decryptedKeyData, 72, 32); if (minisignPrivateKey.KeyId.Length != KeyNumBytes) { throw new CorruptPrivateKeyException("bad KeyId length"); } var calculatedChecksum = GenericHash.Hash( ArrayHelpers.ConcatArrays(minisignPrivateKey.SignatureAlgorithm, minisignPrivateKey.KeyId, minisignPrivateKey.SecretKey), null, 32); if (!ArrayHelpers.ConstantTimeEquals(minisignPrivateKey.Checksum, calculatedChecksum)) { throw new CorruptPrivateKeyException("bad private key checksum"); } // extract the public key from the private key minisignPrivateKey.PublicKey = PublicKeyAuth.ExtractEd25519PublicKeyFromEd25519SecretKey(minisignPrivateKey.SecretKey); return(minisignPrivateKey); }