Skip to content

Commit

Permalink
src: Check if finalized with Incremental classes.
Browse files Browse the repository at this point in the history
Prevents misuse since you need to reinitialize the state after finalizing. This should've already been in place, although there are warnings in the documentation.
  • Loading branch information
samuel-lucas6 committed Sep 24, 2023
1 parent 9cd1def commit 7976d61
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 1 deletion.
35 changes: 35 additions & 0 deletions src/Geralt.Tests/BLAKE2bTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -387,4 +387,39 @@ public void Incremental_Invalid(int hashSize, int messageSize, int keySize)
Assert.ThrowsException<ArgumentOutOfRangeException>(() => blake2b.FinalizeAndVerify(h));
}
}

[TestMethod]
[DynamicData(nameof(UnkeyedTestVectors), DynamicDataSourceType.Method)]
[DynamicData(nameof(KeyedTestVectors), DynamicDataSourceType.Method)]
public void Incremental_Compute_InvalidOperation(string hash, string message, string? key = null)
{
var h = new byte[hash.Length / 2];
var m = Convert.FromHexString(message);
var k = key != null ? Convert.FromHexString(key) : Array.Empty<byte>();

using var blake2b = new IncrementalBLAKE2b(h.Length, k);
blake2b.Update(m);
blake2b.Finalize(h);

Assert.ThrowsException<InvalidOperationException>(() => blake2b.Update(m));
Assert.ThrowsException<InvalidOperationException>(() => blake2b.Finalize(h));
Assert.ThrowsException<InvalidOperationException>(() => blake2b.FinalizeAndVerify(h));
}

[TestMethod]
[DynamicData(nameof(KeyedTestVectors), DynamicDataSourceType.Method)]
public void Incremental_Verify_InvalidOperation(string hash, string message, string key)
{
var h = Convert.FromHexString(hash);
var m = Convert.FromHexString(message);
var k = Convert.FromHexString(key);

using var blake2b = new IncrementalBLAKE2b(h.Length, k);
blake2b.Update(m);
blake2b.FinalizeAndVerify(h);

Assert.ThrowsException<InvalidOperationException>(() => blake2b.Update(m));
Assert.ThrowsException<InvalidOperationException>(() => blake2b.Finalize(h));
Assert.ThrowsException<InvalidOperationException>(() => blake2b.FinalizeAndVerify(h));
}
}
36 changes: 36 additions & 0 deletions src/Geralt.Tests/Ed25519Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,24 @@ public void Incremental_Sign_Invalid(int signatureSize, int messageSize, int pri
Assert.ThrowsException<ArgumentOutOfRangeException>(() => ed25519ph.Finalize(s, sk));
}

[TestMethod]
[DynamicData(nameof(Rfc8032Ed25519phTestVectors), DynamicDataSourceType.Method)]
public void Incremental_Sign_InvalidOperation(string signature, string message, string privateKey)
{
var s = new byte[IncrementalEd25519.SignatureSize];
var m = Convert.FromHexString(message);
var sk = Convert.FromHexString(privateKey);
var pk = sk[^Ed25519.PublicKeySize..];

using var ed25519ph = new IncrementalEd25519();
ed25519ph.Update(m);
ed25519ph.Finalize(s, sk);

Assert.ThrowsException<InvalidOperationException>(() => ed25519ph.Update(m));
Assert.ThrowsException<InvalidOperationException>(() => ed25519ph.Finalize(s, sk));
Assert.ThrowsException<InvalidOperationException>(() => ed25519ph.FinalizeAndVerify(s, pk));
}

[TestMethod]
[DynamicData(nameof(Rfc8032Ed25519phTestVectors), DynamicDataSourceType.Method)]
public void Incremental_Verify_Valid(string signature, string message, string privateKey)
Expand Down Expand Up @@ -390,4 +408,22 @@ public void Incremental_Verify_Invalid(int signatureSize, int messageSize, int p

Assert.ThrowsException<ArgumentOutOfRangeException>(() => ed25519ph.FinalizeAndVerify(s, pk));
}

[TestMethod]
[DynamicData(nameof(Rfc8032Ed25519phTestVectors), DynamicDataSourceType.Method)]
public void Incremental_Verify_InvalidOperation(string signature, string message, string privateKey)
{
var s = Convert.FromHexString(signature);
var m = Convert.FromHexString(message);
var sk = Convert.FromHexString(privateKey);
var pk = sk[^Ed25519.PublicKeySize..];

using var ed25519ph = new IncrementalEd25519();
ed25519ph.Update(m);
ed25519ph.FinalizeAndVerify(s, pk);

Assert.ThrowsException<InvalidOperationException>(() => ed25519ph.Update(m));
Assert.ThrowsException<InvalidOperationException>(() => ed25519ph.Finalize(s, sk));
Assert.ThrowsException<InvalidOperationException>(() => ed25519ph.FinalizeAndVerify(s, pk));
}
}
34 changes: 34 additions & 0 deletions src/Geralt.Tests/Poly1305Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -189,4 +189,38 @@ public void Incremental_Invalid(int tagSize, int messageSize, int keySize)
Assert.ThrowsException<ArgumentOutOfRangeException>(() => poly1305.FinalizeAndVerify(t));
}
}

[TestMethod]
[DynamicData(nameof(Rfc8439TestVectors), DynamicDataSourceType.Method)]
public void Incremental_Compute_InvalidOperation(string tag, string message, string oneTimeKey)
{
var t = new byte[Poly1305.TagSize];
var m = Convert.FromHexString(message);
var k = Convert.FromHexString(oneTimeKey);

using var poly1305 = new IncrementalPoly1305(k);
poly1305.Update(m);
poly1305.Finalize(t);

Assert.ThrowsException<InvalidOperationException>(() => poly1305.Update(m));
Assert.ThrowsException<InvalidOperationException>(() => poly1305.Finalize(t));
Assert.ThrowsException<InvalidOperationException>(() => poly1305.FinalizeAndVerify(t));
}

[TestMethod]
[DynamicData(nameof(Rfc8439TestVectors), DynamicDataSourceType.Method)]
public void Incremental_Verify_InvalidOperation(string tag, string message, string oneTimeKey)
{
var t = Convert.FromHexString(tag);
var m = Convert.FromHexString(message);
var k = Convert.FromHexString(oneTimeKey);

using var poly1305 = new IncrementalPoly1305(k);
poly1305.Update(m);
poly1305.FinalizeAndVerify(t);

Assert.ThrowsException<InvalidOperationException>(() => poly1305.Update(m));
Assert.ThrowsException<InvalidOperationException>(() => poly1305.Finalize(t));
Assert.ThrowsException<InvalidOperationException>(() => poly1305.FinalizeAndVerify(t));
}
}
4 changes: 4 additions & 0 deletions src/Geralt.Tests/XChaCha20Poly1305Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -267,9 +267,13 @@ public void Incremental_InvalidOperation()

using var encryptor = new IncrementalXChaCha20Poly1305(decryption: false, h, k);
Assert.ThrowsException<InvalidOperationException>(() => encryptor.Pull(p, c));
encryptor.Push(c, p, IncrementalXChaCha20Poly1305.ChunkFlag.Final);
Assert.ThrowsException<InvalidOperationException>(() => encryptor.Push(c, p));

using var decryptor = new IncrementalXChaCha20Poly1305(decryption: true, h, k);
Assert.ThrowsException<InvalidOperationException>(() => decryptor.Push(c, p));
decryptor.Pull(p, c);
Assert.ThrowsException<InvalidOperationException>(() => decryptor.Pull(p, c));
}

[TestMethod]
Expand Down
6 changes: 6 additions & 0 deletions src/Geralt/Crypto/IncrementalBLAKE2b.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ public sealed class IncrementalBLAKE2b : IDisposable

private crypto_generichash_blake2b_state _state;
private readonly int _hashSize;
private bool _finalized;

public IncrementalBLAKE2b(int hashSize, ReadOnlySpan<byte> key = default)
{
Validation.SizeBetween(nameof(hashSize), hashSize, MinHashSize, MaxHashSize);
if (key.Length != 0) { Validation.SizeBetween(nameof(key), key.Length, MinKeySize, MaxKeySize); }
Sodium.Initialize();
_hashSize = hashSize;
_finalized = false;
Initialize(key);
}

Expand All @@ -38,6 +40,7 @@ private unsafe void Initialize(ReadOnlySpan<byte> key)

public unsafe void Update(ReadOnlySpan<byte> message)
{
if (_finalized) { throw new InvalidOperationException("Cannot update after finalizing."); }
fixed (byte* m = message)
{
int ret = crypto_generichash_update(ref _state, m, (ulong)message.Length);
Expand All @@ -47,7 +50,9 @@ public unsafe void Update(ReadOnlySpan<byte> message)

public unsafe void Finalize(Span<byte> hash)
{
if (_finalized) { throw new InvalidOperationException("Cannot finalize twice."); }
Validation.EqualToSize(nameof(hash), hash.Length, _hashSize);
_finalized = true;
fixed (byte* h = hash)
{
int ret = crypto_generichash_final(ref _state, h, (nuint)hash.Length);
Expand All @@ -57,6 +62,7 @@ public unsafe void Finalize(Span<byte> hash)

public bool FinalizeAndVerify(ReadOnlySpan<byte> hash)
{
if (_finalized) { throw new InvalidOperationException("Cannot finalize twice."); }
Validation.EqualToSize(nameof(hash), hash.Length, _hashSize);
Span<byte> computedHash = stackalloc byte[_hashSize];
Finalize(computedHash);
Expand Down
7 changes: 7 additions & 0 deletions src/Geralt/Crypto/IncrementalEd25519.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ public sealed class IncrementalEd25519 : IDisposable
public const int SignatureSize = Ed25519.SignatureSize;

private crypto_sign_state _state;
private bool _finalized;

public IncrementalEd25519()
{
Sodium.Initialize();
_finalized = false;
Initialize();
}

Expand All @@ -25,6 +27,7 @@ private void Initialize()

public unsafe void Update(ReadOnlySpan<byte> message)
{
if (_finalized) { throw new InvalidOperationException("Cannot update after finalizing."); }
fixed (byte* m = message)
{
int ret = crypto_sign_update(ref _state, m, (ulong)message.Length);
Expand All @@ -34,8 +37,10 @@ public unsafe void Update(ReadOnlySpan<byte> message)

public unsafe void Finalize(Span<byte> signature, ReadOnlySpan<byte> privateKey)
{
if (_finalized) { throw new InvalidOperationException("Cannot finalize twice."); }
Validation.EqualToSize(nameof(signature), signature.Length, SignatureSize);
Validation.EqualToSize(nameof(privateKey), privateKey.Length, PrivateKeySize);
_finalized = true;
fixed (byte* s = signature, sk = privateKey)
{
int ret = crypto_sign_final_create(ref _state, s, signatureLength: out _, sk);
Expand All @@ -45,8 +50,10 @@ public unsafe void Finalize(Span<byte> signature, ReadOnlySpan<byte> privateKey)

public unsafe bool FinalizeAndVerify(ReadOnlySpan<byte> signature, ReadOnlySpan<byte> publicKey)
{
if (_finalized) { throw new InvalidOperationException("Cannot finalize twice."); }
Validation.EqualToSize(nameof(signature), signature.Length, SignatureSize);
Validation.EqualToSize(nameof(publicKey), publicKey.Length, PublicKeySize);
_finalized = true;
fixed (byte* s = signature, pk = publicKey)
{
return crypto_sign_final_verify(ref _state, s, pk) == 0;
Expand Down
6 changes: 6 additions & 0 deletions src/Geralt/Crypto/IncrementalPoly1305.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ public sealed class IncrementalPoly1305 : IDisposable
public const int TagSize = Poly1305.TagSize;

private crypto_onetimeauth_state _state;
private bool _finalized;

public IncrementalPoly1305(ReadOnlySpan<byte> oneTimeKey)
{
Validation.EqualToSize(nameof(oneTimeKey), oneTimeKey.Length, KeySize);
Sodium.Initialize();
_finalized = false;
Initialize(oneTimeKey);
}

Expand All @@ -28,6 +30,7 @@ private unsafe void Initialize(ReadOnlySpan<byte> oneTimeKey)

public unsafe void Update(ReadOnlySpan<byte> message)
{
if (_finalized) { throw new InvalidOperationException("Cannot update after finalizing."); }
fixed (byte* m = message)
{
int ret = crypto_onetimeauth_update(ref _state, m, (ulong)message.Length);
Expand All @@ -37,7 +40,9 @@ public unsafe void Update(ReadOnlySpan<byte> message)

public unsafe void Finalize(Span<byte> tag)
{
if (_finalized) { throw new InvalidOperationException("Cannot finalize twice."); }
Validation.EqualToSize(nameof(tag), tag.Length, TagSize);
_finalized = true;
fixed (byte* t = tag)
{
int ret = crypto_onetimeauth_final(ref _state, t);
Expand All @@ -47,6 +52,7 @@ public unsafe void Finalize(Span<byte> tag)

public bool FinalizeAndVerify(ReadOnlySpan<byte> tag)
{
if (_finalized) { throw new InvalidOperationException("Cannot finalize twice."); }
Validation.EqualToSize(nameof(tag), tag.Length, TagSize);
Span<byte> computedTag = stackalloc byte[TagSize];
Finalize(computedTag);
Expand Down
8 changes: 7 additions & 1 deletion src/Geralt/Crypto/IncrementalXChaCha20Poly1305.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public sealed class IncrementalXChaCha20Poly1305 : IDisposable

private crypto_secretstream_xchacha20poly1305_state _state;
private readonly bool _decryption;
private bool _finalized;

public enum ChunkFlag
{
Expand All @@ -22,10 +23,11 @@ public enum ChunkFlag

public IncrementalXChaCha20Poly1305(bool decryption, Span<byte> header, ReadOnlySpan<byte> key)
{
_decryption = decryption;
Validation.EqualToSize(nameof(header), header.Length, HeaderSize);
Validation.EqualToSize(nameof(key), key.Length, KeySize);
Sodium.Initialize();
_decryption = decryption;
_finalized = false;
Initialize(header, key);
}

Expand All @@ -48,7 +50,9 @@ public void Push(Span<byte> ciphertextChunk, ReadOnlySpan<byte> plaintextChunk,
public unsafe void Push(Span<byte> ciphertextChunk, ReadOnlySpan<byte> plaintextChunk, ReadOnlySpan<byte> associatedData, ChunkFlag chunkFlag = ChunkFlag.Message)
{
if (_decryption) { throw new InvalidOperationException("Cannot push into a decryption stream."); }
if (_finalized) { throw new InvalidOperationException("Cannot push after the final chunk."); }
Validation.EqualToSize(nameof(ciphertextChunk), ciphertextChunk.Length, plaintextChunk.Length + TagSize);
if (chunkFlag == ChunkFlag.Final) { _finalized = true; }
fixed (byte* c = ciphertextChunk, p = plaintextChunk, ad = associatedData)
{
int ret = crypto_secretstream_xchacha20poly1305_push(ref _state, c, out _, p, (ulong)plaintextChunk.Length, ad, (ulong)associatedData.Length, (byte)chunkFlag);
Expand All @@ -59,12 +63,14 @@ public unsafe void Push(Span<byte> ciphertextChunk, ReadOnlySpan<byte> plaintext
public unsafe ChunkFlag Pull(Span<byte> plaintextChunk, ReadOnlySpan<byte> ciphertextChunk, ReadOnlySpan<byte> associatedData = default)
{
if (!_decryption) { throw new InvalidOperationException("Cannot pull from an encryption stream."); }
if (_finalized) { throw new InvalidOperationException("Cannot pull after the final chunk."); }
Validation.NotLessThanMin(nameof(ciphertextChunk), ciphertextChunk.Length, TagSize);
Validation.EqualToSize(nameof(plaintextChunk), plaintextChunk.Length, ciphertextChunk.Length - TagSize);
fixed (byte* p = plaintextChunk, c = ciphertextChunk, ad = associatedData)
{
int ret = crypto_secretstream_xchacha20poly1305_pull(ref _state, p, out _, out byte chunkFlag, c, (ulong)ciphertextChunk.Length, ad, (ulong)associatedData.Length);
if (ret != 0) { throw new CryptographicException("Error decrypting ciphertext chunk."); }
if (chunkFlag == (byte)ChunkFlag.Final) { _finalized = true; }
return (ChunkFlag)chunkFlag;
}
}
Expand Down

0 comments on commit 7976d61

Please sign in to comment.