Skip to content

Commit

Permalink
Merge pull request #259 from CrspyAu/issue-258-capped-buffer-expansion
Browse files Browse the repository at this point in the history
Capped buffer expansion
  • Loading branch information
dvsekhvalnov authored Dec 26, 2024
2 parents 99bd052 + 5955a2e commit 980838e
Show file tree
Hide file tree
Showing 8 changed files with 64 additions and 38 deletions.
9 changes: 6 additions & 3 deletions .github/workflows/build-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
strategy:
fail-fast: false # we want to see list of failing tests for each combination
matrix:
os: [windows-latest, ubuntu-latest, macos-latest]
os: [windows-latest, ubuntu-latest, ubuntu-22.04, macos-latest]
framework: ['netcoreapp3.1', 'net5.0', 'net8.0']
exclude:
- os: macos-latest
Expand All @@ -24,8 +24,11 @@ jobs:
- os: macos-latest
framework: 'net5.0'

- os: macos-latest
framework: 'net8.0'
- os: ubuntu-latest
framework: 'net5.0'

- os: ubuntu-latest
framework: 'netcoreapp3.1'

steps:

Expand Down
17 changes: 4 additions & 13 deletions UnitTests/SecurityVulnerabilitiesTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public void InvalidCurveAttack(string keyImplementation)
byte[] y = Base64Url.Decode("e8lnCO-AlStT-NJVX-crhB7QRYhiix03illJOVAOyck");
byte[] d = Base64Url.Decode("VEmDZpDXXK8p8N0Cndsxs924q6nS1RXFASRl6BfUqdw");

var privateKey = keyImplementation == "CNG" ? (object) EccKey.New(x, y, d, usage: CngKeyUsages.KeyAgreement) : EcdhKey.New(x, y, d) ;
var privateKey = keyImplementation == "CNG" ? (object)EccKey.New(x, y, d, usage: CngKeyUsages.KeyAgreement) : EcdhKey.New(x, y, d);

//JWT encrypted with attacker private key, which is equals to (reciever_pk mod 113)
var attackMod113 =
Expand Down Expand Up @@ -171,7 +171,7 @@ public void BitLengthIntegerOverflow()
}
}

[Fact]
[Fact]
public void DeflateBomb()
{
// given
Expand All @@ -197,15 +197,7 @@ public void DeflateBomb()
string bomb = Jose.JWT.Encode(payload, publicKey, JweAlgorithm.RSA_OAEP, JweEncryption.A256GCM, JweCompression.DEF);

// when
try
{
string decoded = Jose.JWT.Decode(bomb, privateKey, JwsAlgorithm.RS256);
Assert.True(false, "Should fail with NotSupportedException");
}
catch (JoseException e)
{
Console.Out.WriteLine(e.ToString());
}
Exception thrownException = Assert.Throws<CapacityExceededException>(() => Jose.JWT.Decode(bomb, privateKey));
}

[Fact]
Expand All @@ -217,7 +209,7 @@ public void TruncatedGcmAuthTag()
try
{
// when decrypt token with trunated AES GCM tag, it should fail
Jose.JWT.Decode(token, aes128Key);
Jose.JWT.Decode(token, aes128Key);
Assert.True(false, "Should fail with IntegrityException");

}
Expand All @@ -226,6 +218,5 @@ public void TruncatedGcmAuthTag()
Console.Out.WriteLine(e.ToString());
}
}

}
}
12 changes: 8 additions & 4 deletions jose-jwt/JWT.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
using Jose;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Jose
Expand Down Expand Up @@ -499,7 +497,7 @@ public static byte[] DecryptBytes(string token, object key, JweAlgorithm? alg =

private static byte[] DecodeBytes(Compact.Iterator parts, object key = null, JwsAlgorithm? expectedJwsAlg = null, JweAlgorithm? expectedJweAlg = null, JweEncryption? expectedJweEnc = null, JwtSettings settings = null, byte[] payload = null)
{
Ensure.IsNotEmpty(parts.Token, "Incoming token expected to be in compact serialization form, not empty, whitespace or null.");
Ensure.IsNotEmpty(parts.Token, "Incoming token expected to be in compact serialization form, not empty, whitespace or null.");

if (parts.Count == 5) //encrypted JWT
{
Expand All @@ -512,7 +510,7 @@ private static byte[] DecodeBytes(Compact.Iterator parts, object key = null, Jws
}
else if (parts.Count == 3) // signed JWT
{
if (expectedJweAlg != null || expectedJweEnc !=null)
if (expectedJweAlg != null || expectedJweEnc != null)
{
throw new InvalidAlgorithmException("Signed tokens can't assert encryption type.");
}
Expand Down Expand Up @@ -610,4 +608,10 @@ public class InvalidAlgorithmException : JoseException
public InvalidAlgorithmException(string message) : base(message) { }
public InvalidAlgorithmException(string message, Exception innerException) : base(message, innerException) { }
}

public class CapacityExceededException : JoseException
{
public CapacityExceededException(string message) : base(message) { }
public CapacityExceededException(string message, Exception innerException) : base(message, innerException) { }
}
}
35 changes: 35 additions & 0 deletions jose-jwt/compression/CappedMemoryStream.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System;
using System.IO;

namespace Jose
{
public class CappedMemoryStream : MemoryStream
{
private readonly long maxCapacity;

public CappedMemoryStream(long maxCapacity)
{
this.maxCapacity = maxCapacity;
}

public override void Write(byte[] buffer, int offset, int count)
{
if (Length + Math.Min(count, buffer.Length - offset) > maxCapacity)
{
throw new CapacityExceededException("Exceeding maximum memory stream size.");
}

base.Write(buffer, offset, count);
}

public override void WriteByte(byte value)
{
if (Length + 1 > maxCapacity)
{
throw new CapacityExceededException("Exceeding maximum memory stream size.");
}

base.WriteByte(value);
}
}
}
26 changes: 8 additions & 18 deletions jose-jwt/compression/DeflateCompression.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System;
using System.IO;
using System.IO.Compression;

Expand Down Expand Up @@ -27,28 +26,19 @@ public byte[] Compress(byte[] plainText)
}

public byte[] Decompress(byte[] compressedText)
{
byte[] buffer = new byte[maxBufferSizeBytes];

try
{
using (MemoryStream ms = new CappedMemoryStream(maxBufferSizeBytes))
{
using (MemoryStream ms = new MemoryStream(buffer))
using (MemoryStream compressedStream = new MemoryStream(compressedText))
{
using (MemoryStream compressedStream = new MemoryStream(compressedText))
using (DeflateStream deflater = new DeflateStream(compressedStream, CompressionMode.Decompress))
{
using (DeflateStream deflater = new DeflateStream(compressedStream, CompressionMode.Decompress))
{
deflater.CopyTo(ms);
}
deflater.CopyTo(ms);
}
}

return Arrays.Truncate(ms.ToArray(), ms.Position);
}
}
catch(NotSupportedException e)
{
throw new JoseException("Unable to deflate compressed payload, most likely exceeded decompression buffer size.", e);
}
return ms.ToArray();
}
}
}
}
1 change: 1 addition & 0 deletions jose-jwt/jose-jwt.net40.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="compression\CappedMemoryStream.cs" />
<Compile Include="compression\DeflateCompression.cs" />
<Compile Include="compression\ICompression.cs" />
<Compile Include="crypto\AesGcm.cs" />
Expand Down
1 change: 1 addition & 0 deletions jose-jwt/jose-jwt.net46.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="compression\CappedMemoryStream.cs" />
<Compile Include="compression\DeflateCompression.cs" />
<Compile Include="compression\ICompression.cs" />
<Compile Include="crypto\AesGcm.cs" />
Expand Down
1 change: 1 addition & 0 deletions jose-jwt/jose-jwt.net47.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="compression\CappedMemoryStream.cs" />
<Compile Include="compression\DeflateCompression.cs" />
<Compile Include="compression\ICompression.cs" />
<Compile Include="crypto\AesGcm.cs" />
Expand Down

0 comments on commit 980838e

Please sign in to comment.