diff options
-rw-r--r-- | crypto/crypto.csproj | 5 | ||||
-rw-r--r-- | crypto/src/crypto/tls/CertChainType.cs | 6 | ||||
-rw-r--r-- | crypto/src/crypto/tls/CertificateStatusType.cs | 2 | ||||
-rw-r--r-- | crypto/src/crypto/tls/ChangeCipherSpec.cs | 2 | ||||
-rw-r--r-- | crypto/src/crypto/tls/ClientAuthenticationType.cs | 6 | ||||
-rw-r--r-- | crypto/src/crypto/tls/ECBasisType.cs | 6 | ||||
-rw-r--r-- | crypto/src/crypto/tls/HeartbeatMessageType.cs | 6 | ||||
-rw-r--r-- | crypto/src/crypto/tls/HeartbeatMode.cs | 6 | ||||
-rw-r--r-- | crypto/src/crypto/tls/MaxFragmentLength.cs | 10 | ||||
-rw-r--r-- | crypto/src/crypto/tls/NameType.cs | 2 | ||||
-rw-r--r-- | crypto/src/crypto/tls/NamedCurve.cs | 23 | ||||
-rw-r--r-- | crypto/src/crypto/tls/TlsECDheKeyExchange.cs | 2 | ||||
-rw-r--r-- | crypto/src/crypto/tls/TlsEccUtilities.cs | 644 | ||||
-rw-r--r-- | crypto/src/crypto/tls/TlsProtocolHandler.cs | 2 | ||||
-rw-r--r-- | crypto/src/crypto/tls/TlsUtilities.cs | 146 | ||||
-rw-r--r-- | crypto/src/crypto/tls/UserMappingType.cs | 2 | ||||
-rw-r--r-- | crypto/src/util/Arrays.cs | 102 |
17 files changed, 901 insertions, 71 deletions
diff --git a/crypto/crypto.csproj b/crypto/crypto.csproj index 126f5f1d4..83d0305c2 100644 --- a/crypto/crypto.csproj +++ b/crypto/crypto.csproj @@ -4604,6 +4604,11 @@ BuildAction = "Compile" /> <File + RelPath = "src\crypto\tls\TlsEccUtilities.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File RelPath = "src\crypto\tls\TlsECDheKeyExchange.cs" SubType = "Code" BuildAction = "Compile" diff --git a/crypto/src/crypto/tls/CertChainType.cs b/crypto/src/crypto/tls/CertChainType.cs index b526a79a7..cbb183441 100644 --- a/crypto/src/crypto/tls/CertChainType.cs +++ b/crypto/src/crypto/tls/CertChainType.cs @@ -7,10 +7,10 @@ namespace Org.BouncyCastle.Crypto.Tls */ public abstract class CertChainType { - public const short individual_certs = 0; - public const short pkipath = 1; + public const byte individual_certs = 0; + public const byte pkipath = 1; - public static bool IsValid(short certChainType) + public static bool IsValid(byte certChainType) { return certChainType >= individual_certs && certChainType <= pkipath; } diff --git a/crypto/src/crypto/tls/CertificateStatusType.cs b/crypto/src/crypto/tls/CertificateStatusType.cs index 222d31635..54b741b42 100644 --- a/crypto/src/crypto/tls/CertificateStatusType.cs +++ b/crypto/src/crypto/tls/CertificateStatusType.cs @@ -7,6 +7,6 @@ namespace Org.BouncyCastle.Crypto.Tls /* * RFC 3546 3.6 */ - public const short ocsp = 1; + public const byte ocsp = 1; } } diff --git a/crypto/src/crypto/tls/ChangeCipherSpec.cs b/crypto/src/crypto/tls/ChangeCipherSpec.cs index 2ef4c3b34..323de9162 100644 --- a/crypto/src/crypto/tls/ChangeCipherSpec.cs +++ b/crypto/src/crypto/tls/ChangeCipherSpec.cs @@ -4,6 +4,6 @@ namespace Org.BouncyCastle.Crypto.Tls { public abstract class ChangeCipherSpec { - public const short change_cipher_spec = 1; + public const byte change_cipher_spec = 1; } } diff --git a/crypto/src/crypto/tls/ClientAuthenticationType.cs b/crypto/src/crypto/tls/ClientAuthenticationType.cs index 51e6e5005..dd248f3df 100644 --- a/crypto/src/crypto/tls/ClientAuthenticationType.cs +++ b/crypto/src/crypto/tls/ClientAuthenticationType.cs @@ -7,8 +7,8 @@ namespace Org.BouncyCastle.Crypto.Tls /* * RFC 5077 4 */ - public const short anonymous = 0; - public const short certificate_based = 1; - public const short psk = 2; + public const byte anonymous = 0; + public const byte certificate_based = 1; + public const byte psk = 2; } } diff --git a/crypto/src/crypto/tls/ECBasisType.cs b/crypto/src/crypto/tls/ECBasisType.cs index b7c9c6bd7..5416e17c0 100644 --- a/crypto/src/crypto/tls/ECBasisType.cs +++ b/crypto/src/crypto/tls/ECBasisType.cs @@ -5,10 +5,10 @@ namespace Org.BouncyCastle.Crypto.Tls /// <summary>RFC 4492 5.4. (Errata ID: 2389)</summary> public abstract class ECBasisType { - public const short ec_basis_trinomial = 1; - public const short ec_basis_pentanomial = 2; + public const byte ec_basis_trinomial = 1; + public const byte ec_basis_pentanomial = 2; - public static bool IsValid(short ecBasisType) + public static bool IsValid(byte ecBasisType) { return ecBasisType >= ec_basis_trinomial && ecBasisType <= ec_basis_pentanomial; } diff --git a/crypto/src/crypto/tls/HeartbeatMessageType.cs b/crypto/src/crypto/tls/HeartbeatMessageType.cs index 9e3ad213c..57a4b86be 100644 --- a/crypto/src/crypto/tls/HeartbeatMessageType.cs +++ b/crypto/src/crypto/tls/HeartbeatMessageType.cs @@ -7,10 +7,10 @@ namespace Org.BouncyCastle.Crypto.Tls */ public abstract class HeartbeatMessageType { - public const short heartbeat_request = 1; - public const short heartbeat_response = 2; + public const byte heartbeat_request = 1; + public const byte heartbeat_response = 2; - public static bool IsValid(short heartbeatMessageType) + public static bool IsValid(byte heartbeatMessageType) { return heartbeatMessageType >= heartbeat_request && heartbeatMessageType <= heartbeat_response; } diff --git a/crypto/src/crypto/tls/HeartbeatMode.cs b/crypto/src/crypto/tls/HeartbeatMode.cs index 0968f6e10..f1570a84d 100644 --- a/crypto/src/crypto/tls/HeartbeatMode.cs +++ b/crypto/src/crypto/tls/HeartbeatMode.cs @@ -7,10 +7,10 @@ namespace Org.BouncyCastle.Crypto.Tls */ public abstract class HeartbeatMode { - public const short peer_allowed_to_send = 1; - public const short peer_not_allowed_to_send = 2; + public const byte peer_allowed_to_send = 1; + public const byte peer_not_allowed_to_send = 2; - public static bool IsValid(short heartbeatMode) + public static bool IsValid(byte heartbeatMode) { return heartbeatMode >= peer_allowed_to_send && heartbeatMode <= peer_not_allowed_to_send; } diff --git a/crypto/src/crypto/tls/MaxFragmentLength.cs b/crypto/src/crypto/tls/MaxFragmentLength.cs index adb6d129c..5b10b35dd 100644 --- a/crypto/src/crypto/tls/MaxFragmentLength.cs +++ b/crypto/src/crypto/tls/MaxFragmentLength.cs @@ -7,12 +7,12 @@ namespace Org.BouncyCastle.Crypto.Tls /* * RFC 3546 3.2. */ - public const short pow2_9 = 1; - public const short pow2_10 = 2; - public const short pow2_11 = 3; - public const short pow2_12 = 4; + public const byte pow2_9 = 1; + public const byte pow2_10 = 2; + public const byte pow2_11 = 3; + public const byte pow2_12 = 4; - public static bool IsValid(short maxFragmentLength) + public static bool IsValid(byte maxFragmentLength) { return maxFragmentLength >= pow2_9 && maxFragmentLength <= pow2_12; } diff --git a/crypto/src/crypto/tls/NameType.cs b/crypto/src/crypto/tls/NameType.cs index ffcb639d0..25f6046fc 100644 --- a/crypto/src/crypto/tls/NameType.cs +++ b/crypto/src/crypto/tls/NameType.cs @@ -7,6 +7,6 @@ namespace Org.BouncyCastle.Crypto.Tls /* * RFC 3546 3.1. */ - public const short host_name = 0; + public const byte host_name = 0; } } diff --git a/crypto/src/crypto/tls/NamedCurve.cs b/crypto/src/crypto/tls/NamedCurve.cs index 8ef395069..b8aa0ecde 100644 --- a/crypto/src/crypto/tls/NamedCurve.cs +++ b/crypto/src/crypto/tls/NamedCurve.cs @@ -58,7 +58,8 @@ namespace Org.BouncyCastle.Crypto.Tls public static bool IsValid(int namedCurve) { - return namedCurve >= sect163k1 && namedCurve <= brainpoolP512r1; + return (namedCurve >= sect163k1 && namedCurve <= brainpoolP512r1) + || (namedCurve >= arbitrary_explicit_prime_curves && namedCurve <= arbitrary_explicit_char2_curves); } public static bool RefersToASpecificNamedCurve(int namedCurve) @@ -73,24 +74,4 @@ namespace Org.BouncyCastle.Crypto.Tls } } } - - internal class NamedCurveHelper - { - internal static ECDomainParameters GetECParameters(int namedCurve) - { - if (!NamedCurve.IsValid(namedCurve)) - return null; - - string curveName = namedCurve.ToString(); - - // Lazily created the first time a particular curve is accessed - X9ECParameters ecP = SecNamedCurves.GetByName(curveName); - - if (ecP == null) - return null; - - // It's a bit inefficient to do this conversion every time - return new ECDomainParameters(ecP.Curve, ecP.G, ecP.N, ecP.H, ecP.GetSeed()); - } - } } diff --git a/crypto/src/crypto/tls/TlsECDheKeyExchange.cs b/crypto/src/crypto/tls/TlsECDheKeyExchange.cs index a671ebfbe..a36bff75b 100644 --- a/crypto/src/crypto/tls/TlsECDheKeyExchange.cs +++ b/crypto/src/crypto/tls/TlsECDheKeyExchange.cs @@ -40,7 +40,7 @@ namespace Org.BouncyCastle.Crypto.Tls // TODO Check namedCurve is one we offered? - curve_params = NamedCurveHelper.GetECParameters(namedCurve); + curve_params = TlsEccUtilities.GetParametersForNamedCurve(namedCurve); } else { diff --git a/crypto/src/crypto/tls/TlsEccUtilities.cs b/crypto/src/crypto/tls/TlsEccUtilities.cs new file mode 100644 index 000000000..a306fdb45 --- /dev/null +++ b/crypto/src/crypto/tls/TlsEccUtilities.cs @@ -0,0 +1,644 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Agreement; +using Org.BouncyCastle.Crypto.EC; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Math.Field; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class TlsEccUtilities + { + private static readonly string[] curveNames = new string[] { "sect163k1", "sect163r1", "sect163r2", "sect193r1", + "sect193r2", "sect233k1", "sect233r1", "sect239k1", "sect283k1", "sect283r1", "sect409k1", "sect409r1", + "sect571k1", "sect571r1", "secp160k1", "secp160r1", "secp160r2", "secp192k1", "secp192r1", "secp224k1", + "secp224r1", "secp256k1", "secp256r1", "secp384r1", "secp521r1", + "brainpoolP256r1", "brainpoolP384r1", "brainpoolP512r1"}; + + public static void AddSupportedEllipticCurvesExtension(Hashtable extensions, int[] namedCurves) + { + extensions[ExtensionType.elliptic_curves] = CreateSupportedEllipticCurvesExtension(namedCurves); + } + + public static void AddSupportedPointFormatsExtension(Hashtable extensions, byte[] ecPointFormats) + { + extensions[ExtensionType.ec_point_formats] = CreateSupportedPointFormatsExtension(ecPointFormats); + } + + public static int[] GetSupportedEllipticCurvesExtension(Hashtable extensions) + { + byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.elliptic_curves); + return extensionData == null ? null : ReadSupportedEllipticCurvesExtension(extensionData); + } + + public static byte[] GetSupportedPointFormatsExtension(Hashtable extensions) + { + byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.ec_point_formats); + return extensionData == null ? null : ReadSupportedPointFormatsExtension(extensionData); + } + + public static byte[] CreateSupportedEllipticCurvesExtension(int[] namedCurves) + { + if (namedCurves == null || namedCurves.Length < 1) + throw new TlsFatalAlert(AlertDescription.internal_error); + + return TlsUtilities.EncodeUint16ArrayWithUint16Length(namedCurves); + } + + public static byte[] CreateSupportedPointFormatsExtension(byte[] ecPointFormats) + { + if (ecPointFormats == null || !Arrays.Contains(ecPointFormats, ECPointFormat.uncompressed)) + { + /* + * RFC 4492 5.1. If the Supported Point Formats Extension is indeed sent, it MUST + * contain the value 0 (uncompressed) as one of the items in the list of point formats. + */ + + // NOTE: We add it at the end (lowest preference) + ecPointFormats = Arrays.Append(ecPointFormats, ECPointFormat.uncompressed); + } + + return TlsUtilities.EncodeUint8ArrayWithUint8Length(ecPointFormats); + } + + public static int[] ReadSupportedEllipticCurvesExtension(byte[] extensionData) + { + if (extensionData == null) + throw new ArgumentNullException("extensionData"); + + MemoryStream buf = new MemoryStream(extensionData, false); + + int length = TlsUtilities.ReadUint16(buf); + if (length < 2 || (length & 1) != 0) + throw new TlsFatalAlert(AlertDescription.decode_error); + + int[] namedCurves = TlsUtilities.ReadUint16Array(length / 2, buf); + + TlsProtocolHandler.AssertEmpty(buf); + + return namedCurves; + } + + public static byte[] ReadSupportedPointFormatsExtension(byte[] extensionData) + { + if (extensionData == null) + throw new ArgumentNullException("extensionData"); + + MemoryStream buf = new MemoryStream(extensionData, false); + + byte length = TlsUtilities.ReadUint8(buf); + if (length < 1) + throw new TlsFatalAlert(AlertDescription.decode_error); + + byte[] ecPointFormats = TlsUtilities.ReadUint8Array(length, buf); + + TlsProtocolHandler.AssertEmpty(buf); + + if (!Arrays.Contains(ecPointFormats, ECPointFormat.uncompressed)) + { + /* + * RFC 4492 5.1. If the Supported Point Formats Extension is indeed sent, it MUST + * contain the value 0 (uncompressed) as one of the items in the list of point formats. + */ + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + return ecPointFormats; + } + + public static string GetNameOfNamedCurve(int namedCurve) + { + return IsSupportedNamedCurve(namedCurve) ? curveNames[namedCurve - 1] : null; + } + + public static ECDomainParameters GetParametersForNamedCurve(int namedCurve) + { + string curveName = GetNameOfNamedCurve(namedCurve); + if (curveName == null) + return null; + + // Parameters are lazily created the first time a particular curve is accessed + + X9ECParameters ecP = CustomNamedCurves.GetByName(curveName); + if (ecP == null) + { + ecP = ECNamedCurveTable.GetByName(curveName); + if (ecP == null) + return null; + } + + // It's a bit inefficient to do this conversion every time + return new ECDomainParameters(ecP.Curve, ecP.G, ecP.N, ecP.H, ecP.GetSeed()); + } + + public static bool HasAnySupportedNamedCurves() + { + return curveNames.Length > 0; + } + + public static bool ContainsEccCipherSuites(int[] cipherSuites) + { + for (int i = 0; i < cipherSuites.Length; ++i) + { + if (IsEccCipherSuite(cipherSuites[i])) + return true; + } + return false; + } + + public static bool IsEccCipherSuite(int cipherSuite) + { + switch (cipherSuite) + { + /* + * RFC 4492 + */ + case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDH_anon_WITH_NULL_SHA: + case CipherSuite.TLS_ECDH_anon_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDH_anon_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDH_anon_WITH_AES_256_CBC_SHA: + + /* + * RFC 5289 + */ + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384: + + /* + * RFC 5489 + */ + case CipherSuite.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA256: + case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384: + case CipherSuite.TLS_ECDHE_PSK_WITH_RC4_128_SHA: + + ///* + // * RFC 6367 + // */ + //case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256: + //case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384: + //case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256: + //case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384: + //case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256: + //case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384: + //case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256: + //case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384: + + //case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256: + //case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384: + //case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256: + //case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384: + //case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256: + //case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384: + //case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256: + //case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384: + + //case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256: + //case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384: + + ///* + // * draft-agl-tls-chacha20poly1305-04 + // */ + //case CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: + //case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + + ///* + // * draft-josefsson-salsa20-tls-04 + // */ + //case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1: + //case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1: + //case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1: + //case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_SHA1: + //case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1: + //case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1: + + return true; + + default: + return false; + } + } + + public static bool AreOnSameCurve(ECDomainParameters a, ECDomainParameters b) + { + // TODO Move to ECDomainParameters.Equals() or other utility method? + return a.Curve.Equals(b.Curve) && a.G.Equals(b.G) && a.N.Equals(b.N) && a.H.Equals(b.H); + } + + public static bool IsSupportedNamedCurve(int namedCurve) + { + return (namedCurve > 0 && namedCurve <= curveNames.Length); + } + + public static bool IsCompressionPreferred(byte[] ecPointFormats, byte compressionFormat) + { + if (ecPointFormats == null) + return false; + + for (int i = 0; i < ecPointFormats.Length; ++i) + { + byte ecPointFormat = ecPointFormats[i]; + if (ecPointFormat == ECPointFormat.uncompressed) + return false; + if (ecPointFormat == compressionFormat) + return true; + } + return false; + } + + public static byte[] SerializeECFieldElement(int fieldSize, BigInteger x) + { + return BigIntegers.AsUnsignedByteArray((fieldSize + 7) / 8, x); + } + + public static byte[] SerializeECPoint(byte[] ecPointFormats, ECPoint point) + { + ECCurve curve = point.Curve; + + /* + * RFC 4492 5.7. ...an elliptic curve point in uncompressed or compressed format. Here, the + * format MUST conform to what the server has requested through a Supported Point Formats + * Extension if this extension was used, and MUST be uncompressed if this extension was not + * used. + */ + bool compressed = false; + if (ECAlgorithms.IsFpCurve(curve)) + { + compressed = IsCompressionPreferred(ecPointFormats, ECPointFormat.ansiX962_compressed_prime); + } + else if (ECAlgorithms.IsF2mCurve(curve)) + { + compressed = IsCompressionPreferred(ecPointFormats, ECPointFormat.ansiX962_compressed_char2); + } + return point.GetEncoded(compressed); + } + + public static byte[] SerializeECPublicKey(byte[] ecPointFormats, ECPublicKeyParameters keyParameters) + { + return SerializeECPoint(ecPointFormats, keyParameters.Q); + } + + public static BigInteger DeserializeECFieldElement(int fieldSize, byte[] encoding) + { + int requiredLength = (fieldSize + 7) / 8; + if (encoding.Length != requiredLength) + throw new TlsFatalAlert(AlertDescription.decode_error); + return new BigInteger(1, encoding); + } + + public static ECPoint DeserializeECPoint(byte[] ecPointFormats, ECCurve curve, byte[] encoding) + { + if (encoding == null || encoding.Length < 1) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + byte actualFormat; + switch (encoding[0]) + { + case 0x02: // compressed + case 0x03: // compressed + { + if (ECAlgorithms.IsF2mCurve(curve)) + { + actualFormat = ECPointFormat.ansiX962_compressed_char2; + } + else if (ECAlgorithms.IsFpCurve(curve)) + { + actualFormat = ECPointFormat.ansiX962_compressed_prime; + } + else + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + break; + } + case 0x04: // uncompressed + { + actualFormat = ECPointFormat.uncompressed; + break; + } + case 0x00: // infinity + case 0x06: // hybrid + case 0x07: // hybrid + default: + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + if (!Arrays.Contains(ecPointFormats, actualFormat)) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + return curve.DecodePoint(encoding); + } + + public static ECPublicKeyParameters DeserializeECPublicKey(byte[] ecPointFormats, ECDomainParameters curve_params, + byte[] encoding) + { + try + { + ECPoint Y = DeserializeECPoint(ecPointFormats, curve_params.Curve, encoding); + return new ECPublicKeyParameters(Y, curve_params); + } + catch (Exception) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + } + + public static byte[] CalculateECDHBasicAgreement(ECPublicKeyParameters publicKey, ECPrivateKeyParameters privateKey) + { + ECDHBasicAgreement basicAgreement = new ECDHBasicAgreement(); + basicAgreement.Init(privateKey); + BigInteger agreementValue = basicAgreement.CalculateAgreement(publicKey); + + /* + * RFC 4492 5.10. Note that this octet string (Z in IEEE 1363 terminology) as output by + * FE2OSP, the Field Element to Octet String Conversion Primitive, has constant length for + * any given field; leading zeros found in this octet string MUST NOT be truncated. + */ + return BigIntegers.AsUnsignedByteArray(basicAgreement.GetFieldSize(), agreementValue); + } + + public static AsymmetricCipherKeyPair GenerateECKeyPair(SecureRandom random, ECDomainParameters ecParams) + { + ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator(); + keyPairGenerator.Init(new ECKeyGenerationParameters(ecParams, random)); + return keyPairGenerator.GenerateKeyPair(); + } + + public static ECPrivateKeyParameters GenerateEphemeralClientKeyExchange(SecureRandom random, byte[] ecPointFormats, + ECDomainParameters ecParams, Stream output) + { + AsymmetricCipherKeyPair kp = TlsEccUtilities.GenerateECKeyPair(random, ecParams); + + ECPublicKeyParameters ecPublicKey = (ECPublicKeyParameters)kp.Public; + WriteECPoint(ecPointFormats, ecPublicKey.Q, output); + + return (ECPrivateKeyParameters)kp.Private; + } + + public static ECPublicKeyParameters ValidateECPublicKey(ECPublicKeyParameters key) + { + // TODO Check RFC 4492 for validation + return key; + } + + public static int ReadECExponent(int fieldSize, Stream input) + { + BigInteger K = ReadECParameter(input); + if (K.BitLength < 32) + { + int k = K.IntValue; + if (k > 0 && k < fieldSize) + { + return k; + } + } + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + public static BigInteger ReadECFieldElement(int fieldSize, Stream input) + { + return DeserializeECFieldElement(fieldSize, TlsUtilities.ReadOpaque8(input)); + } + + public static BigInteger ReadECParameter(Stream input) + { + // TODO Are leading zeroes okay here? + return new BigInteger(1, TlsUtilities.ReadOpaque8(input)); + } + + public static ECDomainParameters ReadECParameters(int[] namedCurves, byte[] ecPointFormats, Stream input) + { + try + { + byte curveType = TlsUtilities.ReadUint8(input); + + switch (curveType) + { + case ECCurveType.explicit_prime: + { + CheckNamedCurve(namedCurves, NamedCurve.arbitrary_explicit_prime_curves); + + BigInteger prime_p = ReadECParameter(input); + BigInteger a = ReadECFieldElement(prime_p.BitLength, input); + BigInteger b = ReadECFieldElement(prime_p.BitLength, input); + byte[] baseEncoding = TlsUtilities.ReadOpaque8(input); + BigInteger order = ReadECParameter(input); + BigInteger cofactor = ReadECParameter(input); + ECCurve curve = new FpCurve(prime_p, a, b, order, cofactor); + ECPoint basePoint = DeserializeECPoint(ecPointFormats, curve, baseEncoding); + return new ECDomainParameters(curve, basePoint, order, cofactor); + } + case ECCurveType.explicit_char2: + { + CheckNamedCurve(namedCurves, NamedCurve.arbitrary_explicit_char2_curves); + + int m = TlsUtilities.ReadUint16(input); + byte basis = TlsUtilities.ReadUint8(input); + if (!ECBasisType.IsValid(basis)) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + int k1 = ReadECExponent(m, input), k2 = -1, k3 = -1; + if (basis == ECBasisType.ec_basis_pentanomial) + { + k2 = ReadECExponent(m, input); + k3 = ReadECExponent(m, input); + } + + BigInteger a = ReadECFieldElement(m, input); + BigInteger b = ReadECFieldElement(m, input); + byte[] baseEncoding = TlsUtilities.ReadOpaque8(input); + BigInteger order = ReadECParameter(input); + BigInteger cofactor = ReadECParameter(input); + + ECCurve curve = (basis == ECBasisType.ec_basis_pentanomial) + ? new F2mCurve(m, k1, k2, k3, a, b, order, cofactor) + : new F2mCurve(m, k1, a, b, order, cofactor); + + ECPoint basePoint = DeserializeECPoint(ecPointFormats, curve, baseEncoding); + + return new ECDomainParameters(curve, basePoint, order, cofactor); + } + case ECCurveType.named_curve: + { + int namedCurve = TlsUtilities.ReadUint16(input); + if (!NamedCurve.RefersToASpecificNamedCurve(namedCurve)) + { + /* + * RFC 4492 5.4. All those values of NamedCurve are allowed that refer to a + * specific curve. Values of NamedCurve that indicate support for a class of + * explicitly defined curves are not allowed here [...]. + */ + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + CheckNamedCurve(namedCurves, namedCurve); + + return GetParametersForNamedCurve(namedCurve); + } + default: + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + } + catch (Exception) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + } + + private static void CheckNamedCurve(int[] namedCurves, int namedCurve) + { + if (namedCurves != null && !Arrays.Contains(namedCurves, namedCurve)) + { + /* + * RFC 4492 4. [...] servers MUST NOT negotiate the use of an ECC cipher suite + * unless they can complete the handshake while respecting the choice of curves + * and compression techniques specified by the client. + */ + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + } + + public static void WriteECExponent(int k, Stream output) + { + BigInteger K = BigInteger.ValueOf(k); + WriteECParameter(K, output); + } + + public static void WriteECFieldElement(ECFieldElement x, Stream output) + { + TlsUtilities.WriteOpaque8(x.GetEncoded(), output); + } + + public static void WriteECFieldElement(int fieldSize, BigInteger x, Stream output) + { + TlsUtilities.WriteOpaque8(SerializeECFieldElement(fieldSize, x), output); + } + + public static void WriteECParameter(BigInteger x, Stream output) + { + TlsUtilities.WriteOpaque8(BigIntegers.AsUnsignedByteArray(x), output); + } + + public static void WriteExplicitECParameters(byte[] ecPointFormats, ECDomainParameters ecParameters, + Stream output) + { + ECCurve curve = ecParameters.Curve; + + if (ECAlgorithms.IsFpCurve(curve)) + { + TlsUtilities.WriteUint8(ECCurveType.explicit_prime, output); + + WriteECParameter(curve.Field.Characteristic, output); + } + else if (ECAlgorithms.IsF2mCurve(curve)) + { + IPolynomialExtensionField field = (IPolynomialExtensionField)curve.Field; + int[] exponents = field.MinimalPolynomial.GetExponentsPresent(); + + TlsUtilities.WriteUint8(ECCurveType.explicit_char2, output); + + int m = exponents[exponents.Length - 1]; + TlsUtilities.CheckUint16(m); + TlsUtilities.WriteUint16(m, output); + + if (exponents.Length == 3) + { + TlsUtilities.WriteUint8(ECBasisType.ec_basis_trinomial, output); + WriteECExponent(exponents[1], output); + } + else if (exponents.Length == 5) + { + TlsUtilities.WriteUint8(ECBasisType.ec_basis_pentanomial, output); + WriteECExponent(exponents[1], output); + WriteECExponent(exponents[2], output); + WriteECExponent(exponents[3], output); + } + else + { + throw new ArgumentException("Only trinomial and pentomial curves are supported"); + } + } + else + { + throw new ArgumentException("'ecParameters' not a known curve type"); + } + + WriteECFieldElement(curve.A, output); + WriteECFieldElement(curve.B, output); + TlsUtilities.WriteOpaque8(SerializeECPoint(ecPointFormats, ecParameters.G), output); + WriteECParameter(ecParameters.N, output); + WriteECParameter(ecParameters.H, output); + } + + public static void WriteECPoint(byte[] ecPointFormats, ECPoint point, Stream output) + { + TlsUtilities.WriteOpaque8(SerializeECPoint(ecPointFormats, point), output); + } + + public static void WriteNamedECParameters(int namedCurve, Stream output) + { + if (!NamedCurve.RefersToASpecificNamedCurve(namedCurve)) + { + /* + * RFC 4492 5.4. All those values of NamedCurve are allowed that refer to a specific + * curve. Values of NamedCurve that indicate support for a class of explicitly defined + * curves are not allowed here [...]. + */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + TlsUtilities.WriteUint8(ECCurveType.named_curve, output); + TlsUtilities.CheckUint16(namedCurve); + TlsUtilities.WriteUint16(namedCurve, output); + } + } +} diff --git a/crypto/src/crypto/tls/TlsProtocolHandler.cs b/crypto/src/crypto/tls/TlsProtocolHandler.cs index 4707df3b5..d40e179aa 100644 --- a/crypto/src/crypto/tls/TlsProtocolHandler.cs +++ b/crypto/src/crypto/tls/TlsProtocolHandler.cs @@ -1166,7 +1166,7 @@ namespace Org.BouncyCastle.Crypto.Tls * @param is The Stream to check. * @throws IOException If is is not empty. */ - internal void AssertEmpty( + protected internal static void AssertEmpty( MemoryStream inStr) { if (inStr.Position < inStr.Length) diff --git a/crypto/src/crypto/tls/TlsUtilities.cs b/crypto/src/crypto/tls/TlsUtilities.cs index 2309fc3da..644d079c1 100644 --- a/crypto/src/crypto/tls/TlsUtilities.cs +++ b/crypto/src/crypto/tls/TlsUtilities.cs @@ -52,43 +52,43 @@ namespace Org.BouncyCastle.Crypto.Tls return (i & 0xFFFFFF) == i; } - internal static void WriteUint8(byte i, Stream os) + public static void WriteUint8(byte i, Stream os) { os.WriteByte(i); } - internal static void WriteUint8(byte i, byte[] buf, int offset) + public static void WriteUint8(byte i, byte[] buf, int offset) { buf[offset] = i; } - internal static void WriteUint16(int i, Stream os) + public static void WriteUint16(int i, Stream os) { os.WriteByte((byte)(i >> 8)); os.WriteByte((byte)i); } - internal static void WriteUint16(int i, byte[] buf, int offset) + public static void WriteUint16(int i, byte[] buf, int offset) { buf[offset] = (byte)(i >> 8); buf[offset + 1] = (byte)i; } - internal static void WriteUint24(int i, Stream os) + public static void WriteUint24(int i, Stream os) { os.WriteByte((byte)(i >> 16)); os.WriteByte((byte)(i >> 8)); os.WriteByte((byte)i); } - internal static void WriteUint24(int i, byte[] buf, int offset) + public static void WriteUint24(int i, byte[] buf, int offset) { buf[offset] = (byte)(i >> 16); buf[offset + 1] = (byte)(i >> 8); buf[offset + 2] = (byte)(i); } - internal static void WriteUint64(long i, Stream os) + public static void WriteUint64(long i, Stream os) { os.WriteByte((byte)(i >> 56)); os.WriteByte((byte)(i >> 48)); @@ -100,7 +100,7 @@ namespace Org.BouncyCastle.Crypto.Tls os.WriteByte((byte)i); } - internal static void WriteUint64(long i, byte[] buf, int offset) + public static void WriteUint64(long i, byte[] buf, int offset) { buf[offset] = (byte)(i >> 56); buf[offset + 1] = (byte)(i >> 48); @@ -112,29 +112,38 @@ namespace Org.BouncyCastle.Crypto.Tls buf[offset + 7] = (byte)(i); } - internal static void WriteOpaque8(byte[] buf, Stream os) + public static void WriteOpaque8(byte[] buf, Stream os) { WriteUint8((byte)buf.Length, os); os.Write(buf, 0, buf.Length); } - internal static void WriteOpaque16(byte[] buf, Stream os) + public static void WriteOpaque16(byte[] buf, Stream os) { WriteUint16(buf.Length, os); os.Write(buf, 0, buf.Length); } - internal static void WriteOpaque24(byte[] buf, Stream os) + public static void WriteOpaque24(byte[] buf, Stream os) { WriteUint24(buf.Length, os); os.Write(buf, 0, buf.Length); } - internal static void WriteUint8Array(byte[] uints, Stream os) + public static void WriteUint8Array(byte[] uints, Stream os) { os.Write(uints, 0, uints.Length); } + public static void WriteUint8Array(byte[] uints, byte[] buf, int offset) + { + for (int i = 0; i < uints.Length; ++i) + { + WriteUint8(uints[i], buf, offset); + ++offset; + } + } + public static void WriteUint8ArrayWithUint8Length(byte[] uints, Stream output) { CheckUint8(uints.Length); @@ -142,7 +151,14 @@ namespace Org.BouncyCastle.Crypto.Tls WriteUint8Array(uints, output); } - internal static void WriteUint16Array(int[] uints, Stream os) + public static void WriteUint8ArrayWithUint8Length(byte[] uints, byte[] buf, int offset) + { + CheckUint8(uints.Length); + WriteUint8((byte)uints.Length, buf, offset); + WriteUint8Array(uints, buf, offset + 1); + } + + public static void WriteUint16Array(int[] uints, Stream os) { for (int i = 0; i < uints.Length; ++i) { @@ -150,7 +166,53 @@ namespace Org.BouncyCastle.Crypto.Tls } } - internal static byte ReadUint8(Stream inStr) + public static void WriteUint16Array(int[] uints, byte[] buf, int offset) + { + for (int i = 0; i < uints.Length; ++i) + { + WriteUint16(uints[i], buf, offset); + offset += 2; + } + } + + public static void WriteUint16ArrayWithUint16Length(int[] uints, Stream output) + { + int length = 2 * uints.Length; + CheckUint16(length); + WriteUint16(length, output); + WriteUint16Array(uints, output); + } + + public static void WriteUint16ArrayWithUint16Length(int[] uints, byte[] buf, int offset) + { + int length = 2 * uints.Length; + CheckUint16(length); + WriteUint16(length, buf, offset); + WriteUint16Array(uints, buf, offset + 2); + } + + public static byte[] EncodeOpaque8(byte[] buf) + { + CheckUint8(buf.Length); + return Arrays.Prepend(buf, (byte)buf.Length); + } + + public static byte[] EncodeUint8ArrayWithUint8Length(byte[] uints) + { + byte[] result = new byte[1 + uints.Length]; + WriteUint8ArrayWithUint8Length(uints, result, 0); + return result; + } + + public static byte[] EncodeUint16ArrayWithUint16Length(int[] uints) + { + int length = 2 * uints.Length; + byte[] result = new byte[2 + length]; + WriteUint16ArrayWithUint16Length(uints, result, 0); + return result; + } + + public static byte ReadUint8(Stream inStr) { int i = inStr.ReadByte(); if (i < 0) @@ -160,7 +222,7 @@ namespace Org.BouncyCastle.Crypto.Tls return (byte)i; } - internal static int ReadUint16(Stream inStr) + public static int ReadUint16(Stream inStr) { int i1 = inStr.ReadByte(); int i2 = inStr.ReadByte(); @@ -171,7 +233,7 @@ namespace Org.BouncyCastle.Crypto.Tls return i1 << 8 | i2; } - internal static int ReadUint24(Stream inStr) + public static int ReadUint24(Stream inStr) { int i1 = inStr.ReadByte(); int i2 = inStr.ReadByte(); @@ -193,13 +255,13 @@ namespace Org.BouncyCastle.Crypto.Tls return buf; } - internal static void ReadFully(byte[] buf, Stream inStr) + public static void ReadFully(byte[] buf, Stream inStr) { if (Streams.ReadFully(inStr, buf, 0, buf.Length) < buf.Length) throw new EndOfStreamException(); } - internal static byte[] ReadOpaque8(Stream inStr) + public static byte[] ReadOpaque8(Stream inStr) { byte length = ReadUint8(inStr); byte[] bytes = new byte[length]; @@ -207,7 +269,7 @@ namespace Org.BouncyCastle.Crypto.Tls return bytes; } - internal static byte[] ReadOpaque16(Stream inStr) + public static byte[] ReadOpaque16(Stream inStr) { int length = ReadUint16(inStr); byte[] bytes = new byte[length]; @@ -221,7 +283,27 @@ namespace Org.BouncyCastle.Crypto.Tls return ReadFully(length, input); } - internal static void CheckVersion(byte[] readVersion) + public static byte[] ReadUint8Array(int count, Stream input) + { + byte[] uints = new byte[count]; + for (int i = 0; i < count; ++i) + { + uints[i] = ReadUint8(input); + } + return uints; + } + + public static int[] ReadUint16Array(int count, Stream input) + { + int[] uints = new int[count]; + for (int i = 0; i < count; ++i) + { + uints[i] = ReadUint16(input); + } + return uints; + } + + public static void CheckVersion(byte[] readVersion) { if ((readVersion[0] != 3) || (readVersion[1] != 1)) { @@ -229,7 +311,7 @@ namespace Org.BouncyCastle.Crypto.Tls } } - internal static void CheckVersion(Stream inStr) + public static void CheckVersion(Stream inStr) { int i1 = inStr.ReadByte(); int i2 = inStr.ReadByte(); @@ -263,7 +345,7 @@ namespace Org.BouncyCastle.Crypto.Tls return result; } - internal static void WriteGmtUnixTime(byte[] buf, int offset) + public static void WriteGmtUnixTime(byte[] buf, int offset) { int t = (int)(DateTimeUtilities.CurrentUnixMs() / 1000L); buf[offset] = (byte)(t >> 24); @@ -272,18 +354,34 @@ namespace Org.BouncyCastle.Crypto.Tls buf[offset + 3] = (byte)t; } - internal static void WriteVersion(Stream os) + public static void WriteVersion(Stream os) { os.WriteByte(3); os.WriteByte(1); } - internal static void WriteVersion(byte[] buf, int offset) + public static void WriteVersion(byte[] buf, int offset) { buf[offset] = 3; buf[offset + 1] = 1; } + public static byte[] GetExtensionData(Hashtable extensions, int extensionType) + { + return extensions == null ? null : (byte[])extensions[extensionType]; + } + + public static bool HasExpectedEmptyExtensionData(Hashtable extensions, int extensionType, + byte alertDescription) + { + byte[] extension_data = GetExtensionData(extensions, extensionType); + if (extension_data == null) + return false; + if (extension_data.Length != 0) + throw new TlsFatalAlert(alertDescription); + return true; + } + public static void EncodeSupportedSignatureAlgorithms(IList supportedSignatureAlgorithms, bool allowAnonymous, Stream output) { diff --git a/crypto/src/crypto/tls/UserMappingType.cs b/crypto/src/crypto/tls/UserMappingType.cs index 6e6d40a58..6cff51736 100644 --- a/crypto/src/crypto/tls/UserMappingType.cs +++ b/crypto/src/crypto/tls/UserMappingType.cs @@ -8,6 +8,6 @@ namespace Org.BouncyCastle.Crypto.Tls /* * RFC 4681 */ - public const short upn_domain_hint = 64; + public const byte upn_domain_hint = 64; } } diff --git a/crypto/src/util/Arrays.cs b/crypto/src/util/Arrays.cs index 37da3940f..e629fcf65 100644 --- a/crypto/src/util/Arrays.cs +++ b/crypto/src/util/Arrays.cs @@ -366,6 +366,36 @@ namespace Org.BouncyCastle.Utilities return existing; } + public static bool Contains(byte[] a, byte n) + { + for (int i = 0; i < a.Length; ++i) + { + if (a[i] == n) + return true; + } + return false; + } + + public static bool Contains(short[] a, short n) + { + for (int i = 0; i < a.Length; ++i) + { + if (a[i] == n) + return true; + } + return false; + } + + public static bool Contains(int[] a, int n) + { + for (int i = 0; i < a.Length; ++i) + { + if (a[i] == n) + return true; + } + return false; + } + public static void Fill( byte[] buf, byte b) @@ -384,6 +414,42 @@ namespace Org.BouncyCastle.Utilities return result; } + public static byte[] Append(byte[] a, byte b) + { + if (a == null) + return new byte[] { b }; + + int length = a.Length; + byte[] result = new byte[length + 1]; + Array.Copy(a, 0, result, 0, length); + result[length] = b; + return result; + } + + public static short[] Append(short[] a, short b) + { + if (a == null) + return new short[] { b }; + + int length = a.Length; + short[] result = new short[length + 1]; + Array.Copy(a, 0, result, 0, length); + result[length] = b; + return result; + } + + public static int[] Append(int[] a, int b) + { + if (a == null) + return new int[] { b }; + + int length = a.Length; + int[] result = new int[length + 1]; + Array.Copy(a, 0, result, 0, length); + result[length] = b; + return result; + } + public static byte[] Concatenate(byte[] a, byte[] b) { if (a == null) @@ -397,6 +463,42 @@ namespace Org.BouncyCastle.Utilities return rv; } + public static byte[] Prepend(byte[] a, byte b) + { + if (a == null) + return new byte[] { b }; + + int length = a.Length; + byte[] result = new byte[length + 1]; + Array.Copy(a, 0, result, 1, length); + result[0] = b; + return result; + } + + public static short[] Prepend(short[] a, short b) + { + if (a == null) + return new short[] { b }; + + int length = a.Length; + short[] result = new short[length + 1]; + Array.Copy(a, 0, result, 1, length); + result[0] = b; + return result; + } + + public static int[] Prepend(int[] a, int b) + { + if (a == null) + return new int[] { b }; + + int length = a.Length; + int[] result = new int[length + 1]; + Array.Copy(a, 0, result, 1, length); + result[0] = b; + return result; + } + public static byte[] Reverse(byte[] a) { if (a == null) |