diff --git a/crypto/src/tls/CertificateCompressionAlgorithm.cs b/crypto/src/tls/CertificateCompressionAlgorithm.cs
new file mode 100644
index 000000000..4b6ebdadc
--- /dev/null
+++ b/crypto/src/tls/CertificateCompressionAlgorithm.cs
@@ -0,0 +1,47 @@
+using System;
+
+namespace Org.BouncyCastle.Tls
+{
+ /**
+ * RFC 8879
+ */
+ public abstract class CertificateCompressionAlgorithm
+ {
+ public const int zlib = 1;
+ public const int brotli = 2;
+ public const int zstd = 3;
+
+ public static string GetName(int certificateCompressionAlgorithm)
+ {
+ switch (certificateCompressionAlgorithm)
+ {
+ case zlib:
+ return "zlib";
+ case brotli:
+ return "brotli";
+ case zstd:
+ return "zstd";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ public static string GetText(int certificateCompressionAlgorithm)
+ {
+ return GetName(certificateCompressionAlgorithm) + "(" + certificateCompressionAlgorithm + ")";
+ }
+
+ public static bool IsRecognized(int certificateCompressionAlgorithm)
+ {
+ switch (certificateCompressionAlgorithm)
+ {
+ case zlib:
+ case brotli:
+ case zstd:
+ return true;
+ default:
+ return false;
+ }
+ }
+ }
+}
diff --git a/crypto/src/tls/ExtensionType.cs b/crypto/src/tls/ExtensionType.cs
index 87f6a7574..a1f1fa7e8 100644
--- a/crypto/src/tls/ExtensionType.cs
+++ b/crypto/src/tls/ExtensionType.cs
@@ -107,6 +107,11 @@ namespace Org.BouncyCastle.Tls
public const int cached_info = 25;
/*
+ * RFC 8879
+ */
+ public const int compress_certificate = 27;
+
+ /*
* RFC 8449
*/
public const int record_size_limit = 28;
@@ -191,6 +196,8 @@ namespace Org.BouncyCastle.Tls
return "token_binding";
case cached_info:
return "cached_info";
+ case compress_certificate:
+ return "compress_certificate";
case record_size_limit:
return "record_size_limit";
case session_ticket:
@@ -257,6 +264,7 @@ namespace Org.BouncyCastle.Tls
case extended_master_secret:
case token_binding:
case cached_info:
+ case compress_certificate:
case record_size_limit:
case session_ticket:
case pre_shared_key:
diff --git a/crypto/src/tls/HandshakeType.cs b/crypto/src/tls/HandshakeType.cs
index 563cd1150..ad2c29c07 100644
--- a/crypto/src/tls/HandshakeType.cs
+++ b/crypto/src/tls/HandshakeType.cs
@@ -44,6 +44,11 @@ namespace Org.BouncyCastle.Tls
public const short key_update = 24;
public const short message_hash = 254;
+ /*
+ * RFC 8879
+ */
+ public const short compressed_certificate = 25;
+
public static string GetName(short handshakeType)
{
switch (handshakeType)
@@ -88,6 +93,8 @@ namespace Org.BouncyCastle.Tls
return "key_update";
case message_hash:
return "message_hash";
+ case compressed_certificate:
+ return "compressed_certificate";
default:
return "UNKNOWN";
}
@@ -122,6 +129,7 @@ namespace Org.BouncyCastle.Tls
case encrypted_extensions:
case key_update:
case message_hash:
+ case compressed_certificate:
return true;
default:
return false;
diff --git a/crypto/src/tls/TlsClientProtocol.cs b/crypto/src/tls/TlsClientProtocol.cs
index c132b257b..19e2eda3d 100644
--- a/crypto/src/tls/TlsClientProtocol.cs
+++ b/crypto/src/tls/TlsClientProtocol.cs
@@ -315,6 +315,7 @@ namespace Org.BouncyCastle.Tls
case HandshakeType.certificate_url:
case HandshakeType.client_hello:
case HandshakeType.client_key_exchange:
+ case HandshakeType.compressed_certificate:
case HandshakeType.end_of_early_data:
case HandshakeType.hello_request:
case HandshakeType.hello_verify_request:
@@ -743,6 +744,7 @@ namespace Org.BouncyCastle.Tls
case HandshakeType.certificate_verify:
case HandshakeType.client_hello:
case HandshakeType.client_key_exchange:
+ case HandshakeType.compressed_certificate:
case HandshakeType.encrypted_extensions:
case HandshakeType.end_of_early_data:
case HandshakeType.hello_verify_request:
diff --git a/crypto/src/tls/TlsExtensionsUtilities.cs b/crypto/src/tls/TlsExtensionsUtilities.cs
index 5a13d8d2e..e1db93016 100644
--- a/crypto/src/tls/TlsExtensionsUtilities.cs
+++ b/crypto/src/tls/TlsExtensionsUtilities.cs
@@ -53,6 +53,12 @@ namespace Org.BouncyCastle.Tls
}
/// <exception cref="IOException"/>
+ public static void AddCompressCertificateExtension(IDictionary extensions, int[] algorithms)
+ {
+ extensions[ExtensionType.compress_certificate] = CreateCompressCertificateExtension(algorithms);
+ }
+
+ /// <exception cref="IOException"/>
public static void AddCookieExtension(IDictionary extensions, byte[] cookie)
{
extensions[ExtensionType.cookie] = CreateCookieExtension(cookie);
@@ -280,6 +286,13 @@ namespace Org.BouncyCastle.Tls
}
/// <exception cref="IOException"/>
+ public static int[] GetCompressCertificateExtension(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.compress_certificate);
+ return extensionData == null ? null : ReadCompressCertificateExtension(extensionData);
+ }
+
+ /// <exception cref="IOException"/>
public static byte[] GetCookieExtension(IDictionary extensions)
{
byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.cookie);
@@ -580,6 +593,15 @@ namespace Org.BouncyCastle.Tls
}
/// <exception cref="IOException"/>
+ public static byte[] CreateCompressCertificateExtension(int[] algorithms)
+ {
+ if (TlsUtilities.IsNullOrEmpty(algorithms) || algorithms.Length > 127)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ return TlsUtilities.EncodeUint16ArrayWithUint8Length(algorithms);
+ }
+
+ /// <exception cref="IOException"/>
public static byte[] CreateCookieExtension(byte[] cookie)
{
if (TlsUtilities.IsNullOrEmpty(cookie) || cookie.Length >= (1 << 16))
@@ -995,6 +1017,16 @@ namespace Org.BouncyCastle.Tls
}
/// <exception cref="IOException"/>
+ public static int[] ReadCompressCertificateExtension(byte[] extensionData)
+ {
+ int[] algorithms = TlsUtilities.DecodeUint16ArrayWithUint8Length(extensionData);
+ if (algorithms.Length < 1)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ return algorithms;
+ }
+
+ /// <exception cref="IOException"/>
public static byte[] ReadCookieExtension(byte[] extensionData)
{
return TlsUtilities.DecodeOpaque16(extensionData, 1);
diff --git a/crypto/src/tls/TlsServerProtocol.cs b/crypto/src/tls/TlsServerProtocol.cs
index 40218a2fb..22700a277 100644
--- a/crypto/src/tls/TlsServerProtocol.cs
+++ b/crypto/src/tls/TlsServerProtocol.cs
@@ -834,6 +834,7 @@ namespace Org.BouncyCastle.Tls
case HandshakeType.certificate_status:
case HandshakeType.certificate_url:
case HandshakeType.client_key_exchange:
+ case HandshakeType.compressed_certificate:
case HandshakeType.encrypted_extensions:
case HandshakeType.end_of_early_data:
case HandshakeType.hello_request:
@@ -1201,6 +1202,7 @@ namespace Org.BouncyCastle.Tls
case HandshakeType.certificate_request:
case HandshakeType.certificate_status:
case HandshakeType.certificate_url:
+ case HandshakeType.compressed_certificate:
case HandshakeType.encrypted_extensions:
case HandshakeType.end_of_early_data:
case HandshakeType.hello_request:
diff --git a/crypto/src/tls/TlsUtilities.cs b/crypto/src/tls/TlsUtilities.cs
index 8733fd68f..f48c7e731 100644
--- a/crypto/src/tls/TlsUtilities.cs
+++ b/crypto/src/tls/TlsUtilities.cs
@@ -486,6 +486,14 @@ namespace Org.BouncyCastle.Tls
}
}
+ public static void WriteUint16ArrayWithUint8Length(int[] u16s, byte[] buf, int offset)
+ {
+ int length = 2 * u16s.Length;
+ CheckUint8(length);
+ WriteUint8(length, buf, offset);
+ WriteUint16Array(u16s, buf, offset + 1);
+ }
+
public static void WriteUint16ArrayWithUint16Length(int[] u16s, Stream output)
{
int length = 2 * u16s.Length;
@@ -577,6 +585,25 @@ namespace Org.BouncyCastle.Tls
return ReadUint16(buf, 0);
}
+ public static int[] DecodeUint16ArrayWithUint8Length(byte[] buf)
+ {
+ if (buf == null)
+ throw new ArgumentNullException("buf");
+
+ int length = ReadUint8(buf, 0);
+ if (buf.Length != (length + 1) || (length & 1) != 0)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ int count = length / 2, pos = 1;
+ int[] u16s = new int[count];
+ for (int i = 0; i < count; ++i)
+ {
+ u16s[i] = ReadUint16(buf, pos);
+ pos += 2;
+ }
+ return u16s;
+ }
+
public static long DecodeUint32(byte[] buf)
{
if (buf == null)
@@ -636,6 +663,14 @@ namespace Org.BouncyCastle.Tls
return encoding;
}
+ public static byte[] EncodeUint16ArrayWithUint8Length(int[] u16s)
+ {
+ int length = 2 * u16s.Length;
+ byte[] result = new byte[1 + length];
+ WriteUint16ArrayWithUint8Length(u16s, result, 0);
+ return result;
+ }
+
public static byte[] EncodeUint16ArrayWithUint16Length(int[] u16s)
{
int length = 2 * u16s.Length;
@@ -5348,6 +5383,7 @@ namespace Org.BouncyCastle.Tls
}
}
case ExtensionType.signature_algorithms:
+ case ExtensionType.compress_certificate:
case ExtensionType.certificate_authorities:
case ExtensionType.signature_algorithms_cert:
{
|