mirror of
https://gitee.com/jisol/jisol-game/
synced 2025-11-16 02:58:25 +00:00
提交Unity 联机Pro
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Tls.Crypto;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Tls;
|
||||
using BestHTTP.Logger;
|
||||
|
||||
namespace BestHTTP.Connections.TLS
|
||||
{
|
||||
public abstract class AbstractTls13Client : AbstractTlsClient, TlsAuthentication
|
||||
{
|
||||
protected static readonly int[] DefaultCipherSuites = new int[] {
|
||||
/*
|
||||
* TLS 1.3
|
||||
*/
|
||||
CipherSuite.TLS_CHACHA20_POLY1305_SHA256,
|
||||
CipherSuite.TLS_AES_256_GCM_SHA384,
|
||||
CipherSuite.TLS_AES_128_GCM_SHA256,
|
||||
|
||||
/*
|
||||
* pre-TLS 1.3
|
||||
*/
|
||||
CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
|
||||
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||
CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||
CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||
CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256,
|
||||
CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
};
|
||||
|
||||
protected HTTPRequest _request;
|
||||
protected List<ServerName> _sniServerNames;
|
||||
protected List<ProtocolName> _protocols;
|
||||
|
||||
protected LoggingContext Context { get; private set; }
|
||||
|
||||
protected AbstractTls13Client(HTTPRequest request, List<ServerName> sniServerNames, List<ProtocolName> protocols, TlsCrypto crypto)
|
||||
: base(crypto)
|
||||
{
|
||||
this._request = request;
|
||||
|
||||
// get the request's logging context. The context has no reference to the request, so it won't keep it in memory.
|
||||
this.Context = this._request.Context;
|
||||
|
||||
this._sniServerNames = sniServerNames;
|
||||
this._protocols = protocols;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// TCPConnector has to know what protocol got negotiated
|
||||
/// </summary>
|
||||
public string GetNegotiatedApplicationProtocol() => base.m_context.SecurityParameters.ApplicationProtocol?.GetUtf8Decoding();
|
||||
|
||||
// (Abstract)TLSClient facing functions
|
||||
|
||||
protected override ProtocolVersion[] GetSupportedVersions() => ProtocolVersion.TLSv13.DownTo(ProtocolVersion.TLSv12);
|
||||
protected override IList<ProtocolName> GetProtocolNames() => this._protocols;
|
||||
protected override IList<ServerName> GetSniServerNames() => this._sniServerNames;
|
||||
protected override int[] GetSupportedCipherSuites()
|
||||
{
|
||||
HTTPManager.Logger.Information(nameof(AbstractTls13Client), $"{nameof(GetSupportedCipherSuites)}", this.Context);
|
||||
return TlsUtilities.GetSupportedCipherSuites(Crypto, DefaultCipherSuites);
|
||||
}
|
||||
|
||||
// TlsAuthentication implementation
|
||||
public override TlsAuthentication GetAuthentication()
|
||||
{
|
||||
HTTPManager.Logger.Information(nameof(AbstractTls13Client), $"{nameof(GetAuthentication)}", this.Context);
|
||||
return this;
|
||||
}
|
||||
|
||||
public virtual TlsCredentials GetClientCredentials(CertificateRequest certificateRequest)
|
||||
{
|
||||
HTTPManager.Logger.Information(nameof(AbstractTls13Client), $"{nameof(GetClientCredentials)}", this.Context);
|
||||
return null;
|
||||
}
|
||||
|
||||
public virtual void NotifyServerCertificate(TlsServerCertificate serverCertificate)
|
||||
{
|
||||
HTTPManager.Logger.Information(nameof(AbstractTls13Client), $"{nameof(NotifyServerCertificate)}", this.Context);
|
||||
}
|
||||
|
||||
public override void NotifyAlertReceived(short alertLevel, short alertDescription)
|
||||
{
|
||||
base.NotifyAlertReceived(alertLevel, alertDescription);
|
||||
|
||||
HTTPManager.Logger.Information(nameof(AbstractTls13Client), $"{nameof(NotifyAlertReceived)}({alertLevel}, {alertDescription})", this.Context);
|
||||
}
|
||||
|
||||
public override void NotifyAlertRaised(short alertLevel, short alertDescription, string message, Exception cause)
|
||||
{
|
||||
base.NotifyAlertRaised(alertLevel, alertDescription, message, cause);
|
||||
|
||||
HTTPManager.Logger.Information(nameof(AbstractTls13Client), $"{nameof(NotifyAlertRaised)}({alertLevel}, {alertDescription}, {message}, {cause?.StackTrace})", this.Context);
|
||||
}
|
||||
|
||||
public override void NotifyHandshakeBeginning()
|
||||
{
|
||||
HTTPManager.Logger.Information(nameof(AbstractTls13Client), $"{nameof(NotifyHandshakeBeginning)}", this.Context);
|
||||
}
|
||||
|
||||
public override void NotifyHandshakeComplete()
|
||||
{
|
||||
HTTPManager.Logger.Information(nameof(AbstractTls13Client), $"{nameof(NotifyHandshakeComplete)}", this.Context);
|
||||
this._request = null;
|
||||
}
|
||||
|
||||
public override void NotifyNewSessionTicket(NewSessionTicket newSessionTicket)
|
||||
{
|
||||
HTTPManager.Logger.Information(nameof(AbstractTls13Client), $"{nameof(NotifyNewSessionTicket)}", this.Context);
|
||||
|
||||
base.NotifyNewSessionTicket(newSessionTicket);
|
||||
}
|
||||
|
||||
public override void NotifySecureRenegotiation(bool secureRenegotiation)
|
||||
{
|
||||
HTTPManager.Logger.Information(nameof(AbstractTls13Client), $"{nameof(NotifySecureRenegotiation)}", this.Context);
|
||||
|
||||
base.NotifySecureRenegotiation(secureRenegotiation);
|
||||
}
|
||||
|
||||
public override void NotifySelectedCipherSuite(int selectedCipherSuite)
|
||||
{
|
||||
HTTPManager.Logger.Information(nameof(AbstractTls13Client), $"{nameof(NotifySelectedCipherSuite)}({selectedCipherSuite})", this.Context);
|
||||
|
||||
base.NotifySelectedCipherSuite(selectedCipherSuite);
|
||||
}
|
||||
|
||||
public override void NotifySelectedPsk(TlsPsk selectedPsk)
|
||||
{
|
||||
HTTPManager.Logger.Information(nameof(AbstractTls13Client), $"{nameof(NotifySelectedPsk)}({selectedPsk?.PrfAlgorithm})", this.Context);
|
||||
|
||||
base.NotifySelectedPsk(selectedPsk);
|
||||
}
|
||||
|
||||
public override void NotifyServerVersion(ProtocolVersion serverVersion)
|
||||
{
|
||||
HTTPManager.Logger.Information(nameof(AbstractTls13Client), $"{nameof(NotifyServerVersion)}({serverVersion})", this.Context);
|
||||
|
||||
base.NotifyServerVersion(serverVersion);
|
||||
}
|
||||
|
||||
public override void NotifySessionID(byte[] sessionID)
|
||||
{
|
||||
HTTPManager.Logger.Information(nameof(AbstractTls13Client), $"{nameof(NotifySessionID)}", this.Context);
|
||||
|
||||
base.NotifySessionID(sessionID);
|
||||
}
|
||||
|
||||
public override void NotifySessionToResume(TlsSession session)
|
||||
{
|
||||
HTTPManager.Logger.Information(nameof(AbstractTls13Client), $"{nameof(NotifySessionToResume)}", this.Context);
|
||||
|
||||
base.NotifySessionToResume(session);
|
||||
}
|
||||
|
||||
public override void ProcessServerExtensions(IDictionary<int, byte[]> serverExtensions)
|
||||
{
|
||||
HTTPManager.Logger.Information(nameof(AbstractTls13Client), $"{nameof(ProcessServerExtensions)}", this.Context);
|
||||
|
||||
base.ProcessServerExtensions(serverExtensions);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a656ab93e95c3174cb0afd2f26ef7da1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 129f7b1f27cbedb43b7948eaf7a4d423
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,156 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
using System;
|
||||
|
||||
using BestHTTP.Connections.TLS.Crypto.Impl;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Engines;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Security;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Tls.Crypto;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Tls.Crypto.Impl;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Tls.Crypto.Impl.BC;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Tls;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.IO;
|
||||
|
||||
namespace BestHTTP.Connections.TLS.Crypto
|
||||
{
|
||||
public sealed class FastTlsCrypto : BcTlsCrypto
|
||||
{
|
||||
public FastTlsCrypto(SecureRandom entropySource)
|
||||
: base(entropySource)
|
||||
{
|
||||
}
|
||||
|
||||
public override TlsCipher CreateCipher(TlsCryptoParameters cryptoParams, int encryptionAlgorithm, int macAlgorithm)
|
||||
{
|
||||
HTTPManager.Logger.Verbose(nameof(FastTlsCrypto), $"CreateCipher({encryptionAlgorithm}, {macAlgorithm})");
|
||||
|
||||
switch (encryptionAlgorithm)
|
||||
{
|
||||
case EncryptionAlgorithm.CHACHA20_POLY1305:
|
||||
{
|
||||
// NOTE: Ignores macAlgorithm
|
||||
//return CreateChaCha20Poly1305(cryptoParams);
|
||||
FastBcChaCha20Poly1305 encrypt = new FastBcChaCha20Poly1305(true);
|
||||
FastBcChaCha20Poly1305 decrypt = new FastBcChaCha20Poly1305(false);
|
||||
|
||||
return new FastTlsAeadCipher(cryptoParams, encrypt, decrypt, 32, 16, TlsAeadCipher.AEAD_CHACHA20_POLY1305);
|
||||
}
|
||||
|
||||
case EncryptionAlgorithm.AES_128_CBC:
|
||||
case EncryptionAlgorithm.ARIA_128_CBC:
|
||||
case EncryptionAlgorithm.CAMELLIA_128_CBC:
|
||||
case EncryptionAlgorithm.SEED_CBC:
|
||||
case EncryptionAlgorithm.SM4_CBC:
|
||||
{
|
||||
//return CreateCipher_Cbc(cryptoParams, encryptionAlgorithm, 16, macAlgorithm);
|
||||
FastTlsBlockCipherImpl encrypt = new FastTlsBlockCipherImpl(CreateCbcBlockCipher(encryptionAlgorithm), true);
|
||||
FastTlsBlockCipherImpl decrypt = new FastTlsBlockCipherImpl(CreateCbcBlockCipher(encryptionAlgorithm), false);
|
||||
|
||||
TlsHmac clientMac = CreateMac(cryptoParams, macAlgorithm);
|
||||
TlsHmac serverMac = CreateMac(cryptoParams, macAlgorithm);
|
||||
|
||||
return new FastTlsBlockCipher(cryptoParams, encrypt, decrypt, clientMac, serverMac, 16);
|
||||
}
|
||||
|
||||
case EncryptionAlgorithm.AES_256_CBC:
|
||||
case EncryptionAlgorithm.ARIA_256_CBC:
|
||||
case EncryptionAlgorithm.CAMELLIA_256_CBC:
|
||||
{
|
||||
//return CreateCipher_Cbc(cryptoParams, encryptionAlgorithm, 32, macAlgorithm);
|
||||
FastTlsBlockCipherImpl encrypt = new FastTlsBlockCipherImpl(CreateCbcBlockCipher(encryptionAlgorithm), true);
|
||||
FastTlsBlockCipherImpl decrypt = new FastTlsBlockCipherImpl(CreateCbcBlockCipher(encryptionAlgorithm), false);
|
||||
|
||||
TlsHmac clientMac = CreateMac(cryptoParams, macAlgorithm);
|
||||
TlsHmac serverMac = CreateMac(cryptoParams, macAlgorithm);
|
||||
|
||||
return new FastTlsBlockCipher(cryptoParams, encrypt, decrypt, clientMac, serverMac, 32);
|
||||
}
|
||||
|
||||
case EncryptionAlgorithm.AES_128_CCM:
|
||||
{
|
||||
// NOTE: Ignores macAlgorithm
|
||||
//return CreateCipher_Aes_Ccm(cryptoParams, 16, 16);
|
||||
FastTlsAeadCipherImpl encrypt = new FastTlsAeadCipherImpl(CreateAeadCipher_Aes_Ccm(), true);
|
||||
FastTlsAeadCipherImpl decrypt = new FastTlsAeadCipherImpl(CreateAeadCipher_Aes_Ccm(), false);
|
||||
|
||||
return new FastTlsAeadCipher(cryptoParams, encrypt, decrypt, 16, 16, TlsAeadCipher.AEAD_CCM);
|
||||
|
||||
}
|
||||
case EncryptionAlgorithm.AES_128_CCM_8:
|
||||
{
|
||||
// NOTE: Ignores macAlgorithm
|
||||
//return CreateCipher_Aes_Ccm(cryptoParams, 16, 8);
|
||||
FastTlsAeadCipherImpl encrypt = new FastTlsAeadCipherImpl(CreateAeadCipher_Aes_Ccm(), true);
|
||||
FastTlsAeadCipherImpl decrypt = new FastTlsAeadCipherImpl(CreateAeadCipher_Aes_Ccm(), false);
|
||||
|
||||
return new FastTlsAeadCipher(cryptoParams, encrypt, decrypt, 16, 8, TlsAeadCipher.AEAD_CCM);
|
||||
}
|
||||
case EncryptionAlgorithm.AES_256_CCM:
|
||||
{
|
||||
// NOTE: Ignores macAlgorithm
|
||||
//return CreateCipher_Aes_Ccm(cryptoParams, 32, 16);
|
||||
FastTlsAeadCipherImpl encrypt = new FastTlsAeadCipherImpl(CreateAeadCipher_Aes_Ccm(), true);
|
||||
FastTlsAeadCipherImpl decrypt = new FastTlsAeadCipherImpl(CreateAeadCipher_Aes_Ccm(), false);
|
||||
|
||||
return new FastTlsAeadCipher(cryptoParams, encrypt, decrypt, 32, 16, TlsAeadCipher.AEAD_CCM);
|
||||
}
|
||||
case EncryptionAlgorithm.AES_256_CCM_8:
|
||||
{
|
||||
// NOTE: Ignores macAlgorithm
|
||||
//return CreateCipher_Aes_Ccm(cryptoParams, 32, 8);
|
||||
FastTlsAeadCipherImpl encrypt = new FastTlsAeadCipherImpl(CreateAeadCipher_Aes_Ccm(), true);
|
||||
FastTlsAeadCipherImpl decrypt = new FastTlsAeadCipherImpl(CreateAeadCipher_Aes_Ccm(), false);
|
||||
|
||||
return new FastTlsAeadCipher(cryptoParams, encrypt, decrypt, 32, 8, TlsAeadCipher.AEAD_CCM);
|
||||
}
|
||||
|
||||
case EncryptionAlgorithm.AES_128_GCM:
|
||||
{
|
||||
// NOTE: Ignores macAlgorithm
|
||||
//return CreateCipher_Aes_Gcm(cryptoParams, 16, 16);
|
||||
FastTlsAeadCipherImpl encrypt = new FastTlsAeadCipherImpl(CreateAeadCipher_Aes_Gcm(), true);
|
||||
FastTlsAeadCipherImpl decrypt = new FastTlsAeadCipherImpl(CreateAeadCipher_Aes_Gcm(), false);
|
||||
|
||||
return new FastTlsAeadCipher(cryptoParams, encrypt, decrypt, 16, 16, TlsAeadCipher.AEAD_GCM);
|
||||
}
|
||||
|
||||
case EncryptionAlgorithm.AES_256_GCM:
|
||||
{
|
||||
// NOTE: Ignores macAlgorithm
|
||||
//return CreateCipher_Aes_Gcm(cryptoParams, 32, 16);
|
||||
FastTlsAeadCipherImpl encrypt = new FastTlsAeadCipherImpl(CreateAeadCipher_Aes_Gcm(), true);
|
||||
FastTlsAeadCipherImpl decrypt = new FastTlsAeadCipherImpl(CreateAeadCipher_Aes_Gcm(), false);
|
||||
|
||||
return new FastTlsAeadCipher(cryptoParams, encrypt, decrypt, 32, 16, TlsAeadCipher.AEAD_GCM);
|
||||
}
|
||||
|
||||
default:
|
||||
return base.CreateCipher(cryptoParams, encryptionAlgorithm, macAlgorithm);
|
||||
}
|
||||
}
|
||||
|
||||
protected override IBlockCipher CreateAesEngine()
|
||||
{
|
||||
//return new AesEngine();
|
||||
return new FastAesEngine();
|
||||
}
|
||||
|
||||
protected override IAeadCipher CreateCcmMode(IBlockCipher engine)
|
||||
{
|
||||
return new FastCcmBlockCipher(engine);
|
||||
}
|
||||
|
||||
protected override IAeadCipher CreateGcmMode(IBlockCipher engine)
|
||||
{
|
||||
// TODO Consider allowing custom configuration of multiplier
|
||||
return new FastGcmBlockCipher(engine);
|
||||
}
|
||||
|
||||
protected override IBlockCipher CreateCbcBlockCipher(IBlockCipher blockCipher)
|
||||
{
|
||||
return new FastCbcBlockCipher(blockCipher);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2a2a6059d661c094db479c8d4e9ea6ef
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8a8a4ac374e70b84b87d66c99e9dcc90
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,150 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
#pragma warning disable
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes.Gcm;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Utilities;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities;
|
||||
|
||||
#if BESTHTTP_WITH_BURST
|
||||
using Unity.Burst;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
#endif
|
||||
|
||||
namespace BestHTTP.Connections.TLS.Crypto.Impl
|
||||
{
|
||||
[BestHTTP.PlatformSupport.IL2CPP.Il2CppEagerStaticClassConstructionAttribute]
|
||||
#if BESTHTTP_WITH_BURST
|
||||
[BurstCompile]
|
||||
#endif
|
||||
public sealed class BurstTables8kGcmMultiplier //: IGcmMultiplier
|
||||
{
|
||||
private byte[] H;
|
||||
private GcmUtilities.FieldElement[][] T;
|
||||
|
||||
public void Init(byte[] H)
|
||||
{
|
||||
if (T == null)
|
||||
{
|
||||
T = new GcmUtilities.FieldElement[2][];
|
||||
}
|
||||
else if (Arrays.AreEqual(this.H, H))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.H == null)
|
||||
this.H = Arrays.Clone(H);
|
||||
else
|
||||
{
|
||||
if (this.H.Length != H.Length)
|
||||
Array.Resize(ref this.H, H.Length);
|
||||
|
||||
Array.Copy(H, this.H, H.Length);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
if (T[i] == null)
|
||||
T[i] = new GcmUtilities.FieldElement[256];
|
||||
|
||||
GcmUtilities.FieldElement[] t = T[i];
|
||||
|
||||
// t[0] = 0
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
// t[1] = H.p^7
|
||||
GcmUtilities.AsFieldElement(this.H, out t[1]);
|
||||
GcmUtilities.MultiplyP7(ref t[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// t[1] = T[i-1][1].p^8
|
||||
GcmUtilities.MultiplyP8(ref T[i - 1][1], out t[1]);
|
||||
}
|
||||
|
||||
for (int n = 1; n < 128; ++n)
|
||||
{
|
||||
// t[2.n] = t[n].p^-1
|
||||
GcmUtilities.DivideP(ref t[n], out t[n << 1]);
|
||||
|
||||
// t[2.n + 1] = t[2.n] + t[1]
|
||||
GcmUtilities.Xor(ref t[n << 1], ref t[1], out t[(n << 1) + 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public unsafe void MultiplyH(byte[] x)
|
||||
{
|
||||
fixed (byte* px = x)
|
||||
fixed (GcmUtilities.FieldElement* pT0 = this.T[0])
|
||||
fixed (GcmUtilities.FieldElement* pT1 = this.T[1])
|
||||
MultiplyHImpl(px, pT0, pT1);
|
||||
}
|
||||
|
||||
#if BESTHTTP_WITH_BURST
|
||||
[BurstCompile]
|
||||
#endif
|
||||
private static unsafe void MultiplyHImpl(
|
||||
#if BESTHTTP_WITH_BURST
|
||||
[NoAlias]
|
||||
#endif
|
||||
byte* px,
|
||||
#if BESTHTTP_WITH_BURST
|
||||
[NoAlias]
|
||||
#endif
|
||||
GcmUtilities.FieldElement* pT0,
|
||||
#if BESTHTTP_WITH_BURST
|
||||
[NoAlias]
|
||||
#endif
|
||||
GcmUtilities.FieldElement* pT1)
|
||||
{
|
||||
int vPos = px[15];
|
||||
int uPos = px[14];
|
||||
ulong z1 = pT0[uPos].n1 ^ pT1[vPos].n1;
|
||||
ulong z0 = pT0[uPos].n0 ^ pT1[vPos].n0;
|
||||
|
||||
for (int i = 12; i >= 0; i -= 2)
|
||||
{
|
||||
vPos = px[i + 1];
|
||||
uPos = px[i];
|
||||
|
||||
ulong c = z1 << 48;
|
||||
z1 = pT0[uPos].n1 ^ pT1[vPos].n1 ^ ((z1 >> 16) | (z0 << 48));
|
||||
z0 = pT0[uPos].n0 ^ pT1[vPos].n0 ^ (z0 >> 16) ^ c ^ (c >> 1) ^ (c >> 2) ^ (c >> 7);
|
||||
}
|
||||
|
||||
//GcmUtilities.AsBytes(z0, z1, x);
|
||||
|
||||
//UInt32_To_BE((uint)(n >> 32), bs, off);
|
||||
uint n = (uint)(z0 >> 32);
|
||||
px[0] = (byte)(n >> 24);
|
||||
px[1] = (byte)(n >> 16);
|
||||
px[2] = (byte)(n >> 8);
|
||||
px[3] = (byte)(n);
|
||||
//UInt32_To_BE((uint)(n), bs, off + 4);
|
||||
n = (uint)(z0);
|
||||
px[4] = (byte)(n >> 24);
|
||||
px[5] = (byte)(n >> 16);
|
||||
px[6] = (byte)(n >> 8);
|
||||
px[7] = (byte)(n);
|
||||
|
||||
n = (uint)(z1 >> 32);
|
||||
px[8] = (byte)(n >> 24);
|
||||
px[9] = (byte)(n >> 16);
|
||||
px[10] = (byte)(n >> 8);
|
||||
px[11] = (byte)(n);
|
||||
//UInt32_To_BE((uint)(n), bs, off + 4);
|
||||
n = (uint)(z1);
|
||||
px[12] = (byte)(n >> 24);
|
||||
px[13] = (byte)(n >> 16);
|
||||
px[14] = (byte)(n >> 8);
|
||||
px[15] = (byte)(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5e88c42789588844893c08f62978218b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,938 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
#pragma warning disable
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Utilities;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities;
|
||||
|
||||
namespace BestHTTP.Connections.TLS.Crypto.Impl
|
||||
{
|
||||
/**
|
||||
* an implementation of the AES (Rijndael), from FIPS-197.
|
||||
* <p>
|
||||
* For further details see: <a href="http://csrc.nist.gov/encryption/aes/">http://csrc.nist.gov/encryption/aes/</a>.
|
||||
*
|
||||
* This implementation is based on optimizations from Dr. Brian Gladman's paper and C code at
|
||||
* <a href="http://fp.gladman.plus.com/cryptography_technology/rijndael/">http://fp.gladman.plus.com/cryptography_technology/rijndael/</a>
|
||||
*
|
||||
* There are three levels of tradeoff of speed vs memory
|
||||
* Because java has no preprocessor, they are written as three separate classes from which to choose
|
||||
*
|
||||
* The fastest uses 8Kbytes of static tables to precompute round calculations, 4 256 word tables for encryption
|
||||
* and 4 for decryption.
|
||||
*
|
||||
* The middle performance version uses only one 256 word table for each, for a total of 2Kbytes,
|
||||
* adding 12 rotate operations per round to compute the values contained in the other tables from
|
||||
* the contents of the first.
|
||||
*
|
||||
* The slowest version uses no static tables at all and computes the values in each round.
|
||||
* </p>
|
||||
* <p>
|
||||
* This file contains the middle performance version with 2Kbytes of static tables for round precomputation.
|
||||
* </p>
|
||||
*/
|
||||
|
||||
[BestHTTP.PlatformSupport.IL2CPP.Il2CppEagerStaticClassConstructionAttribute]
|
||||
public sealed class FastAesEngine
|
||||
: IBlockCipher
|
||||
{
|
||||
// The S box
|
||||
private static readonly byte[] S =
|
||||
{
|
||||
99, 124, 119, 123, 242, 107, 111, 197,
|
||||
48, 1, 103, 43, 254, 215, 171, 118,
|
||||
202, 130, 201, 125, 250, 89, 71, 240,
|
||||
173, 212, 162, 175, 156, 164, 114, 192,
|
||||
183, 253, 147, 38, 54, 63, 247, 204,
|
||||
52, 165, 229, 241, 113, 216, 49, 21,
|
||||
4, 199, 35, 195, 24, 150, 5, 154,
|
||||
7, 18, 128, 226, 235, 39, 178, 117,
|
||||
9, 131, 44, 26, 27, 110, 90, 160,
|
||||
82, 59, 214, 179, 41, 227, 47, 132,
|
||||
83, 209, 0, 237, 32, 252, 177, 91,
|
||||
106, 203, 190, 57, 74, 76, 88, 207,
|
||||
208, 239, 170, 251, 67, 77, 51, 133,
|
||||
69, 249, 2, 127, 80, 60, 159, 168,
|
||||
81, 163, 64, 143, 146, 157, 56, 245,
|
||||
188, 182, 218, 33, 16, 255, 243, 210,
|
||||
205, 12, 19, 236, 95, 151, 68, 23,
|
||||
196, 167, 126, 61, 100, 93, 25, 115,
|
||||
96, 129, 79, 220, 34, 42, 144, 136,
|
||||
70, 238, 184, 20, 222, 94, 11, 219,
|
||||
224, 50, 58, 10, 73, 6, 36, 92,
|
||||
194, 211, 172, 98, 145, 149, 228, 121,
|
||||
231, 200, 55, 109, 141, 213, 78, 169,
|
||||
108, 86, 244, 234, 101, 122, 174, 8,
|
||||
186, 120, 37, 46, 28, 166, 180, 198,
|
||||
232, 221, 116, 31, 75, 189, 139, 138,
|
||||
112, 62, 181, 102, 72, 3, 246, 14,
|
||||
97, 53, 87, 185, 134, 193, 29, 158,
|
||||
225, 248, 152, 17, 105, 217, 142, 148,
|
||||
155, 30, 135, 233, 206, 85, 40, 223,
|
||||
140, 161, 137, 13, 191, 230, 66, 104,
|
||||
65, 153, 45, 15, 176, 84, 187, 22,
|
||||
};
|
||||
|
||||
// The inverse S-box
|
||||
private static readonly byte[] Si =
|
||||
{
|
||||
82, 9, 106, 213, 48, 54, 165, 56,
|
||||
191, 64, 163, 158, 129, 243, 215, 251,
|
||||
124, 227, 57, 130, 155, 47, 255, 135,
|
||||
52, 142, 67, 68, 196, 222, 233, 203,
|
||||
84, 123, 148, 50, 166, 194, 35, 61,
|
||||
238, 76, 149, 11, 66, 250, 195, 78,
|
||||
8, 46, 161, 102, 40, 217, 36, 178,
|
||||
118, 91, 162, 73, 109, 139, 209, 37,
|
||||
114, 248, 246, 100, 134, 104, 152, 22,
|
||||
212, 164, 92, 204, 93, 101, 182, 146,
|
||||
108, 112, 72, 80, 253, 237, 185, 218,
|
||||
94, 21, 70, 87, 167, 141, 157, 132,
|
||||
144, 216, 171, 0, 140, 188, 211, 10,
|
||||
247, 228, 88, 5, 184, 179, 69, 6,
|
||||
208, 44, 30, 143, 202, 63, 15, 2,
|
||||
193, 175, 189, 3, 1, 19, 138, 107,
|
||||
58, 145, 17, 65, 79, 103, 220, 234,
|
||||
151, 242, 207, 206, 240, 180, 230, 115,
|
||||
150, 172, 116, 34, 231, 173, 53, 133,
|
||||
226, 249, 55, 232, 28, 117, 223, 110,
|
||||
71, 241, 26, 113, 29, 41, 197, 137,
|
||||
111, 183, 98, 14, 170, 24, 190, 27,
|
||||
252, 86, 62, 75, 198, 210, 121, 32,
|
||||
154, 219, 192, 254, 120, 205, 90, 244,
|
||||
31, 221, 168, 51, 136, 7, 199, 49,
|
||||
177, 18, 16, 89, 39, 128, 236, 95,
|
||||
96, 81, 127, 169, 25, 181, 74, 13,
|
||||
45, 229, 122, 159, 147, 201, 156, 239,
|
||||
160, 224, 59, 77, 174, 42, 245, 176,
|
||||
200, 235, 187, 60, 131, 83, 153, 97,
|
||||
23, 43, 4, 126, 186, 119, 214, 38,
|
||||
225, 105, 20, 99, 85, 33, 12, 125,
|
||||
};
|
||||
|
||||
// vector used in calculating key schedule (powers of x in GF(256))
|
||||
private static readonly byte[] rcon =
|
||||
{
|
||||
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
|
||||
0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91
|
||||
};
|
||||
|
||||
// precomputation tables of calculations for rounds
|
||||
private static readonly uint[] T0 =
|
||||
{
|
||||
0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0x0df2f2ff,
|
||||
0xbd6b6bd6, 0xb16f6fde, 0x54c5c591, 0x50303060, 0x03010102,
|
||||
0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d,
|
||||
0x9a7676ec, 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa,
|
||||
0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb, 0xecadad41,
|
||||
0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453,
|
||||
0x967272e4, 0x5bc0c09b, 0xc2b7b775, 0x1cfdfde1, 0xae93933d,
|
||||
0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83,
|
||||
0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x08f1f1f9, 0x937171e2,
|
||||
0x73d8d8ab, 0x53313162, 0x3f15152a, 0x0c040408, 0x52c7c795,
|
||||
0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637, 0x0f05050a,
|
||||
0xb59a9a2f, 0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df,
|
||||
0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912,
|
||||
0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc,
|
||||
0xee5a5ab4, 0xfba0a05b, 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7,
|
||||
0xceb3b37d, 0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413,
|
||||
0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1, 0x60202040,
|
||||
0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d,
|
||||
0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0,
|
||||
0x4acfcf85, 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed,
|
||||
0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, 0xcf45458a,
|
||||
0x10f9f9e9, 0x06020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78,
|
||||
0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d, 0xc0404080,
|
||||
0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1,
|
||||
0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020,
|
||||
0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18,
|
||||
0x35131326, 0x2fececc3, 0xe15f5fbe, 0xa2979735, 0xcc444488,
|
||||
0x3917172e, 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a,
|
||||
0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, 0xa06060c0,
|
||||
0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54,
|
||||
0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b,
|
||||
0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad,
|
||||
0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992,
|
||||
0x0a06060c, 0x6c242448, 0xe45c5cb8, 0x5dc2c29f, 0x6ed3d3bd,
|
||||
0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3,
|
||||
0x8b7979f2, 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda,
|
||||
0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8,
|
||||
0xfa5656ac, 0x07f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4,
|
||||
0xe9aeae47, 0x18080810, 0xd5baba6f, 0x887878f0, 0x6f25254a,
|
||||
0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697,
|
||||
0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, 0xdd4b4b96,
|
||||
0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c,
|
||||
0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x05030306, 0x01f6f6f7,
|
||||
0x120e0e1c, 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969,
|
||||
0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27, 0x38e1e1d9,
|
||||
0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9,
|
||||
0x898e8e07, 0xa7949433, 0xb69b9b2d, 0x221e1e3c, 0x92878715,
|
||||
0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5,
|
||||
0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65,
|
||||
0x31e6e6d7, 0xc6424284, 0xb86868d0, 0xc3414182, 0xb0999929,
|
||||
0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d,
|
||||
0x3a16162c
|
||||
};
|
||||
|
||||
private static readonly uint[] Tinv0 =
|
||||
{
|
||||
0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a, 0xcb6bab3b,
|
||||
0xf1459d1f, 0xab58faac, 0x9303e34b, 0x55fa3020, 0xf66d76ad,
|
||||
0x9176cc88, 0x254c02f5, 0xfcd7e54f, 0xd7cb2ac5, 0x80443526,
|
||||
0x8fa362b5, 0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d,
|
||||
0x02752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b, 0xe75f8f03,
|
||||
0x959c9215, 0xeb7a6dbf, 0xda595295, 0x2d83bed4, 0xd3217458,
|
||||
0x2969e049, 0x44c8c98e, 0x6a89c275, 0x78798ef4, 0x6b3e5899,
|
||||
0xdd71b927, 0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d,
|
||||
0x184adf63, 0x82311ae5, 0x60335197, 0x457f5362, 0xe07764b1,
|
||||
0x84ae6bbb, 0x1ca081fe, 0x942b08f9, 0x58684870, 0x19fd458f,
|
||||
0x876cde94, 0xb7f87b52, 0x23d373ab, 0xe2024b72, 0x578f1fe3,
|
||||
0x2aab5566, 0x0728ebb2, 0x03c2b52f, 0x9a7bc586, 0xa50837d3,
|
||||
0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed, 0x2b1ccf8a,
|
||||
0x92b479a7, 0xf0f207f3, 0xa1e2694e, 0xcdf4da65, 0xd5be0506,
|
||||
0x1f6234d1, 0x8afea6c4, 0x9d532e34, 0xa055f3a2, 0x32e18a05,
|
||||
0x75ebf6a4, 0x39ec830b, 0xaaef6040, 0x069f715e, 0x51106ebd,
|
||||
0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d, 0xb58d5491,
|
||||
0x055dc471, 0x6fd40604, 0xff155060, 0x24fb9819, 0x97e9bdd6,
|
||||
0xcc434089, 0x779ed967, 0xbd42e8b0, 0x888b8907, 0x385b19e7,
|
||||
0xdbeec879, 0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x00000000,
|
||||
0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c, 0xfbff0efd,
|
||||
0x5638850f, 0x1ed5ae3d, 0x27392d36, 0x64d90f0a, 0x21a65c68,
|
||||
0xd1545b9b, 0x3a2e3624, 0xb1670a0c, 0x0fe75793, 0xd296eeb4,
|
||||
0x9e919b1b, 0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c,
|
||||
0x0aba93e2, 0xe52aa0c0, 0x43e0223c, 0x1d171b12, 0x0b0d090e,
|
||||
0xadc78bf2, 0xb9a8b62d, 0xc8a91e14, 0x8519f157, 0x4c0775af,
|
||||
0xbbdd99ee, 0xfd607fa3, 0x9f2601f7, 0xbcf5725c, 0xc53b6644,
|
||||
0x347efb5b, 0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8,
|
||||
0xcadc31d7, 0x10856342, 0x40229713, 0x2011c684, 0x7d244a85,
|
||||
0xf83dbbd2, 0x1132f9ae, 0x6da129c7, 0x4b2f9e1d, 0xf330b2dc,
|
||||
0xec52860d, 0xd0e3c177, 0x6c16b32b, 0x99b970a9, 0xfa489411,
|
||||
0x2264e947, 0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56, 0xef903322,
|
||||
0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498, 0xcf81f5a6,
|
||||
0x28de7aa5, 0x268eb7da, 0xa4bfad3f, 0xe49d3a2c, 0x0d927850,
|
||||
0x9bcc5f6a, 0x62467e54, 0xc2138df6, 0xe8b8d890, 0x5ef7392e,
|
||||
0xf5afc382, 0xbe805d9f, 0x7c93d069, 0xa92dd56f, 0xb31225cf,
|
||||
0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb, 0x097826cd,
|
||||
0xf418596e, 0x01b79aec, 0xa89a4f83, 0x656e95e6, 0x7ee6ffaa,
|
||||
0x08cfbc21, 0xe6e815ef, 0xd99be7ba, 0xce366f4a, 0xd4099fea,
|
||||
0xd67cb029, 0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235,
|
||||
0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733, 0x4a9804f1,
|
||||
0xf7daec41, 0x0e50cd7f, 0x2ff69117, 0x8dd64d76, 0x4db0ef43,
|
||||
0x544daacc, 0xdf0496e4, 0xe3b5d19e, 0x1b886a4c, 0xb81f2cc1,
|
||||
0x7f516546, 0x04ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb,
|
||||
0x5a1d67b3, 0x52d2db92, 0x335610e9, 0x1347d66d, 0x8c61d79a,
|
||||
0x7a0ca137, 0x8e14f859, 0x893c13eb, 0xee27a9ce, 0x35c961b7,
|
||||
0xede51ce1, 0x3cb1477a, 0x59dfd29c, 0x3f73f255, 0x79ce1418,
|
||||
0xbf37c773, 0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478,
|
||||
0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2, 0x72c31d16,
|
||||
0x0c25e2bc, 0x8b493c28, 0x41950dff, 0x7101a839, 0xdeb30c08,
|
||||
0x9ce4b4d8, 0x90c15664, 0x6184cb7b, 0x70b632d5, 0x745c6c48,
|
||||
0x4257b8d0
|
||||
};
|
||||
|
||||
private static uint Shift(uint r, int shift)
|
||||
{
|
||||
return (r >> shift) | (r << (32 - shift));
|
||||
}
|
||||
|
||||
/* multiply four bytes in GF(2^8) by 'x' {02} in parallel */
|
||||
|
||||
private const uint m1 = 0x80808080;
|
||||
private const uint m2 = 0x7f7f7f7f;
|
||||
private const uint m3 = 0x0000001b;
|
||||
private const uint m4 = 0xC0C0C0C0;
|
||||
private const uint m5 = 0x3f3f3f3f;
|
||||
|
||||
private static uint FFmulX(uint x)
|
||||
{
|
||||
return ((x & m2) << 1) ^ (((x & m1) >> 7) * m3);
|
||||
}
|
||||
|
||||
private static uint FFmulX2(uint x)
|
||||
{
|
||||
uint t0 = (x & m5) << 2;
|
||||
uint t1 = (x & m4);
|
||||
t1 ^= (t1 >> 1);
|
||||
return t0 ^ (t1 >> 2) ^ (t1 >> 5);
|
||||
}
|
||||
|
||||
/*
|
||||
The following defines provide alternative definitions of FFmulX that might
|
||||
give improved performance if a fast 32-bit multiply is not available.
|
||||
|
||||
private int FFmulX(int x) { int u = x & m1; u |= (u >> 1); return ((x & m2) << 1) ^ ((u >>> 3) | (u >>> 6)); }
|
||||
private static final int m4 = 0x1b1b1b1b;
|
||||
private int FFmulX(int x) { int u = x & m1; return ((x & m2) << 1) ^ ((u - (u >>> 7)) & m4); }
|
||||
|
||||
*/
|
||||
|
||||
private static uint Inv_Mcol(uint x)
|
||||
{
|
||||
uint t0, t1;
|
||||
t0 = x;
|
||||
t1 = t0 ^ Shift(t0, 8);
|
||||
t0 ^= FFmulX(t1);
|
||||
t1 ^= FFmulX2(t0);
|
||||
t0 ^= t1 ^ Shift(t1, 16);
|
||||
return t0;
|
||||
}
|
||||
|
||||
private static uint SubWord(uint x)
|
||||
{
|
||||
return (uint)S[x & 255]
|
||||
| (((uint)S[(x >> 8) & 255]) << 8)
|
||||
| (((uint)S[(x >> 16) & 255]) << 16)
|
||||
| (((uint)S[(x >> 24) & 255]) << 24);
|
||||
}
|
||||
|
||||
uint[][] W = null;
|
||||
|
||||
/**
|
||||
* Calculate the necessary round keys
|
||||
* The number of calculations depends on key size and block size
|
||||
* AES specified a fixed block size of 128 bits and key sizes 128/192/256 bits
|
||||
* This code is written assuming those are the only possible values
|
||||
*/
|
||||
private uint[][] GenerateWorkingKey(byte[] key, bool forEncryption)
|
||||
{
|
||||
int keyLen = key.Length;
|
||||
if (keyLen < 16 || keyLen > 32 || (keyLen & 7) != 0)
|
||||
throw new ArgumentException("Key length not 128/192/256 bits.");
|
||||
|
||||
int KC = keyLen >> 2;
|
||||
this.ROUNDS = KC + 6; // This is not always true for the generalized Rijndael that allows larger block sizes
|
||||
|
||||
if (W == null || W.Length < ROUNDS + 1)
|
||||
{
|
||||
W = new uint[ROUNDS + 1][]; // 4 words in a block
|
||||
for (int i = 0; i <= ROUNDS; ++i)
|
||||
{
|
||||
W[i] = new uint[4];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < W.Length; ++i)
|
||||
Array.Clear(W[i], 0, W[i].Length);
|
||||
}
|
||||
|
||||
switch (KC)
|
||||
{
|
||||
case 4:
|
||||
{
|
||||
uint t0 = Pack.LE_To_UInt32(key, 0); W[0][0] = t0;
|
||||
uint t1 = Pack.LE_To_UInt32(key, 4); W[0][1] = t1;
|
||||
uint t2 = Pack.LE_To_UInt32(key, 8); W[0][2] = t2;
|
||||
uint t3 = Pack.LE_To_UInt32(key, 12); W[0][3] = t3;
|
||||
|
||||
for (int i = 1; i <= 10; ++i)
|
||||
{
|
||||
uint u = SubWord(Shift(t3, 8)) ^ rcon[i - 1];
|
||||
t0 ^= u; W[i][0] = t0;
|
||||
t1 ^= t0; W[i][1] = t1;
|
||||
t2 ^= t1; W[i][2] = t2;
|
||||
t3 ^= t2; W[i][3] = t3;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 6:
|
||||
{
|
||||
uint t0 = Pack.LE_To_UInt32(key, 0); W[0][0] = t0;
|
||||
uint t1 = Pack.LE_To_UInt32(key, 4); W[0][1] = t1;
|
||||
uint t2 = Pack.LE_To_UInt32(key, 8); W[0][2] = t2;
|
||||
uint t3 = Pack.LE_To_UInt32(key, 12); W[0][3] = t3;
|
||||
uint t4 = Pack.LE_To_UInt32(key, 16); W[1][0] = t4;
|
||||
uint t5 = Pack.LE_To_UInt32(key, 20); W[1][1] = t5;
|
||||
|
||||
uint rcon = 1;
|
||||
uint u = SubWord(Shift(t5, 8)) ^ rcon; rcon <<= 1;
|
||||
t0 ^= u; W[1][2] = t0;
|
||||
t1 ^= t0; W[1][3] = t1;
|
||||
t2 ^= t1; W[2][0] = t2;
|
||||
t3 ^= t2; W[2][1] = t3;
|
||||
t4 ^= t3; W[2][2] = t4;
|
||||
t5 ^= t4; W[2][3] = t5;
|
||||
|
||||
for (int i = 3; i < 12; i += 3)
|
||||
{
|
||||
u = SubWord(Shift(t5, 8)) ^ rcon; rcon <<= 1;
|
||||
t0 ^= u; W[i][0] = t0;
|
||||
t1 ^= t0; W[i][1] = t1;
|
||||
t2 ^= t1; W[i][2] = t2;
|
||||
t3 ^= t2; W[i][3] = t3;
|
||||
t4 ^= t3; W[i + 1][0] = t4;
|
||||
t5 ^= t4; W[i + 1][1] = t5;
|
||||
u = SubWord(Shift(t5, 8)) ^ rcon; rcon <<= 1;
|
||||
t0 ^= u; W[i + 1][2] = t0;
|
||||
t1 ^= t0; W[i + 1][3] = t1;
|
||||
t2 ^= t1; W[i + 2][0] = t2;
|
||||
t3 ^= t2; W[i + 2][1] = t3;
|
||||
t4 ^= t3; W[i + 2][2] = t4;
|
||||
t5 ^= t4; W[i + 2][3] = t5;
|
||||
}
|
||||
|
||||
u = SubWord(Shift(t5, 8)) ^ rcon;
|
||||
t0 ^= u; W[12][0] = t0;
|
||||
t1 ^= t0; W[12][1] = t1;
|
||||
t2 ^= t1; W[12][2] = t2;
|
||||
t3 ^= t2; W[12][3] = t3;
|
||||
|
||||
break;
|
||||
}
|
||||
case 8:
|
||||
{
|
||||
uint t0 = Pack.LE_To_UInt32(key, 0); W[0][0] = t0;
|
||||
uint t1 = Pack.LE_To_UInt32(key, 4); W[0][1] = t1;
|
||||
uint t2 = Pack.LE_To_UInt32(key, 8); W[0][2] = t2;
|
||||
uint t3 = Pack.LE_To_UInt32(key, 12); W[0][3] = t3;
|
||||
uint t4 = Pack.LE_To_UInt32(key, 16); W[1][0] = t4;
|
||||
uint t5 = Pack.LE_To_UInt32(key, 20); W[1][1] = t5;
|
||||
uint t6 = Pack.LE_To_UInt32(key, 24); W[1][2] = t6;
|
||||
uint t7 = Pack.LE_To_UInt32(key, 28); W[1][3] = t7;
|
||||
|
||||
uint u, rcon = 1;
|
||||
|
||||
for (int i = 2; i < 14; i += 2)
|
||||
{
|
||||
u = SubWord(Shift(t7, 8)) ^ rcon; rcon <<= 1;
|
||||
t0 ^= u; W[i][0] = t0;
|
||||
t1 ^= t0; W[i][1] = t1;
|
||||
t2 ^= t1; W[i][2] = t2;
|
||||
t3 ^= t2; W[i][3] = t3;
|
||||
u = SubWord(t3);
|
||||
t4 ^= u; W[i + 1][0] = t4;
|
||||
t5 ^= t4; W[i + 1][1] = t5;
|
||||
t6 ^= t5; W[i + 1][2] = t6;
|
||||
t7 ^= t6; W[i + 1][3] = t7;
|
||||
}
|
||||
|
||||
u = SubWord(Shift(t7, 8)) ^ rcon;
|
||||
t0 ^= u; W[14][0] = t0;
|
||||
t1 ^= t0; W[14][1] = t1;
|
||||
t2 ^= t1; W[14][2] = t2;
|
||||
t3 ^= t2; W[14][3] = t3;
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new InvalidOperationException("Should never get here");
|
||||
}
|
||||
}
|
||||
|
||||
if (!forEncryption)
|
||||
{
|
||||
for (int j = 1; j < ROUNDS; j++)
|
||||
{
|
||||
uint[] w = W[j];
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
w[i] = Inv_Mcol(w[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return W;
|
||||
}
|
||||
|
||||
private int ROUNDS;
|
||||
private uint[][] WorkingKey;
|
||||
private bool forEncryption;
|
||||
|
||||
private byte[] s;
|
||||
|
||||
private const int BLOCK_SIZE = 16;
|
||||
|
||||
/**
|
||||
* default constructor - 128 bit block size.
|
||||
*/
|
||||
public FastAesEngine()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* initialise an AES cipher.
|
||||
*
|
||||
* @param forEncryption whether or not we are for encryption.
|
||||
* @param parameters the parameters required to set up the cipher.
|
||||
* @exception ArgumentException if the parameters argument is
|
||||
* inappropriate.
|
||||
*/
|
||||
public void Init(bool forEncryption, ICipherParameters parameters)
|
||||
{
|
||||
if (!(parameters is KeyParameter keyParameter))
|
||||
throw new ArgumentException("invalid parameter passed to AES init - "
|
||||
+ BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.GetTypeName(parameters));
|
||||
|
||||
WorkingKey = GenerateWorkingKey(keyParameter.GetKey(), forEncryption);
|
||||
|
||||
this.forEncryption = forEncryption;
|
||||
this.s = /*Arrays.Clone*/(forEncryption ? S : Si);
|
||||
}
|
||||
|
||||
public string AlgorithmName
|
||||
{
|
||||
get { return "AES"; }
|
||||
}
|
||||
|
||||
public int GetBlockSize()
|
||||
{
|
||||
return BLOCK_SIZE;
|
||||
}
|
||||
|
||||
public int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
|
||||
{
|
||||
if (WorkingKey == null)
|
||||
throw new InvalidOperationException("AES engine not initialised");
|
||||
|
||||
Check.DataLength(input, inOff, 16, "input buffer too short");
|
||||
Check.OutputLength(output, outOff, 16, "output buffer too short");
|
||||
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
if (forEncryption)
|
||||
{
|
||||
EncryptBlock(input.AsSpan(inOff), output.AsSpan(outOff), WorkingKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
DecryptBlock(input.AsSpan(inOff), output.AsSpan(outOff), WorkingKey);
|
||||
}
|
||||
#else
|
||||
if (forEncryption)
|
||||
{
|
||||
EncryptBlock(input, inOff, output, outOff, WorkingKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
DecryptBlock(input, inOff, output, outOff, WorkingKey);
|
||||
}
|
||||
#endif
|
||||
|
||||
return BLOCK_SIZE;
|
||||
}
|
||||
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
public unsafe int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
|
||||
{
|
||||
if (WorkingKey == null)
|
||||
throw new InvalidOperationException("AES engine not initialised");
|
||||
|
||||
Check.DataLength(input, 16, "input buffer too short");
|
||||
Check.OutputLength(output, 16, "output buffer too short");
|
||||
|
||||
if (forEncryption)
|
||||
{
|
||||
//EncryptBlock(input, output, WorkingKey);
|
||||
|
||||
uint C0 = System.Buffers.Binary.BinaryPrimitives.ReadUInt32LittleEndian(input);
|
||||
uint C1 = System.Buffers.Binary.BinaryPrimitives.ReadUInt32LittleEndian(input[4..]);
|
||||
uint C2 = System.Buffers.Binary.BinaryPrimitives.ReadUInt32LittleEndian(input[8..]);
|
||||
uint C3 = System.Buffers.Binary.BinaryPrimitives.ReadUInt32LittleEndian(input[12..]);
|
||||
|
||||
uint[] kw = WorkingKey[0];
|
||||
uint t0 = C0 ^ kw[0];
|
||||
uint t1 = C1 ^ kw[1];
|
||||
uint t2 = C2 ^ kw[2];
|
||||
|
||||
uint r0, r1, r2, r3 = C3 ^ kw[3];
|
||||
int r = 1;
|
||||
uint tmp1, tmp2, tmp3;
|
||||
uint shift1, shift2, shift3;
|
||||
|
||||
fixed (uint* pT0 = T0)
|
||||
{
|
||||
while (r < ROUNDS - 1)
|
||||
{
|
||||
kw = WorkingKey[r++];
|
||||
fixed (uint* pkw = kw)
|
||||
{
|
||||
tmp1 = pT0[(t1 >> 8) & 255]; tmp2 = pT0[(t2 >> 16) & 255]; tmp3 = pT0[(r3 >> 24) & 255];
|
||||
shift1 = (tmp1 >> 24) | (tmp1 << 8); shift2 = (tmp2 >> 16) | (tmp2 << 16); shift3 = (tmp3 >> 8) | (tmp3 << 24);
|
||||
r0 = pT0[t0 & 255] ^ shift1 ^ shift2 ^ shift3 ^ pkw[0];
|
||||
|
||||
tmp1 = pT0[(t2 >> 8) & 255]; tmp2 = pT0[(r3 >> 16) & 255]; tmp3 = pT0[(t0 >> 24) & 255];
|
||||
shift1 = (tmp1 >> 24) | (tmp1 << 8); shift2 = (tmp2 >> 16) | (tmp2 << 16); shift3 = (tmp3 >> 8) | (tmp3 << 24);
|
||||
r1 = pT0[t1 & 255] ^ shift1 ^ shift2 ^ shift3 ^ pkw[1];
|
||||
|
||||
tmp1 = pT0[(r3 >> 8) & 255]; tmp2 = pT0[(t0 >> 16) & 255]; tmp3 = pT0[(t1 >> 24) & 255];
|
||||
shift1 = (tmp1 >> 24) | (tmp1 << 8); shift2 = (tmp2 >> 16) | (tmp2 << 16); shift3 = (tmp3 >> 8) | (tmp3 << 24);
|
||||
r2 = pT0[t2 & 255] ^ shift1 ^ shift2 ^ shift3 ^ pkw[2];
|
||||
|
||||
tmp1 = pT0[(t0 >> 8) & 255]; tmp2 = pT0[(t1 >> 16) & 255]; tmp3 = pT0[(t2 >> 24) & 255];
|
||||
shift1 = (tmp1 >> 24) | (tmp1 << 8); shift2 = (tmp2 >> 16) | (tmp2 << 16); shift3 = (tmp3 >> 8) | (tmp3 << 24);
|
||||
r3 = pT0[r3 & 255] ^ shift1 ^ shift2 ^ shift3 ^ pkw[3];
|
||||
}
|
||||
|
||||
kw = WorkingKey[r++];
|
||||
|
||||
fixed (uint* pkw = kw)
|
||||
{
|
||||
tmp1 = pT0[(r1 >> 8) & 255]; tmp2 = pT0[(r2 >> 16) & 255]; tmp3 = pT0[(r3 >> 24) & 255];
|
||||
shift1 = (tmp1 >> 24) | (tmp1 << 8); shift2 = (tmp2 >> 16) | (tmp2 << 16); shift3 = (tmp3 >> 8) | (tmp3 << 24);
|
||||
t0 = pT0[r0 & 255] ^ shift1 ^ shift2 ^ shift3 ^ pkw[0];
|
||||
|
||||
tmp1 = pT0[(r2 >> 8) & 255]; tmp2 = pT0[(r3 >> 16) & 255]; tmp3 = pT0[(r0 >> 24) & 255];
|
||||
shift1 = (tmp1 >> 24) | (tmp1 << 8); shift2 = (tmp2 >> 16) | (tmp2 << 16); shift3 = (tmp3 >> 8) | (tmp3 << 24);
|
||||
t1 = pT0[r1 & 255] ^ shift1 ^ shift2 ^ shift3 ^ pkw[1];
|
||||
|
||||
tmp1 = pT0[(r3 >> 8) & 255]; tmp2 = pT0[(r0 >> 16) & 255]; tmp3 = pT0[(r1 >> 24) & 255];
|
||||
shift1 = (tmp1 >> 24) | (tmp1 << 8); shift2 = (tmp2 >> 16) | (tmp2 << 16); shift3 = (tmp3 >> 8) | (tmp3 << 24);
|
||||
t2 = pT0[r2 & 255] ^ shift1 ^ shift2 ^ shift3 ^ pkw[2];
|
||||
|
||||
tmp1 = pT0[(r0 >> 8) & 255]; tmp2 = pT0[(r1 >> 16) & 255]; tmp3 = pT0[(r2 >> 24) & 255];
|
||||
shift1 = (tmp1 >> 24) | (tmp1 << 8); shift2 = (tmp2 >> 16) | (tmp2 << 16); shift3 = (tmp3 >> 8) | (tmp3 << 24);
|
||||
r3 = pT0[r3 & 255] ^ shift1 ^ shift2 ^ shift3 ^ pkw[3];
|
||||
}
|
||||
}
|
||||
|
||||
kw = WorkingKey[r++];
|
||||
fixed (uint* pkw = kw)
|
||||
{
|
||||
tmp1 = pT0[(t1 >> 8) & 255]; tmp2 = pT0[(t2 >> 16) & 255]; tmp3 = pT0[(r3 >> 24) & 255];
|
||||
shift1 = (tmp1 >> 24) | (tmp1 << 8); shift2 = (tmp2 >> 16) | (tmp2 << 16); shift3 = (tmp3 >> 8) | (tmp3 << 24);
|
||||
r0 = pT0[t0 & 255] ^ shift1 ^ shift2 ^ shift3 ^ pkw[0];
|
||||
|
||||
tmp1 = pT0[(t2 >> 8) & 255]; tmp2 = pT0[(r3 >> 16) & 255]; tmp3 = pT0[(t0 >> 24) & 255];
|
||||
shift1 = (tmp1 >> 24) | (tmp1 << 8); shift2 = (tmp2 >> 16) | (tmp2 << 16); shift3 = (tmp3 >> 8) | (tmp3 << 24);
|
||||
r1 = pT0[t1 & 255] ^ shift1 ^ shift2 ^ shift3 ^ pkw[1];
|
||||
|
||||
tmp1 = pT0[(r3 >> 8) & 255]; tmp2 = pT0[(t0 >> 16) & 255]; tmp3 = pT0[(t1 >> 24) & 255];
|
||||
shift1 = (tmp1 >> 24) | (tmp1 << 8); shift2 = (tmp2 >> 16) | (tmp2 << 16); shift3 = (tmp3 >> 8) | (tmp3 << 24);
|
||||
r2 = pT0[t2 & 255] ^ shift1 ^ shift2 ^ shift3 ^ pkw[2];
|
||||
|
||||
tmp1 = pT0[(t0 >> 8) & 255]; tmp2 = pT0[(t1 >> 16) & 255]; tmp3 = pT0[(t2 >> 24) & 255];
|
||||
shift1 = (tmp1 >> 24) | (tmp1 << 8); shift2 = (tmp2 >> 16) | (tmp2 << 16); shift3 = (tmp3 >> 8) | (tmp3 << 24);
|
||||
r3 = pT0[r3 & 255] ^ shift1 ^ shift2 ^ shift3 ^ pkw[3];
|
||||
}
|
||||
}
|
||||
|
||||
// the final round's table is a simple function of S so we don't use a whole other four tables for it
|
||||
|
||||
kw = WorkingKey[r];
|
||||
fixed (uint* pkw = kw)
|
||||
fixed (byte* pS = S)
|
||||
fixed (byte* ps = s)
|
||||
{
|
||||
C0 = (uint)pS[r0 & 255] ^ (((uint)pS[(r1 >> 8) & 255]) << 8) ^ (((uint)ps[(r2 >> 16) & 255]) << 16) ^ (((uint)ps[(r3 >> 24) & 255]) << 24) ^ pkw[0];
|
||||
C1 = (uint)ps[r1 & 255] ^ (((uint)pS[(r2 >> 8) & 255]) << 8) ^ (((uint)pS[(r3 >> 16) & 255]) << 16) ^ (((uint)ps[(r0 >> 24) & 255]) << 24) ^ pkw[1];
|
||||
C2 = (uint)ps[r2 & 255] ^ (((uint)pS[(r3 >> 8) & 255]) << 8) ^ (((uint)pS[(r0 >> 16) & 255]) << 16) ^ (((uint)pS[(r1 >> 24) & 255]) << 24) ^ pkw[2];
|
||||
C3 = (uint)ps[r3 & 255] ^ (((uint)ps[(r0 >> 8) & 255]) << 8) ^ (((uint)ps[(r1 >> 16) & 255]) << 16) ^ (((uint)pS[(r2 >> 24) & 255]) << 24) ^ pkw[3];
|
||||
}
|
||||
|
||||
System.Buffers.Binary.BinaryPrimitives.WriteUInt32LittleEndian(output, C0);
|
||||
System.Buffers.Binary.BinaryPrimitives.WriteUInt32LittleEndian(output[4..], C1);
|
||||
System.Buffers.Binary.BinaryPrimitives.WriteUInt32LittleEndian(output[8..], C2);
|
||||
System.Buffers.Binary.BinaryPrimitives.WriteUInt32LittleEndian(output[12..], C3);
|
||||
}
|
||||
else
|
||||
{
|
||||
//DecryptBlock(input, output, WorkingKey);
|
||||
|
||||
uint C0 = System.Buffers.Binary.BinaryPrimitives.ReadUInt32LittleEndian(input);
|
||||
uint C1 = System.Buffers.Binary.BinaryPrimitives.ReadUInt32LittleEndian(input[4..]);
|
||||
uint C2 = System.Buffers.Binary.BinaryPrimitives.ReadUInt32LittleEndian(input[8..]);
|
||||
uint C3 = System.Buffers.Binary.BinaryPrimitives.ReadUInt32LittleEndian(input[12..]);
|
||||
|
||||
uint[] kw = WorkingKey[ROUNDS];
|
||||
uint t0 = C0 ^ kw[0];
|
||||
uint t1 = C1 ^ kw[1];
|
||||
uint t2 = C2 ^ kw[2];
|
||||
|
||||
uint r0, r1, r2, r3 = C3 ^ kw[3];
|
||||
int r = ROUNDS - 1;
|
||||
|
||||
uint tmp1, tmp2, tmp3;
|
||||
uint shift1, shift2, shift3;
|
||||
|
||||
fixed (uint* pTinv0 = Tinv0)
|
||||
{
|
||||
while (r > 1)
|
||||
{
|
||||
kw = WorkingKey[r--];
|
||||
fixed (uint* pkw = kw)
|
||||
{
|
||||
tmp1 = pTinv0[(r3 >> 8) & 255]; tmp2 = pTinv0[(t2 >> 16) & 255]; tmp3 = pTinv0[(t1 >> 24) & 255];
|
||||
shift1 = (tmp1 >> 24) | (tmp1 << 8); shift2 = (tmp2 >> 16) | (tmp2 << 16); shift3 = (tmp3 >> 8) | (tmp3 << 24);
|
||||
r0 = pTinv0[t0 & 255] ^ shift1 ^ shift2 ^ shift3 ^ pkw[0];
|
||||
|
||||
tmp1 = pTinv0[(t0 >> 8) & 255]; tmp2 = pTinv0[(r3 >> 16) & 255]; tmp3 = pTinv0[(t2 >> 24) & 255];
|
||||
shift1 = (tmp1 >> 24) | (tmp1 << 8); shift2 = (tmp2 >> 16) | (tmp2 << 16); shift3 = (tmp3 >> 8) | (tmp3 << 24);
|
||||
r1 = pTinv0[t1 & 255] ^ shift1 ^ shift2 ^ shift3 ^ pkw[1];
|
||||
|
||||
tmp1 = pTinv0[(t1 >> 8) & 255]; tmp2 = pTinv0[(t0 >> 16) & 255]; tmp3 = pTinv0[(r3 >> 24) & 255];
|
||||
shift1 = (tmp1 >> 24) | (tmp1 << 8); shift2 = (tmp2 >> 16) | (tmp2 << 16); shift3 = (tmp3 >> 8) | (tmp3 << 24);
|
||||
r2 = pTinv0[t2 & 255] ^ shift1 ^ shift2 ^ shift3 ^ pkw[2];
|
||||
|
||||
tmp1 = pTinv0[(t2 >> 8) & 255]; tmp2 = pTinv0[(t1 >> 16) & 255]; tmp3 = pTinv0[(t0 >> 24) & 255];
|
||||
shift1 = (tmp1 >> 24) | (tmp1 << 8); shift2 = (tmp2 >> 16) | (tmp2 << 16); shift3 = (tmp3 >> 8) | (tmp3 << 24);
|
||||
r3 = pTinv0[r3 & 255] ^ shift1 ^ shift2 ^ shift3 ^ pkw[3];
|
||||
}
|
||||
|
||||
kw = WorkingKey[r--];
|
||||
|
||||
fixed (uint* pkw = kw)
|
||||
{
|
||||
tmp1 = pTinv0[(r3 >> 8) & 255]; tmp2 = pTinv0[(r2 >> 16) & 255]; tmp3 = pTinv0[(r1 >> 24) & 255];
|
||||
shift1 = (tmp1 >> 24) | (tmp1 << 8); shift2 = (tmp2 >> 16) | (tmp2 << 16); shift3 = (tmp3 >> 8) | (tmp3 << 24);
|
||||
t0 = pTinv0[r0 & 255] ^ shift1 ^ shift2 ^ shift3 ^ pkw[0];
|
||||
|
||||
tmp1 = pTinv0[(r0 >> 8) & 255]; tmp2 = pTinv0[(r3 >> 16) & 255]; tmp3 = pTinv0[(r2 >> 24) & 255];
|
||||
shift1 = (tmp1 >> 24) | (tmp1 << 8); shift2 = (tmp2 >> 16) | (tmp2 << 16); shift3 = (tmp3 >> 8) | (tmp3 << 24);
|
||||
t1 = pTinv0[r1 & 255] ^ shift1 ^ shift2 ^ shift3 ^ pkw[1];
|
||||
|
||||
tmp1 = pTinv0[(r1 >> 8) & 255]; tmp2 = pTinv0[(r0 >> 16) & 255]; tmp3 = pTinv0[(r3 >> 24) & 255];
|
||||
shift1 = (tmp1 >> 24) | (tmp1 << 8); shift2 = (tmp2 >> 16) | (tmp2 << 16); shift3 = (tmp3 >> 8) | (tmp3 << 24);
|
||||
t2 = pTinv0[r2 & 255] ^ shift1 ^ shift2 ^ shift3 ^ pkw[2];
|
||||
|
||||
tmp1 = pTinv0[(r2 >> 8) & 255]; tmp2 = pTinv0[(r1 >> 16) & 255]; tmp3 = pTinv0[(r0 >> 24) & 255];
|
||||
shift1 = (tmp1 >> 24) | (tmp1 << 8); shift2 = (tmp2 >> 16) | (tmp2 << 16); shift3 = (tmp3 >> 8) | (tmp3 << 24);
|
||||
r3 = pTinv0[r3 & 255] ^ shift1 ^ shift2 ^ shift3 ^ pkw[3];
|
||||
}
|
||||
}
|
||||
|
||||
kw = WorkingKey[1];
|
||||
|
||||
fixed (uint* pkw = kw)
|
||||
{
|
||||
tmp1 = pTinv0[(r3 >> 8) & 255]; tmp2 = pTinv0[(t2 >> 16) & 255]; tmp3 = pTinv0[(t1 >> 24) & 255];
|
||||
shift1 = (tmp1 >> 24) | (tmp1 << 8); shift2 = (tmp2 >> 16) | (tmp2 << 16); shift3 = (tmp3 >> 8) | (tmp3 << 24);
|
||||
r0 = pTinv0[t0 & 255] ^ shift1 ^ shift2 ^ shift3 ^ pkw[0];
|
||||
|
||||
tmp1 = pTinv0[(t0 >> 8) & 255]; tmp2 = pTinv0[(r3 >> 16) & 255]; tmp3 = pTinv0[(t2 >> 24) & 255];
|
||||
shift1 = (tmp1 >> 24) | (tmp1 << 8); shift2 = (tmp2 >> 16) | (tmp2 << 16); shift3 = (tmp3 >> 8) | (tmp3 << 24);
|
||||
r1 = pTinv0[t1 & 255] ^ shift1 ^ shift2 ^ shift3 ^ pkw[1];
|
||||
|
||||
tmp1 = pTinv0[(t1 >> 8) & 255]; tmp2 = pTinv0[(t0 >> 16) & 255]; tmp3 = pTinv0[(r3 >> 24) & 255];
|
||||
shift1 = (tmp1 >> 24) | (tmp1 << 8); shift2 = (tmp2 >> 16) | (tmp2 << 16); shift3 = (tmp3 >> 8) | (tmp3 << 24);
|
||||
r2 = pTinv0[t2 & 255] ^ shift1 ^ shift2 ^ shift3 ^ pkw[2];
|
||||
|
||||
tmp1 = pTinv0[(t2 >> 8) & 255]; tmp2 = pTinv0[(t1 >> 16) & 255]; tmp3 = pTinv0[(t0 >> 24) & 255];
|
||||
shift1 = (tmp1 >> 24) | (tmp1 << 8); shift2 = (tmp2 >> 16) | (tmp2 << 16); shift3 = (tmp3 >> 8) | (tmp3 << 24);
|
||||
r3 = pTinv0[r3 & 255] ^ shift1 ^ shift2 ^ shift3 ^ pkw[3];
|
||||
}
|
||||
}
|
||||
// the final round's table is a simple function of Si so we don't use a whole other four tables for it
|
||||
|
||||
kw = WorkingKey[0];
|
||||
fixed (uint* pkw = kw)
|
||||
fixed(byte* pSi = Si)
|
||||
fixed (byte* ps = s)
|
||||
{
|
||||
C0 = (uint)pSi[r0 & 255] ^ (((uint)ps[(r3 >> 8) & 255]) << 8) ^ (((uint)ps[(r2 >> 16) & 255]) << 16) ^ (((uint)pSi[(r1 >> 24) & 255]) << 24) ^ pkw[0];
|
||||
C1 = (uint)ps[r1 & 255] ^ (((uint)ps[(r0 >> 8) & 255]) << 8) ^ (((uint)pSi[(r3 >> 16) & 255]) << 16) ^ (((uint)ps[(r2 >> 24) & 255]) << 24) ^ pkw[1];
|
||||
C2 = (uint)ps[r2 & 255] ^ (((uint)pSi[(r1 >> 8) & 255]) << 8) ^ (((uint)pSi[(r0 >> 16) & 255]) << 16) ^ (((uint)ps[(r3 >> 24) & 255]) << 24) ^ pkw[2];
|
||||
C3 = (uint)pSi[r3 & 255] ^ (((uint)ps[(r2 >> 8) & 255]) << 8) ^ (((uint)ps[(r1 >> 16) & 255]) << 16) ^ (((uint)ps[(r0 >> 24) & 255]) << 24) ^ pkw[3];
|
||||
}
|
||||
|
||||
System.Buffers.Binary.BinaryPrimitives.WriteUInt32LittleEndian(output, C0);
|
||||
System.Buffers.Binary.BinaryPrimitives.WriteUInt32LittleEndian(output[4..], C1);
|
||||
System.Buffers.Binary.BinaryPrimitives.WriteUInt32LittleEndian(output[8..], C2);
|
||||
System.Buffers.Binary.BinaryPrimitives.WriteUInt32LittleEndian(output[12..], C3);
|
||||
}
|
||||
|
||||
return BLOCK_SIZE;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
private void EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output, uint[][] KW)
|
||||
{
|
||||
uint C0 = Pack.LE_To_UInt32(input);
|
||||
uint C1 = Pack.LE_To_UInt32(input[4..]);
|
||||
uint C2 = Pack.LE_To_UInt32(input[8..]);
|
||||
uint C3 = Pack.LE_To_UInt32(input[12..]);
|
||||
|
||||
uint[] kw = KW[0];
|
||||
uint t0 = C0 ^ kw[0];
|
||||
uint t1 = C1 ^ kw[1];
|
||||
uint t2 = C2 ^ kw[2];
|
||||
|
||||
uint r0, r1, r2, r3 = C3 ^ kw[3];
|
||||
int r = 1;
|
||||
while (r < ROUNDS - 1)
|
||||
{
|
||||
kw = KW[r++];
|
||||
r0 = T0[t0 & 255] ^ Shift(T0[(t1 >> 8) & 255], 24) ^ Shift(T0[(t2 >> 16) & 255], 16) ^ Shift(T0[(r3 >> 24) & 255], 8) ^ kw[0];
|
||||
r1 = T0[t1 & 255] ^ Shift(T0[(t2 >> 8) & 255], 24) ^ Shift(T0[(r3 >> 16) & 255], 16) ^ Shift(T0[(t0 >> 24) & 255], 8) ^ kw[1];
|
||||
r2 = T0[t2 & 255] ^ Shift(T0[(r3 >> 8) & 255], 24) ^ Shift(T0[(t0 >> 16) & 255], 16) ^ Shift(T0[(t1 >> 24) & 255], 8) ^ kw[2];
|
||||
r3 = T0[r3 & 255] ^ Shift(T0[(t0 >> 8) & 255], 24) ^ Shift(T0[(t1 >> 16) & 255], 16) ^ Shift(T0[(t2 >> 24) & 255], 8) ^ kw[3];
|
||||
kw = KW[r++];
|
||||
t0 = T0[r0 & 255] ^ Shift(T0[(r1 >> 8) & 255], 24) ^ Shift(T0[(r2 >> 16) & 255], 16) ^ Shift(T0[(r3 >> 24) & 255], 8) ^ kw[0];
|
||||
t1 = T0[r1 & 255] ^ Shift(T0[(r2 >> 8) & 255], 24) ^ Shift(T0[(r3 >> 16) & 255], 16) ^ Shift(T0[(r0 >> 24) & 255], 8) ^ kw[1];
|
||||
t2 = T0[r2 & 255] ^ Shift(T0[(r3 >> 8) & 255], 24) ^ Shift(T0[(r0 >> 16) & 255], 16) ^ Shift(T0[(r1 >> 24) & 255], 8) ^ kw[2];
|
||||
r3 = T0[r3 & 255] ^ Shift(T0[(r0 >> 8) & 255], 24) ^ Shift(T0[(r1 >> 16) & 255], 16) ^ Shift(T0[(r2 >> 24) & 255], 8) ^ kw[3];
|
||||
}
|
||||
|
||||
kw = KW[r++];
|
||||
r0 = T0[t0 & 255] ^ Shift(T0[(t1 >> 8) & 255], 24) ^ Shift(T0[(t2 >> 16) & 255], 16) ^ Shift(T0[(r3 >> 24) & 255], 8) ^ kw[0];
|
||||
r1 = T0[t1 & 255] ^ Shift(T0[(t2 >> 8) & 255], 24) ^ Shift(T0[(r3 >> 16) & 255], 16) ^ Shift(T0[(t0 >> 24) & 255], 8) ^ kw[1];
|
||||
r2 = T0[t2 & 255] ^ Shift(T0[(r3 >> 8) & 255], 24) ^ Shift(T0[(t0 >> 16) & 255], 16) ^ Shift(T0[(t1 >> 24) & 255], 8) ^ kw[2];
|
||||
r3 = T0[r3 & 255] ^ Shift(T0[(t0 >> 8) & 255], 24) ^ Shift(T0[(t1 >> 16) & 255], 16) ^ Shift(T0[(t2 >> 24) & 255], 8) ^ kw[3];
|
||||
|
||||
// the final round's table is a simple function of S so we don't use a whole other four tables for it
|
||||
|
||||
kw = KW[r];
|
||||
C0 = (uint)S[r0 & 255] ^ (((uint)S[(r1 >> 8) & 255]) << 8) ^ (((uint)s[(r2 >> 16) & 255]) << 16) ^ (((uint)s[(r3 >> 24) & 255]) << 24) ^ kw[0];
|
||||
C1 = (uint)s[r1 & 255] ^ (((uint)S[(r2 >> 8) & 255]) << 8) ^ (((uint)S[(r3 >> 16) & 255]) << 16) ^ (((uint)s[(r0 >> 24) & 255]) << 24) ^ kw[1];
|
||||
C2 = (uint)s[r2 & 255] ^ (((uint)S[(r3 >> 8) & 255]) << 8) ^ (((uint)S[(r0 >> 16) & 255]) << 16) ^ (((uint)S[(r1 >> 24) & 255]) << 24) ^ kw[2];
|
||||
C3 = (uint)s[r3 & 255] ^ (((uint)s[(r0 >> 8) & 255]) << 8) ^ (((uint)s[(r1 >> 16) & 255]) << 16) ^ (((uint)S[(r2 >> 24) & 255]) << 24) ^ kw[3];
|
||||
|
||||
Pack.UInt32_To_LE(C0, output);
|
||||
Pack.UInt32_To_LE(C1, output[4..]);
|
||||
Pack.UInt32_To_LE(C2, output[8..]);
|
||||
Pack.UInt32_To_LE(C3, output[12..]);
|
||||
}
|
||||
|
||||
private void DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output, uint[][] KW)
|
||||
{
|
||||
uint C0 = Pack.LE_To_UInt32(input);
|
||||
uint C1 = Pack.LE_To_UInt32(input[4..]);
|
||||
uint C2 = Pack.LE_To_UInt32(input[8..]);
|
||||
uint C3 = Pack.LE_To_UInt32(input[12..]);
|
||||
|
||||
uint[] kw = KW[ROUNDS];
|
||||
uint t0 = C0 ^ kw[0];
|
||||
uint t1 = C1 ^ kw[1];
|
||||
uint t2 = C2 ^ kw[2];
|
||||
|
||||
uint r0, r1, r2, r3 = C3 ^ kw[3];
|
||||
int r = ROUNDS - 1;
|
||||
while (r > 1)
|
||||
{
|
||||
kw = KW[r--];
|
||||
r0 = Tinv0[t0 & 255] ^ Shift(Tinv0[(r3 >> 8) & 255], 24) ^ Shift(Tinv0[(t2 >> 16) & 255], 16) ^ Shift(Tinv0[(t1 >> 24) & 255], 8) ^ kw[0];
|
||||
r1 = Tinv0[t1 & 255] ^ Shift(Tinv0[(t0 >> 8) & 255], 24) ^ Shift(Tinv0[(r3 >> 16) & 255], 16) ^ Shift(Tinv0[(t2 >> 24) & 255], 8) ^ kw[1];
|
||||
r2 = Tinv0[t2 & 255] ^ Shift(Tinv0[(t1 >> 8) & 255], 24) ^ Shift(Tinv0[(t0 >> 16) & 255], 16) ^ Shift(Tinv0[(r3 >> 24) & 255], 8) ^ kw[2];
|
||||
r3 = Tinv0[r3 & 255] ^ Shift(Tinv0[(t2 >> 8) & 255], 24) ^ Shift(Tinv0[(t1 >> 16) & 255], 16) ^ Shift(Tinv0[(t0 >> 24) & 255], 8) ^ kw[3];
|
||||
kw = KW[r--];
|
||||
t0 = Tinv0[r0 & 255] ^ Shift(Tinv0[(r3 >> 8) & 255], 24) ^ Shift(Tinv0[(r2 >> 16) & 255], 16) ^ Shift(Tinv0[(r1 >> 24) & 255], 8) ^ kw[0];
|
||||
t1 = Tinv0[r1 & 255] ^ Shift(Tinv0[(r0 >> 8) & 255], 24) ^ Shift(Tinv0[(r3 >> 16) & 255], 16) ^ Shift(Tinv0[(r2 >> 24) & 255], 8) ^ kw[1];
|
||||
t2 = Tinv0[r2 & 255] ^ Shift(Tinv0[(r1 >> 8) & 255], 24) ^ Shift(Tinv0[(r0 >> 16) & 255], 16) ^ Shift(Tinv0[(r3 >> 24) & 255], 8) ^ kw[2];
|
||||
r3 = Tinv0[r3 & 255] ^ Shift(Tinv0[(r2 >> 8) & 255], 24) ^ Shift(Tinv0[(r1 >> 16) & 255], 16) ^ Shift(Tinv0[(r0 >> 24) & 255], 8) ^ kw[3];
|
||||
}
|
||||
|
||||
kw = KW[1];
|
||||
r0 = Tinv0[t0 & 255] ^ Shift(Tinv0[(r3 >> 8) & 255], 24) ^ Shift(Tinv0[(t2 >> 16) & 255], 16) ^ Shift(Tinv0[(t1 >> 24) & 255], 8) ^ kw[0];
|
||||
r1 = Tinv0[t1 & 255] ^ Shift(Tinv0[(t0 >> 8) & 255], 24) ^ Shift(Tinv0[(r3 >> 16) & 255], 16) ^ Shift(Tinv0[(t2 >> 24) & 255], 8) ^ kw[1];
|
||||
r2 = Tinv0[t2 & 255] ^ Shift(Tinv0[(t1 >> 8) & 255], 24) ^ Shift(Tinv0[(t0 >> 16) & 255], 16) ^ Shift(Tinv0[(r3 >> 24) & 255], 8) ^ kw[2];
|
||||
r3 = Tinv0[r3 & 255] ^ Shift(Tinv0[(t2 >> 8) & 255], 24) ^ Shift(Tinv0[(t1 >> 16) & 255], 16) ^ Shift(Tinv0[(t0 >> 24) & 255], 8) ^ kw[3];
|
||||
|
||||
// the final round's table is a simple function of Si so we don't use a whole other four tables for it
|
||||
|
||||
kw = KW[0];
|
||||
C0 = (uint)Si[r0 & 255] ^ (((uint)s[(r3 >> 8) & 255]) << 8) ^ (((uint)s[(r2 >> 16) & 255]) << 16) ^ (((uint)Si[(r1 >> 24) & 255]) << 24) ^ kw[0];
|
||||
C1 = (uint)s[r1 & 255] ^ (((uint)s[(r0 >> 8) & 255]) << 8) ^ (((uint)Si[(r3 >> 16) & 255]) << 16) ^ (((uint)s[(r2 >> 24) & 255]) << 24) ^ kw[1];
|
||||
C2 = (uint)s[r2 & 255] ^ (((uint)Si[(r1 >> 8) & 255]) << 8) ^ (((uint)Si[(r0 >> 16) & 255]) << 16) ^ (((uint)s[(r3 >> 24) & 255]) << 24) ^ kw[2];
|
||||
C3 = (uint)Si[r3 & 255] ^ (((uint)s[(r2 >> 8) & 255]) << 8) ^ (((uint)s[(r1 >> 16) & 255]) << 16) ^ (((uint)s[(r0 >> 24) & 255]) << 24) ^ kw[3];
|
||||
|
||||
Pack.UInt32_To_LE(C0, output);
|
||||
Pack.UInt32_To_LE(C1, output[4..]);
|
||||
Pack.UInt32_To_LE(C2, output[8..]);
|
||||
Pack.UInt32_To_LE(C3, output[12..]);
|
||||
}
|
||||
#else
|
||||
private void EncryptBlock(byte[] input, int inOff, byte[] output, int outOff, uint[][] KW)
|
||||
{
|
||||
FastAesEngineHelper.EncryptBlock(input, inOff, output, outOff, KW, ROUNDS, T0, S, s);
|
||||
//uint C0 = Pack.LE_To_UInt32(input, inOff + 0);
|
||||
//uint C1 = Pack.LE_To_UInt32(input, inOff + 4);
|
||||
//uint C2 = Pack.LE_To_UInt32(input, inOff + 8);
|
||||
//uint C3 = Pack.LE_To_UInt32(input, inOff + 12);
|
||||
//
|
||||
//uint[] kw = KW[0];
|
||||
//uint t0 = C0 ^ kw[0];
|
||||
//uint t1 = C1 ^ kw[1];
|
||||
//uint t2 = C2 ^ kw[2];
|
||||
//
|
||||
//uint r0, r1, r2, r3 = C3 ^ kw[3];
|
||||
//int r = 1;
|
||||
//while (r < ROUNDS - 1)
|
||||
//{
|
||||
// kw = KW[r++];
|
||||
// r0 = T0[t0 & 255] ^ Shift(T0[(t1 >> 8) & 255], 24) ^ Shift(T0[(t2 >> 16) & 255], 16) ^ Shift(T0[(r3 >> 24) & 255], 8) ^ kw[0];
|
||||
// r1 = T0[t1 & 255] ^ Shift(T0[(t2 >> 8) & 255], 24) ^ Shift(T0[(r3 >> 16) & 255], 16) ^ Shift(T0[(t0 >> 24) & 255], 8) ^ kw[1];
|
||||
// r2 = T0[t2 & 255] ^ Shift(T0[(r3 >> 8) & 255], 24) ^ Shift(T0[(t0 >> 16) & 255], 16) ^ Shift(T0[(t1 >> 24) & 255], 8) ^ kw[2];
|
||||
// r3 = T0[r3 & 255] ^ Shift(T0[(t0 >> 8) & 255], 24) ^ Shift(T0[(t1 >> 16) & 255], 16) ^ Shift(T0[(t2 >> 24) & 255], 8) ^ kw[3];
|
||||
// kw = KW[r++];
|
||||
// t0 = T0[r0 & 255] ^ Shift(T0[(r1 >> 8) & 255], 24) ^ Shift(T0[(r2 >> 16) & 255], 16) ^ Shift(T0[(r3 >> 24) & 255], 8) ^ kw[0];
|
||||
// t1 = T0[r1 & 255] ^ Shift(T0[(r2 >> 8) & 255], 24) ^ Shift(T0[(r3 >> 16) & 255], 16) ^ Shift(T0[(r0 >> 24) & 255], 8) ^ kw[1];
|
||||
// t2 = T0[r2 & 255] ^ Shift(T0[(r3 >> 8) & 255], 24) ^ Shift(T0[(r0 >> 16) & 255], 16) ^ Shift(T0[(r1 >> 24) & 255], 8) ^ kw[2];
|
||||
// r3 = T0[r3 & 255] ^ Shift(T0[(r0 >> 8) & 255], 24) ^ Shift(T0[(r1 >> 16) & 255], 16) ^ Shift(T0[(r2 >> 24) & 255], 8) ^ kw[3];
|
||||
//}
|
||||
//
|
||||
//kw = KW[r++];
|
||||
//r0 = T0[t0 & 255] ^ Shift(T0[(t1 >> 8) & 255], 24) ^ Shift(T0[(t2 >> 16) & 255], 16) ^ Shift(T0[(r3 >> 24) & 255], 8) ^ kw[0];
|
||||
//r1 = T0[t1 & 255] ^ Shift(T0[(t2 >> 8) & 255], 24) ^ Shift(T0[(r3 >> 16) & 255], 16) ^ Shift(T0[(t0 >> 24) & 255], 8) ^ kw[1];
|
||||
//r2 = T0[t2 & 255] ^ Shift(T0[(r3 >> 8) & 255], 24) ^ Shift(T0[(t0 >> 16) & 255], 16) ^ Shift(T0[(t1 >> 24) & 255], 8) ^ kw[2];
|
||||
//r3 = T0[r3 & 255] ^ Shift(T0[(t0 >> 8) & 255], 24) ^ Shift(T0[(t1 >> 16) & 255], 16) ^ Shift(T0[(t2 >> 24) & 255], 8) ^ kw[3];
|
||||
//
|
||||
//// the final round's table is a simple function of S so we don't use a whole other four tables for it
|
||||
//
|
||||
//kw = KW[r];
|
||||
//C0 = (uint)S[r0 & 255] ^ (((uint)S[(r1 >> 8) & 255]) << 8) ^ (((uint)s[(r2 >> 16) & 255]) << 16) ^ (((uint)s[(r3 >> 24) & 255]) << 24) ^ kw[0];
|
||||
//C1 = (uint)s[r1 & 255] ^ (((uint)S[(r2 >> 8) & 255]) << 8) ^ (((uint)S[(r3 >> 16) & 255]) << 16) ^ (((uint)s[(r0 >> 24) & 255]) << 24) ^ kw[1];
|
||||
//C2 = (uint)s[r2 & 255] ^ (((uint)S[(r3 >> 8) & 255]) << 8) ^ (((uint)S[(r0 >> 16) & 255]) << 16) ^ (((uint)S[(r1 >> 24) & 255]) << 24) ^ kw[2];
|
||||
//C3 = (uint)s[r3 & 255] ^ (((uint)s[(r0 >> 8) & 255]) << 8) ^ (((uint)s[(r1 >> 16) & 255]) << 16) ^ (((uint)S[(r2 >> 24) & 255]) << 24) ^ kw[3];
|
||||
//
|
||||
//Pack.UInt32_To_LE(C0, output, outOff + 0);
|
||||
//Pack.UInt32_To_LE(C1, output, outOff + 4);
|
||||
//Pack.UInt32_To_LE(C2, output, outOff + 8);
|
||||
//Pack.UInt32_To_LE(C3, output, outOff + 12);
|
||||
}
|
||||
|
||||
private void DecryptBlock(byte[] input, int inOff, byte[] output, int outOff, uint[][] KW)
|
||||
{
|
||||
uint C0 = Pack.LE_To_UInt32(input, inOff + 0);
|
||||
uint C1 = Pack.LE_To_UInt32(input, inOff + 4);
|
||||
uint C2 = Pack.LE_To_UInt32(input, inOff + 8);
|
||||
uint C3 = Pack.LE_To_UInt32(input, inOff + 12);
|
||||
|
||||
uint[] kw = KW[ROUNDS];
|
||||
uint t0 = C0 ^ kw[0];
|
||||
uint t1 = C1 ^ kw[1];
|
||||
uint t2 = C2 ^ kw[2];
|
||||
|
||||
uint r0, r1, r2, r3 = C3 ^ kw[3];
|
||||
int r = ROUNDS - 1;
|
||||
while (r > 1)
|
||||
{
|
||||
kw = KW[r--];
|
||||
r0 = Tinv0[t0 & 255] ^ Shift(Tinv0[(r3 >> 8) & 255], 24) ^ Shift(Tinv0[(t2 >> 16) & 255], 16) ^ Shift(Tinv0[(t1 >> 24) & 255], 8) ^ kw[0];
|
||||
r1 = Tinv0[t1 & 255] ^ Shift(Tinv0[(t0 >> 8) & 255], 24) ^ Shift(Tinv0[(r3 >> 16) & 255], 16) ^ Shift(Tinv0[(t2 >> 24) & 255], 8) ^ kw[1];
|
||||
r2 = Tinv0[t2 & 255] ^ Shift(Tinv0[(t1 >> 8) & 255], 24) ^ Shift(Tinv0[(t0 >> 16) & 255], 16) ^ Shift(Tinv0[(r3 >> 24) & 255], 8) ^ kw[2];
|
||||
r3 = Tinv0[r3 & 255] ^ Shift(Tinv0[(t2 >> 8) & 255], 24) ^ Shift(Tinv0[(t1 >> 16) & 255], 16) ^ Shift(Tinv0[(t0 >> 24) & 255], 8) ^ kw[3];
|
||||
kw = KW[r--];
|
||||
t0 = Tinv0[r0 & 255] ^ Shift(Tinv0[(r3 >> 8) & 255], 24) ^ Shift(Tinv0[(r2 >> 16) & 255], 16) ^ Shift(Tinv0[(r1 >> 24) & 255], 8) ^ kw[0];
|
||||
t1 = Tinv0[r1 & 255] ^ Shift(Tinv0[(r0 >> 8) & 255], 24) ^ Shift(Tinv0[(r3 >> 16) & 255], 16) ^ Shift(Tinv0[(r2 >> 24) & 255], 8) ^ kw[1];
|
||||
t2 = Tinv0[r2 & 255] ^ Shift(Tinv0[(r1 >> 8) & 255], 24) ^ Shift(Tinv0[(r0 >> 16) & 255], 16) ^ Shift(Tinv0[(r3 >> 24) & 255], 8) ^ kw[2];
|
||||
r3 = Tinv0[r3 & 255] ^ Shift(Tinv0[(r2 >> 8) & 255], 24) ^ Shift(Tinv0[(r1 >> 16) & 255], 16) ^ Shift(Tinv0[(r0 >> 24) & 255], 8) ^ kw[3];
|
||||
}
|
||||
|
||||
kw = KW[1];
|
||||
r0 = Tinv0[t0 & 255] ^ Shift(Tinv0[(r3 >> 8) & 255], 24) ^ Shift(Tinv0[(t2 >> 16) & 255], 16) ^ Shift(Tinv0[(t1 >> 24) & 255], 8) ^ kw[0];
|
||||
r1 = Tinv0[t1 & 255] ^ Shift(Tinv0[(t0 >> 8) & 255], 24) ^ Shift(Tinv0[(r3 >> 16) & 255], 16) ^ Shift(Tinv0[(t2 >> 24) & 255], 8) ^ kw[1];
|
||||
r2 = Tinv0[t2 & 255] ^ Shift(Tinv0[(t1 >> 8) & 255], 24) ^ Shift(Tinv0[(t0 >> 16) & 255], 16) ^ Shift(Tinv0[(r3 >> 24) & 255], 8) ^ kw[2];
|
||||
r3 = Tinv0[r3 & 255] ^ Shift(Tinv0[(t2 >> 8) & 255], 24) ^ Shift(Tinv0[(t1 >> 16) & 255], 16) ^ Shift(Tinv0[(t0 >> 24) & 255], 8) ^ kw[3];
|
||||
|
||||
// the final round's table is a simple function of Si so we don't use a whole other four tables for it
|
||||
|
||||
kw = KW[0];
|
||||
C0 = (uint)Si[r0 & 255] ^ (((uint)s[(r3 >> 8) & 255]) << 8) ^ (((uint)s[(r2 >> 16) & 255]) << 16) ^ (((uint)Si[(r1 >> 24) & 255]) << 24) ^ kw[0];
|
||||
C1 = (uint)s[r1 & 255] ^ (((uint)s[(r0 >> 8) & 255]) << 8) ^ (((uint)Si[(r3 >> 16) & 255]) << 16) ^ (((uint)s[(r2 >> 24) & 255]) << 24) ^ kw[1];
|
||||
C2 = (uint)s[r2 & 255] ^ (((uint)Si[(r1 >> 8) & 255]) << 8) ^ (((uint)Si[(r0 >> 16) & 255]) << 16) ^ (((uint)s[(r3 >> 24) & 255]) << 24) ^ kw[2];
|
||||
C3 = (uint)Si[r3 & 255] ^ (((uint)s[(r2 >> 8) & 255]) << 8) ^ (((uint)s[(r1 >> 16) & 255]) << 16) ^ (((uint)s[(r0 >> 24) & 255]) << 24) ^ kw[3];
|
||||
|
||||
Pack.UInt32_To_LE(C0, output, outOff + 0);
|
||||
Pack.UInt32_To_LE(C1, output, outOff + 4);
|
||||
Pack.UInt32_To_LE(C2, output, outOff + 8);
|
||||
Pack.UInt32_To_LE(C3, output, outOff + 12);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#pragma warning restore
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 04025dfce7b22d142b61813959b98226
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,195 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
using System;
|
||||
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Utilities;
|
||||
|
||||
namespace BestHTTP.Connections.TLS.Crypto.Impl
|
||||
{
|
||||
internal static class FastAesEngineHelper
|
||||
{
|
||||
public unsafe static void EncryptBlock(byte[] input, int inOff, byte[] output, int outOff, uint[][] KW, int ROUNDS, uint[] T0, byte[] S, byte[] s)
|
||||
{
|
||||
uint C0 = Pack.LE_To_UInt32(input, inOff + 0);
|
||||
uint C1 = Pack.LE_To_UInt32(input, inOff + 4);
|
||||
uint C2 = Pack.LE_To_UInt32(input, inOff + 8);
|
||||
uint C3 = Pack.LE_To_UInt32(input, inOff + 12);
|
||||
|
||||
uint[] kw = KW[0];
|
||||
uint t0 = C0 ^ kw[0];
|
||||
uint t1 = C1 ^ kw[1];
|
||||
uint t2 = C2 ^ kw[2];
|
||||
|
||||
uint r0, r1, r2, r3 = C3 ^ kw[3];
|
||||
int r = 1;
|
||||
|
||||
byte idx;
|
||||
uint tmp1, tmp2, tmp3;
|
||||
|
||||
fixed (uint* pT0 = T0)
|
||||
{
|
||||
while (r < ROUNDS - 1)
|
||||
{
|
||||
kw = KW[r++];
|
||||
|
||||
fixed (uint* pkw = kw)
|
||||
{
|
||||
idx = (byte)(t1 >> 8);
|
||||
tmp1 = (pT0[idx] >> 24) | (pT0[idx] << 8);
|
||||
|
||||
idx = (byte)(t2 >> 16);
|
||||
tmp2 = (pT0[idx] >> 16) | (pT0[idx] << 16);
|
||||
|
||||
idx = (byte)(r3 >> 24);
|
||||
tmp3 = (pT0[idx] >> 8) | (pT0[idx] << 24);
|
||||
r0 = pT0[t0 & 255] ^ tmp1 ^ tmp2 ^ tmp3 ^ pkw[0];
|
||||
|
||||
idx = (byte)(t2 >> 8);
|
||||
tmp1 = (pT0[idx] >> 24) | (pT0[idx] << 8);
|
||||
|
||||
idx = (byte)(r3 >> 16);
|
||||
tmp2 = (pT0[idx] >> 16) | (pT0[idx] << 16);
|
||||
|
||||
idx = (byte)(t0 >> 24);
|
||||
tmp3 = (pT0[idx] >> 8) | (pT0[idx] << 24);
|
||||
r1 = pT0[t1 & 255] ^ tmp1 ^ tmp2 ^ tmp3 ^ pkw[1];
|
||||
|
||||
idx = (byte)(r3 >> 8);
|
||||
tmp1 = (pT0[idx] >> 24) | (pT0[idx] << 8);
|
||||
|
||||
idx = (byte)(t0 >> 16);
|
||||
tmp2 = (pT0[idx] >> 16) | (pT0[idx] << 16);
|
||||
|
||||
idx = (byte)(t1 >> 24);
|
||||
tmp3 = (pT0[idx] >> 8) | (pT0[idx] << 24);
|
||||
|
||||
r2 = pT0[t2 & 255] ^ tmp1 ^ tmp2 ^ tmp3 ^ pkw[2];
|
||||
|
||||
idx = (byte)(t0 >> 8);
|
||||
tmp1 = (pT0[idx] >> 24) | (pT0[idx] << 8);
|
||||
|
||||
idx = (byte)(t1 >> 16);
|
||||
tmp2 = (pT0[idx] >> 16) | (pT0[idx] << 16);
|
||||
|
||||
idx = (byte)(t2 >> 24);
|
||||
tmp3 = (pT0[idx] >> 8) | (pT0[idx] << 24);
|
||||
|
||||
r3 = pT0[r3 & 255] ^ tmp1 ^ tmp2 ^ tmp3 ^ pkw[3];
|
||||
}
|
||||
|
||||
kw = KW[r++];
|
||||
|
||||
fixed (uint* pkw = kw)
|
||||
{
|
||||
idx = (byte)(r1 >> 8);
|
||||
tmp1 = (pT0[idx] >> 24) | (pT0[idx] << 8);
|
||||
|
||||
idx = (byte)(r2 >> 16);
|
||||
tmp2 = (pT0[idx] >> 16) | (pT0[idx] << 16);
|
||||
|
||||
idx = (byte)(r3 >> 24);
|
||||
tmp3 = (pT0[idx] >> 8) | (pT0[idx] << 24);
|
||||
t0 = pT0[r0 & 255] ^ tmp1 ^ tmp2 ^ tmp3 ^ pkw[0];
|
||||
|
||||
idx = (byte)(r2 >> 8);
|
||||
tmp1 = (pT0[idx] >> 24) | (pT0[idx] << 8);
|
||||
|
||||
idx = (byte)(r3 >> 16);
|
||||
tmp2 = (pT0[idx] >> 16) | (pT0[idx] << 16);
|
||||
|
||||
idx = (byte)(r0 >> 24);
|
||||
tmp3 = (pT0[idx] >> 8) | (pT0[idx] << 24);
|
||||
t1 = pT0[r1 & 255] ^ tmp1 ^ tmp2 ^ tmp3 ^ pkw[1];
|
||||
|
||||
idx = (byte)(r3 >> 8);
|
||||
tmp1 = (pT0[idx] >> 24) | (pT0[idx] << 8);
|
||||
|
||||
idx = (byte)(r0 >> 16);
|
||||
tmp2 = (pT0[idx] >> 16) | (pT0[idx] << 16);
|
||||
|
||||
idx = (byte)(r1 >> 24);
|
||||
tmp3 = (pT0[idx] >> 8) | (pT0[idx] << 24);
|
||||
|
||||
t2 = pT0[r2 & 255] ^ tmp1 ^ tmp2 ^ tmp3 ^ pkw[2];
|
||||
|
||||
idx = (byte)(r0 >> 8);
|
||||
tmp1 = (pT0[idx] >> 24) | (pT0[idx] << 8);
|
||||
|
||||
idx = (byte)(r1 >> 16);
|
||||
tmp2 = (pT0[idx] >> 16) | (pT0[idx] << 16);
|
||||
|
||||
idx = (byte)(r2 >> 24);
|
||||
tmp3 = (pT0[idx] >> 8) | (pT0[idx] << 24);
|
||||
r3 = pT0[r3 & 255] ^ tmp1 ^ tmp2 ^ tmp3 ^ pkw[3];
|
||||
}
|
||||
}
|
||||
|
||||
kw = KW[r++];
|
||||
|
||||
fixed (uint* pkw = kw)
|
||||
{
|
||||
idx = (byte)(t1 >> 8);
|
||||
tmp1 = (pT0[idx] >> 24) | (pT0[idx] << 8);
|
||||
|
||||
idx = (byte)(t2 >> 16);
|
||||
tmp2 = (pT0[idx] >> 16) | (pT0[idx] << 16);
|
||||
|
||||
idx = (byte)(r3 >> 24);
|
||||
tmp3 = (pT0[idx] >> 8) | (pT0[idx] << 24);
|
||||
r0 = pT0[t0 & 255] ^ tmp1 ^ tmp2 ^ tmp3 ^ pkw[0];
|
||||
|
||||
idx = (byte)(t2 >> 8);
|
||||
tmp1 = (pT0[idx] >> 24) | (pT0[idx] << 8);
|
||||
|
||||
idx = (byte)(r3 >> 16);
|
||||
tmp2 = (pT0[idx] >> 16) | (pT0[idx] << 16);
|
||||
|
||||
idx = (byte)(t0 >> 24);
|
||||
tmp3 = (pT0[idx] >> 8) | (pT0[idx] << 24);
|
||||
|
||||
r1 = pT0[t1 & 255] ^ tmp1 ^ tmp2 ^ tmp3 ^ pkw[1];
|
||||
|
||||
idx = (byte)(r3 >> 8);
|
||||
tmp1 = (pT0[idx] >> 24) | (pT0[idx] << 8);
|
||||
|
||||
idx = (byte)(t0 >> 16);
|
||||
tmp2 = (pT0[idx] >> 16) | (pT0[idx] << 16);
|
||||
|
||||
idx = (byte)(t1 >> 24);
|
||||
tmp3 = (pT0[idx] >> 8) | (pT0[idx] << 24);
|
||||
|
||||
r2 = pT0[t2 & 255] ^ tmp1 ^ tmp2 ^ tmp3 ^ pkw[2];
|
||||
|
||||
idx = (byte)(t0 >> 8);
|
||||
tmp1 = (pT0[idx] >> 24) | (pT0[idx] << 8);
|
||||
|
||||
idx = (byte)(t1 >> 16);
|
||||
tmp2 = (pT0[idx] >> 16) | (pT0[idx] << 16);
|
||||
|
||||
idx = (byte)(t2 >> 24);
|
||||
tmp3 = (pT0[idx] >> 8) | (pT0[idx] << 24);
|
||||
|
||||
r3 = pT0[r3 & 255] ^ tmp1 ^ tmp2 ^ tmp3 ^ pkw[3];
|
||||
}
|
||||
|
||||
// the final round's table is a simple function of S so we don't use a whole other four tables for it
|
||||
|
||||
kw = KW[r];
|
||||
|
||||
fixed (byte* pS = S, ps = s)
|
||||
fixed (uint* pkw = kw)
|
||||
{
|
||||
C0 = (uint)pS[(byte)r0] ^ (((uint)pS[(byte)(r1 >> 8)]) << 8) ^ (((uint)ps[(byte)(r2 >> 16)]) << 16) ^ (((uint)ps[(byte)(r3 >> 24)]) << 24) ^ pkw[0];
|
||||
C1 = (uint)ps[(byte)r1] ^ (((uint)pS[(byte)(r2 >> 8)]) << 8) ^ (((uint)pS[(byte)(r3 >> 16)]) << 16) ^ (((uint)ps[(byte)(r0 >> 24)]) << 24) ^ pkw[1];
|
||||
C2 = (uint)ps[(byte)r2] ^ (((uint)pS[(byte)(r3 >> 8)]) << 8) ^ (((uint)pS[(byte)(r0 >> 16)]) << 16) ^ (((uint)pS[(byte)(r1 >> 24)]) << 24) ^ pkw[2];
|
||||
C3 = (uint)ps[(byte)r3] ^ (((uint)ps[(byte)(r0 >> 8)]) << 8) ^ (((uint)ps[(byte)(r1 >> 16)]) << 16) ^ (((uint)pS[(byte)(r2 >> 24)]) << 24) ^ pkw[3];
|
||||
}
|
||||
}
|
||||
|
||||
Pack.UInt32_To_LE(C0, output, outOff + 0);
|
||||
Pack.UInt32_To_LE(C1, output, outOff + 4);
|
||||
Pack.UInt32_To_LE(C2, output, outOff + 8);
|
||||
Pack.UInt32_To_LE(C3, output, outOff + 12);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0f41ac81f5b4391419ba276b4bb78044
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,189 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
#pragma warning disable
|
||||
using System;
|
||||
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Engines;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Macs;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Utilities;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Tls.Crypto.Impl;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Tls;
|
||||
using BestHTTP.PlatformSupport.Memory;
|
||||
|
||||
namespace BestHTTP.Connections.TLS.Crypto.Impl
|
||||
{
|
||||
public sealed class FastBcChaCha20Poly1305
|
||||
: TlsAeadCipherImpl
|
||||
{
|
||||
private static readonly byte[] Zeroes = new byte[15];
|
||||
|
||||
private readonly FastChaCha7539Engine m_cipher = new FastChaCha7539Engine();
|
||||
private readonly FastPoly1305 m_mac = new FastPoly1305();
|
||||
|
||||
private readonly bool m_isEncrypting;
|
||||
|
||||
private int m_additionalDataLength;
|
||||
|
||||
public FastBcChaCha20Poly1305(bool isEncrypting)
|
||||
{
|
||||
this.m_isEncrypting = isEncrypting;
|
||||
}
|
||||
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
unsafe
|
||||
#endif
|
||||
public int DoFinal(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset)
|
||||
{
|
||||
if (m_isEncrypting)
|
||||
{
|
||||
int ciphertextLength = inputLength;
|
||||
|
||||
m_cipher.DoFinal(input, inputOffset, inputLength, output, outputOffset);
|
||||
int outputLength = inputLength;
|
||||
|
||||
if (ciphertextLength != outputLength)
|
||||
throw new InvalidOperationException();
|
||||
|
||||
UpdateMac(output, outputOffset, ciphertextLength);
|
||||
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
Span<byte> lengths = stackalloc byte[16];
|
||||
|
||||
Pack.UInt64_To_LE((ulong)m_additionalDataLength, lengths);
|
||||
Pack.UInt64_To_LE((ulong)ciphertextLength, lengths[8..]);
|
||||
|
||||
m_mac.BlockUpdate(lengths);
|
||||
m_mac.DoFinal(output.AsSpan(outputOffset + ciphertextLength));
|
||||
#else
|
||||
byte[] lengths = BufferPool.Get(16, true);
|
||||
using (var _ = new PooledBuffer(lengths))
|
||||
{
|
||||
Pack.UInt64_To_LE((ulong)m_additionalDataLength, lengths, 0);
|
||||
Pack.UInt64_To_LE((ulong)ciphertextLength, lengths, 8);
|
||||
|
||||
m_mac.BlockUpdate(lengths, 0, 16);
|
||||
m_mac.DoFinal(output, outputOffset + ciphertextLength);
|
||||
}
|
||||
#endif
|
||||
|
||||
return ciphertextLength + 16;
|
||||
}
|
||||
else
|
||||
{
|
||||
int ciphertextLength = inputLength - 16;
|
||||
|
||||
UpdateMac(input, inputOffset, ciphertextLength);
|
||||
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
Span<byte> expectedMac = stackalloc byte[16];
|
||||
|
||||
Pack.UInt64_To_LE((ulong)m_additionalDataLength, expectedMac);
|
||||
Pack.UInt64_To_LE((ulong)ciphertextLength, expectedMac[8..]);
|
||||
|
||||
m_mac.BlockUpdate(expectedMac);
|
||||
m_mac.DoFinal(expectedMac);
|
||||
|
||||
bool badMac = !TlsUtilities.ConstantTimeAreEqual(16, expectedMac, 0, input, inputOffset + ciphertextLength);
|
||||
|
||||
if (badMac)
|
||||
throw new TlsFatalAlert(AlertDescription.bad_record_mac);
|
||||
#else
|
||||
byte[] expectedMac = BufferPool.Get(16, true);
|
||||
using (var _ = new PooledBuffer(expectedMac))
|
||||
{
|
||||
Pack.UInt64_To_LE((ulong)m_additionalDataLength, expectedMac, 0);
|
||||
Pack.UInt64_To_LE((ulong)ciphertextLength, expectedMac, 8);
|
||||
|
||||
m_mac.BlockUpdate(expectedMac, 0, 16);
|
||||
m_mac.DoFinal(expectedMac, 0);
|
||||
|
||||
bool badMac = !TlsUtilities.ConstantTimeAreEqual(16, expectedMac, 0, input, inputOffset + ciphertextLength);
|
||||
|
||||
if (badMac)
|
||||
throw new TlsFatalAlert(AlertDescription.bad_record_mac);
|
||||
}
|
||||
#endif
|
||||
|
||||
m_cipher.DoFinal(input, inputOffset, ciphertextLength, output, outputOffset);
|
||||
int outputLength = ciphertextLength;
|
||||
|
||||
if (ciphertextLength != outputLength)
|
||||
throw new InvalidOperationException();
|
||||
|
||||
return ciphertextLength;
|
||||
}
|
||||
}
|
||||
|
||||
public int GetOutputSize(int inputLength)
|
||||
{
|
||||
return m_isEncrypting ? inputLength + 16 : inputLength - 16;
|
||||
}
|
||||
|
||||
public void Init(byte[] nonce, int macSize, byte[] additionalData)
|
||||
{
|
||||
if (nonce == null || nonce.Length != 12 || macSize != 16)
|
||||
throw new TlsFatalAlert(AlertDescription.internal_error);
|
||||
|
||||
m_cipher.Init(m_isEncrypting, new ParametersWithIV(null, nonce));
|
||||
InitMac();
|
||||
if (additionalData == null)
|
||||
{
|
||||
this.m_additionalDataLength = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.m_additionalDataLength = additionalData.Length;
|
||||
UpdateMac(additionalData, 0, additionalData.Length);
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
m_cipher.Reset();
|
||||
m_mac.Reset();
|
||||
}
|
||||
|
||||
public void SetKey(byte[] key, int keyOff, int keyLen)
|
||||
{
|
||||
KeyParameter cipherKey = new KeyParameter(key, keyOff, keyLen);
|
||||
m_cipher.Init(m_isEncrypting, new ParametersWithIV(cipherKey, Zeroes, 0, 12));
|
||||
}
|
||||
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
public void SetKey(ReadOnlySpan<byte> key)
|
||||
{
|
||||
KeyParameter cipherKey = new KeyParameter(key);
|
||||
m_cipher.Init(m_isEncrypting, new ParametersWithIV(cipherKey, Zeroes[..12]));
|
||||
}
|
||||
#endif
|
||||
|
||||
byte[] firstBlock = new byte[64];
|
||||
private void InitMac()
|
||||
{
|
||||
m_cipher.ProcessBytes(firstBlock, 0, 64, firstBlock, 0);
|
||||
m_mac.Init(new KeyParameter(firstBlock, 0, 32));
|
||||
Array.Clear(firstBlock, 0, firstBlock.Length);
|
||||
}
|
||||
|
||||
private void UpdateMac(byte[] buf, int off, int len)
|
||||
{
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
m_mac.BlockUpdate(buf.AsSpan(off, len));
|
||||
#else
|
||||
m_mac.BlockUpdate(buf, off, len);
|
||||
#endif
|
||||
|
||||
int partial = len % 16;
|
||||
if (partial != 0)
|
||||
{
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
m_mac.BlockUpdate(Zeroes.AsSpan(0, 16 - partial));
|
||||
#else
|
||||
m_mac.BlockUpdate(Zeroes, 0, 16 - partial);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8147dce9f66d3aa42acf242bd200aa46
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,229 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
#pragma warning disable
|
||||
using System;
|
||||
|
||||
using BestHTTP.Connections.TLS.Crypto.Impl;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
|
||||
|
||||
namespace BestHTTP.Connections.TLS.Crypto
|
||||
{
|
||||
/**
|
||||
* implements Cipher-Block-Chaining (CBC) mode on top of a simple cipher.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
[BestHTTP.PlatformSupport.IL2CPP.Il2CppEagerStaticClassConstructionAttribute]
|
||||
public sealed class FastCbcBlockCipher
|
||||
: IBlockCipherMode
|
||||
{
|
||||
private byte[] IV, cbcV, cbcNextV;
|
||||
private int blockSize;
|
||||
private IBlockCipher cipher;
|
||||
private bool encrypting;
|
||||
|
||||
/**
|
||||
* Basic constructor.
|
||||
*
|
||||
* @param cipher the block cipher to be used as the basis of chaining.
|
||||
*/
|
||||
public FastCbcBlockCipher(
|
||||
IBlockCipher cipher)
|
||||
{
|
||||
this.cipher = cipher;
|
||||
this.blockSize = cipher.GetBlockSize();
|
||||
|
||||
this.IV = new byte[blockSize];
|
||||
this.cbcV = new byte[blockSize];
|
||||
this.cbcNextV = new byte[blockSize];
|
||||
}
|
||||
|
||||
/**
|
||||
* return the underlying block cipher that we are wrapping.
|
||||
*
|
||||
* @return the underlying block cipher that we are wrapping.
|
||||
*/
|
||||
public IBlockCipher UnderlyingCipher => cipher;
|
||||
|
||||
/**
|
||||
* Initialise the cipher and, possibly, the initialisation vector (IV).
|
||||
* If an IV isn't passed as part of the parameter, the IV will be all zeros.
|
||||
*
|
||||
* @param forEncryption if true the cipher is initialised for
|
||||
* encryption, if false for decryption.
|
||||
* @param param the key and other data required by the cipher.
|
||||
* @exception ArgumentException if the parameters argument is
|
||||
* inappropriate.
|
||||
*/
|
||||
public void Init(bool forEncryption, ICipherParameters parameters)
|
||||
{
|
||||
bool oldEncrypting = this.encrypting;
|
||||
|
||||
this.encrypting = forEncryption;
|
||||
|
||||
if (parameters is ParametersWithIV ivParam)
|
||||
{
|
||||
byte[] iv = ivParam.GetIV();
|
||||
|
||||
if (iv.Length != blockSize)
|
||||
throw new ArgumentException("initialisation vector must be the same length as block size");
|
||||
|
||||
Array.Copy(iv, 0, IV, 0, iv.Length);
|
||||
|
||||
parameters = ivParam.Parameters;
|
||||
}
|
||||
|
||||
Reset();
|
||||
|
||||
// if null it's an IV changed only.
|
||||
if (parameters != null)
|
||||
{
|
||||
cipher.Init(encrypting, parameters);
|
||||
}
|
||||
else if (oldEncrypting != encrypting)
|
||||
{
|
||||
throw new ArgumentException("cannot change encrypting state without providing key.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* return the algorithm name and mode.
|
||||
*
|
||||
* @return the name of the underlying algorithm followed by "/CBC".
|
||||
*/
|
||||
public string AlgorithmName
|
||||
{
|
||||
get { return cipher.AlgorithmName + "/CBC"; }
|
||||
}
|
||||
|
||||
public bool IsPartialBlockOkay
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/**
|
||||
* return the block size of the underlying cipher.
|
||||
*
|
||||
* @return the block size of the underlying cipher.
|
||||
*/
|
||||
public int GetBlockSize()
|
||||
{
|
||||
return cipher.GetBlockSize();
|
||||
}
|
||||
|
||||
public int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
|
||||
{
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
return encrypting
|
||||
? EncryptBlock(input.AsSpan(inOff), output.AsSpan(outOff))
|
||||
: DecryptBlock(input.AsSpan(inOff), output.AsSpan(outOff));
|
||||
#else
|
||||
return encrypting
|
||||
? EncryptBlock(input, inOff, output, outOff)
|
||||
: DecryptBlock(input, inOff, output, outOff);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
public int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
|
||||
{
|
||||
return encrypting
|
||||
? EncryptBlock(input, output)
|
||||
: DecryptBlock(input, output);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* reset the chaining vector back to the IV and reset the underlying
|
||||
* cipher.
|
||||
*/
|
||||
public void Reset()
|
||||
{
|
||||
Array.Copy(IV, 0, cbcV, 0, IV.Length);
|
||||
Array.Clear(cbcNextV, 0, cbcNextV.Length);
|
||||
}
|
||||
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
private int EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
|
||||
{
|
||||
Check.DataLength(input, blockSize, "input buffer too short");
|
||||
Check.OutputLength(output, blockSize, "output buffer too short");
|
||||
|
||||
for (int i = 0; i < blockSize; i++)
|
||||
{
|
||||
cbcV[i] ^= input[i];
|
||||
}
|
||||
|
||||
int length = cipher.ProcessBlock(cbcV, output);
|
||||
|
||||
output[..blockSize].CopyTo(cbcV);
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
private int DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
|
||||
{
|
||||
Check.DataLength(input, blockSize, "input buffer too short");
|
||||
Check.OutputLength(output, blockSize, "output buffer too short");
|
||||
|
||||
input[..blockSize].CopyTo(cbcNextV);
|
||||
|
||||
int length = cipher.ProcessBlock(input, output);
|
||||
|
||||
for (int i = 0; i < blockSize; i++)
|
||||
{
|
||||
output[i] ^= cbcV[i];
|
||||
}
|
||||
|
||||
byte[] tmp = cbcV;
|
||||
cbcV = cbcNextV;
|
||||
cbcNextV = tmp;
|
||||
|
||||
return length;
|
||||
}
|
||||
#else
|
||||
private int EncryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff)
|
||||
{
|
||||
Check.DataLength(input, inOff, blockSize, "input buffer too short");
|
||||
Check.OutputLength(outBytes, outOff, blockSize, "output buffer too short");
|
||||
|
||||
for (int i = 0; i < blockSize; i++)
|
||||
{
|
||||
cbcV[i] ^= input[inOff + i];
|
||||
}
|
||||
|
||||
int length = cipher.ProcessBlock(cbcV, 0, outBytes, outOff);
|
||||
|
||||
Array.Copy(outBytes, outOff, cbcV, 0, cbcV.Length);
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
private int DecryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff)
|
||||
{
|
||||
Check.DataLength(input, inOff, blockSize, "input buffer too short");
|
||||
Check.OutputLength(outBytes, outOff, blockSize, "output buffer too short");
|
||||
|
||||
Array.Copy(input, inOff, cbcNextV, 0, blockSize);
|
||||
|
||||
int length = cipher.ProcessBlock(input, inOff, outBytes, outOff);
|
||||
|
||||
for (int i = 0; i < blockSize; i++)
|
||||
{
|
||||
outBytes[outOff + i] ^= cbcV[i];
|
||||
}
|
||||
|
||||
byte[] tmp = cbcV;
|
||||
cbcV = cbcNextV;
|
||||
cbcNextV = tmp;
|
||||
|
||||
return length;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
#pragma warning restore
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 885849ed4738c9c4197fbee14f00dd5e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,661 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
#pragma warning disable
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Macs;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities;
|
||||
|
||||
namespace BestHTTP.Connections.TLS.Crypto.Impl
|
||||
{
|
||||
/**
|
||||
* Implements the Counter with Cipher Block Chaining mode (CCM) detailed in
|
||||
* NIST Special Publication 800-38C.
|
||||
* <p>
|
||||
* <b>Note</b>: this mode is a packet mode - it needs all the data up front.
|
||||
* </p>
|
||||
*/
|
||||
public class FastCcmBlockCipher
|
||||
: IAeadBlockCipher
|
||||
{
|
||||
private static readonly int BlockSize = 16;
|
||||
|
||||
private readonly IBlockCipher cipher;
|
||||
private readonly byte[] macBlock;
|
||||
private bool forEncryption;
|
||||
private byte[] nonce;
|
||||
private byte[] initialAssociatedText;
|
||||
private int macSize;
|
||||
private ICipherParameters keyParam;
|
||||
private readonly MemoryStream associatedText = new MemoryStream();
|
||||
private readonly MemoryStream data = new MemoryStream();
|
||||
|
||||
/**
|
||||
* Basic constructor.
|
||||
*
|
||||
* @param cipher the block cipher to be used.
|
||||
*/
|
||||
public FastCcmBlockCipher(
|
||||
IBlockCipher cipher)
|
||||
{
|
||||
this.cipher = cipher;
|
||||
this.macBlock = new byte[BlockSize];
|
||||
|
||||
if (cipher.GetBlockSize() != BlockSize)
|
||||
throw new ArgumentException("cipher required with a block size of " + BlockSize + ".");
|
||||
}
|
||||
|
||||
/**
|
||||
* return the underlying block cipher that we are wrapping.
|
||||
*
|
||||
* @return the underlying block cipher that we are wrapping.
|
||||
*/
|
||||
public virtual IBlockCipher UnderlyingCipher => cipher;
|
||||
|
||||
public virtual void Init(bool forEncryption, ICipherParameters parameters)
|
||||
{
|
||||
this.forEncryption = forEncryption;
|
||||
|
||||
ICipherParameters cipherParameters;
|
||||
if (parameters is AeadParameters aeadParameters)
|
||||
{
|
||||
nonce = aeadParameters.GetNonce();
|
||||
initialAssociatedText = aeadParameters.GetAssociatedText();
|
||||
macSize = GetMacSize(forEncryption, aeadParameters.MacSize);
|
||||
cipherParameters = aeadParameters.Key;
|
||||
}
|
||||
else if (parameters is ParametersWithIV parametersWithIV)
|
||||
{
|
||||
nonce = parametersWithIV.GetIV();
|
||||
initialAssociatedText = null;
|
||||
macSize = GetMacSize(forEncryption, 64);
|
||||
cipherParameters = parametersWithIV.Parameters;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException("invalid parameters passed to CCM");
|
||||
}
|
||||
|
||||
// NOTE: Very basic support for key re-use, but no performance gain from it
|
||||
if (cipherParameters != null)
|
||||
{
|
||||
keyParam = cipherParameters;
|
||||
}
|
||||
|
||||
if (nonce == null || nonce.Length < 7 || nonce.Length > 13)
|
||||
throw new ArgumentException("nonce must have length from 7 to 13 octets");
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
public virtual string AlgorithmName => cipher.AlgorithmName + "/CCM";
|
||||
|
||||
public virtual int GetBlockSize()
|
||||
{
|
||||
return cipher.GetBlockSize();
|
||||
}
|
||||
|
||||
public virtual void ProcessAadByte(byte input)
|
||||
{
|
||||
associatedText.WriteByte(input);
|
||||
}
|
||||
|
||||
public virtual void ProcessAadBytes(byte[] inBytes, int inOff, int len)
|
||||
{
|
||||
// TODO: Process AAD online
|
||||
associatedText.Write(inBytes, inOff, len);
|
||||
}
|
||||
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
public virtual void ProcessAadBytes(ReadOnlySpan<byte> input)
|
||||
{
|
||||
// TODO: Process AAD online
|
||||
associatedText.Write(input);
|
||||
}
|
||||
#endif
|
||||
|
||||
public virtual int ProcessByte(byte input, byte[] outBytes, int outOff)
|
||||
{
|
||||
data.WriteByte(input);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
public virtual int ProcessByte(byte input, Span<byte> output)
|
||||
{
|
||||
data.WriteByte(input);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
public virtual int ProcessBytes(byte[] inBytes, int inOff, int inLen, byte[] outBytes, int outOff)
|
||||
{
|
||||
Check.DataLength(inBytes, inOff, inLen, "input buffer too short");
|
||||
|
||||
data.Write(inBytes, inOff, inLen);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
public virtual int ProcessBytes(ReadOnlySpan<byte> input, Span<byte> output)
|
||||
{
|
||||
data.Write(input);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
public virtual int DoFinal(byte[] outBytes, int outOff)
|
||||
{
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
return DoFinal(outBytes.AsSpan(outOff));
|
||||
#else
|
||||
byte[] input = data.GetBuffer();
|
||||
int inLen = Convert.ToInt32(data.Length);
|
||||
|
||||
int len = ProcessPacket(input, 0, inLen, outBytes, outOff);
|
||||
|
||||
Reset();
|
||||
|
||||
return len;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
public virtual int DoFinal(Span<byte> output)
|
||||
{
|
||||
byte[] input = data.GetBuffer();
|
||||
int inLen = Convert.ToInt32(data.Length);
|
||||
|
||||
int len = ProcessPacket(input.AsSpan(0, inLen), output);
|
||||
|
||||
Reset();
|
||||
|
||||
return len;
|
||||
}
|
||||
#endif
|
||||
|
||||
public virtual void Reset()
|
||||
{
|
||||
associatedText.SetLength(0);
|
||||
data.SetLength(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a byte array containing the mac calculated as part of the
|
||||
* last encrypt or decrypt operation.
|
||||
*
|
||||
* @return the last mac calculated.
|
||||
*/
|
||||
public virtual byte[] GetMac()
|
||||
{
|
||||
return Arrays.CopyOfRange(macBlock, 0, macSize);
|
||||
}
|
||||
|
||||
public virtual int GetUpdateOutputSize(int len)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public virtual int GetOutputSize(int len)
|
||||
{
|
||||
int totalData = Convert.ToInt32(data.Length) + len;
|
||||
|
||||
if (forEncryption)
|
||||
{
|
||||
return totalData + macSize;
|
||||
}
|
||||
|
||||
return totalData < macSize ? 0 : totalData - macSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a packet of data for either CCM decryption or encryption.
|
||||
*
|
||||
* @param in data for processing.
|
||||
* @param inOff offset at which data starts in the input array.
|
||||
* @param inLen length of the data in the input array.
|
||||
* @return a byte array containing the processed input..
|
||||
* @throws IllegalStateException if the cipher is not appropriately set up.
|
||||
* @throws InvalidCipherTextException if the input data is truncated or the mac check fails.
|
||||
*/
|
||||
public virtual byte[] ProcessPacket(byte[] input, int inOff, int inLen)
|
||||
{
|
||||
byte[] output;
|
||||
|
||||
if (forEncryption)
|
||||
{
|
||||
output = new byte[inLen + macSize];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (inLen < macSize)
|
||||
throw new InvalidCipherTextException("data too short");
|
||||
|
||||
output = new byte[inLen - macSize];
|
||||
}
|
||||
|
||||
ProcessPacket(input, inOff, inLen, output, 0);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a packet of data for either CCM decryption or encryption.
|
||||
*
|
||||
* @param in data for processing.
|
||||
* @param inOff offset at which data starts in the input array.
|
||||
* @param inLen length of the data in the input array.
|
||||
* @param output output array.
|
||||
* @param outOff offset into output array to start putting processed bytes.
|
||||
* @return the number of bytes added to output.
|
||||
* @throws IllegalStateException if the cipher is not appropriately set up.
|
||||
* @throws InvalidCipherTextException if the input data is truncated or the mac check fails.
|
||||
* @throws DataLengthException if output buffer too short.
|
||||
*/
|
||||
public virtual int ProcessPacket(byte[] input, int inOff, int inLen, byte[] output, int outOff)
|
||||
{
|
||||
// TODO: handle null keyParam (e.g. via RepeatedKeySpec)
|
||||
// Need to keep the CTR and CBC Mac parts around and reset
|
||||
if (keyParam == null)
|
||||
throw new InvalidOperationException("CCM cipher unitialized.");
|
||||
|
||||
int n = nonce.Length;
|
||||
int q = 15 - n;
|
||||
if (q < 4)
|
||||
{
|
||||
int limitLen = 1 << (8 * q);
|
||||
if (inLen >= limitLen)
|
||||
throw new InvalidOperationException("CCM packet too large for choice of q.");
|
||||
}
|
||||
|
||||
byte[] iv = new byte[BlockSize];
|
||||
iv[0] = (byte)((q - 1) & 0x7);
|
||||
nonce.CopyTo(iv, 1);
|
||||
|
||||
IBlockCipher ctrCipher = new FastSicBlockCipher(cipher);
|
||||
ctrCipher.Init(forEncryption, new ParametersWithIV(keyParam, iv));
|
||||
|
||||
int outputLen;
|
||||
int inIndex = inOff;
|
||||
int outIndex = outOff;
|
||||
|
||||
if (forEncryption)
|
||||
{
|
||||
outputLen = inLen + macSize;
|
||||
Check.OutputLength(output, outOff, outputLen, "Output buffer too short.");
|
||||
|
||||
CalculateMac(input, inOff, inLen, macBlock);
|
||||
|
||||
byte[] encMac = new byte[BlockSize];
|
||||
ctrCipher.ProcessBlock(macBlock, 0, encMac, 0); // S0
|
||||
|
||||
while (inIndex < (inOff + inLen - BlockSize)) // S1...
|
||||
{
|
||||
ctrCipher.ProcessBlock(input, inIndex, output, outIndex);
|
||||
outIndex += BlockSize;
|
||||
inIndex += BlockSize;
|
||||
}
|
||||
|
||||
byte[] block = new byte[BlockSize];
|
||||
|
||||
Array.Copy(input, inIndex, block, 0, inLen + inOff - inIndex);
|
||||
|
||||
ctrCipher.ProcessBlock(block, 0, block, 0);
|
||||
|
||||
Array.Copy(block, 0, output, outIndex, inLen + inOff - inIndex);
|
||||
|
||||
Array.Copy(encMac, 0, output, outOff + inLen, macSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (inLen < macSize)
|
||||
throw new InvalidCipherTextException("data too short");
|
||||
|
||||
outputLen = inLen - macSize;
|
||||
Check.OutputLength(output, outOff, outputLen, "Output buffer too short.");
|
||||
|
||||
Array.Copy(input, inOff + outputLen, macBlock, 0, macSize);
|
||||
|
||||
ctrCipher.ProcessBlock(macBlock, 0, macBlock, 0);
|
||||
|
||||
for (int i = macSize; i != macBlock.Length; i++)
|
||||
{
|
||||
macBlock[i] = 0;
|
||||
}
|
||||
|
||||
while (inIndex < (inOff + outputLen - BlockSize))
|
||||
{
|
||||
ctrCipher.ProcessBlock(input, inIndex, output, outIndex);
|
||||
outIndex += BlockSize;
|
||||
inIndex += BlockSize;
|
||||
}
|
||||
|
||||
byte[] block = new byte[BlockSize];
|
||||
|
||||
Array.Copy(input, inIndex, block, 0, outputLen - (inIndex - inOff));
|
||||
|
||||
ctrCipher.ProcessBlock(block, 0, block, 0);
|
||||
|
||||
Array.Copy(block, 0, output, outIndex, outputLen - (inIndex - inOff));
|
||||
|
||||
byte[] calculatedMacBlock = new byte[BlockSize];
|
||||
|
||||
CalculateMac(output, outOff, outputLen, calculatedMacBlock);
|
||||
|
||||
if (!Arrays.ConstantTimeAreEqual(macBlock, calculatedMacBlock))
|
||||
throw new InvalidCipherTextException("mac check in CCM failed");
|
||||
}
|
||||
|
||||
return outputLen;
|
||||
}
|
||||
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
public virtual int ProcessPacket(ReadOnlySpan<byte> input, Span<byte> output)
|
||||
{
|
||||
int inLen = input.Length;
|
||||
|
||||
// TODO: handle null keyParam (e.g. via RepeatedKeySpec)
|
||||
// Need to keep the CTR and CBC Mac parts around and reset
|
||||
if (keyParam == null)
|
||||
throw new InvalidOperationException("CCM cipher unitialized.");
|
||||
|
||||
int n = nonce.Length;
|
||||
int q = 15 - n;
|
||||
if (q < 4)
|
||||
{
|
||||
int limitLen = 1 << (8 * q);
|
||||
if (inLen >= limitLen)
|
||||
throw new InvalidOperationException("CCM packet too large for choice of q.");
|
||||
}
|
||||
|
||||
byte[] iv = new byte[BlockSize];
|
||||
iv[0] = (byte)((q - 1) & 0x7);
|
||||
nonce.CopyTo(iv, 1);
|
||||
|
||||
IBlockCipher ctrCipher = new SicBlockCipher(cipher);
|
||||
ctrCipher.Init(forEncryption, new ParametersWithIV(keyParam, iv));
|
||||
|
||||
int outputLen;
|
||||
int index = 0;
|
||||
Span<byte> block = stackalloc byte[BlockSize];
|
||||
|
||||
if (forEncryption)
|
||||
{
|
||||
outputLen = inLen + macSize;
|
||||
Check.OutputLength(output, outputLen, "output buffer too short");
|
||||
|
||||
CalculateMac(input, macBlock);
|
||||
|
||||
byte[] encMac = new byte[BlockSize];
|
||||
ctrCipher.ProcessBlock(macBlock, encMac); // S0
|
||||
|
||||
while (index < (inLen - BlockSize)) // S1...
|
||||
{
|
||||
ctrCipher.ProcessBlock(input[index..], output[index..]);
|
||||
index += BlockSize;
|
||||
}
|
||||
|
||||
input[index..].CopyTo(block);
|
||||
|
||||
ctrCipher.ProcessBlock(block, block);
|
||||
|
||||
block[..(inLen - index)].CopyTo(output[index..]);
|
||||
|
||||
encMac.AsSpan(0, macSize).CopyTo(output[inLen..]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (inLen < macSize)
|
||||
throw new InvalidCipherTextException("data too short");
|
||||
|
||||
outputLen = inLen - macSize;
|
||||
Check.OutputLength(output, outputLen, "output buffer too short");
|
||||
|
||||
input[outputLen..].CopyTo(macBlock);
|
||||
|
||||
ctrCipher.ProcessBlock(macBlock, macBlock);
|
||||
|
||||
for (int i = macSize; i != macBlock.Length; i++)
|
||||
{
|
||||
macBlock[i] = 0;
|
||||
}
|
||||
|
||||
while (index < (outputLen - BlockSize))
|
||||
{
|
||||
ctrCipher.ProcessBlock(input[index..], output[index..]);
|
||||
index += BlockSize;
|
||||
}
|
||||
|
||||
input[index..outputLen].CopyTo(block);
|
||||
|
||||
ctrCipher.ProcessBlock(block, block);
|
||||
|
||||
block[..(outputLen - index)].CopyTo(output[index..]);
|
||||
|
||||
Span<byte> calculatedMacBlock = stackalloc byte[BlockSize];
|
||||
|
||||
CalculateMac(output[..outputLen], calculatedMacBlock);
|
||||
|
||||
if (!Arrays.ConstantTimeAreEqual(macBlock, calculatedMacBlock))
|
||||
throw new InvalidCipherTextException("mac check in CCM failed");
|
||||
}
|
||||
|
||||
return outputLen;
|
||||
}
|
||||
#endif
|
||||
|
||||
private int CalculateMac(byte[] data, int dataOff, int dataLen, byte[] macBlock)
|
||||
{
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
return CalculateMac(data.AsSpan(dataOff, dataLen), macBlock);
|
||||
#else
|
||||
IMac cMac = new CbcBlockCipherMac(cipher, macSize * 8);
|
||||
|
||||
cMac.Init(keyParam);
|
||||
|
||||
//
|
||||
// build b0
|
||||
//
|
||||
byte[] b0 = new byte[16];
|
||||
|
||||
if (HasAssociatedText())
|
||||
{
|
||||
b0[0] |= 0x40;
|
||||
}
|
||||
|
||||
b0[0] |= (byte)((((cMac.GetMacSize() - 2) / 2) & 0x7) << 3);
|
||||
|
||||
b0[0] |= (byte)(((15 - nonce.Length) - 1) & 0x7);
|
||||
|
||||
Array.Copy(nonce, 0, b0, 1, nonce.Length);
|
||||
|
||||
int q = dataLen;
|
||||
int count = 1;
|
||||
while (q > 0)
|
||||
{
|
||||
b0[b0.Length - count] = (byte)(q & 0xff);
|
||||
q >>= 8;
|
||||
count++;
|
||||
}
|
||||
|
||||
cMac.BlockUpdate(b0, 0, b0.Length);
|
||||
|
||||
//
|
||||
// process associated text
|
||||
//
|
||||
if (HasAssociatedText())
|
||||
{
|
||||
int extra;
|
||||
|
||||
int textLength = GetAssociatedTextLength();
|
||||
if (textLength < ((1 << 16) - (1 << 8)))
|
||||
{
|
||||
cMac.Update((byte)(textLength >> 8));
|
||||
cMac.Update((byte)textLength);
|
||||
|
||||
extra = 2;
|
||||
}
|
||||
else // can't go any higher than 2^32
|
||||
{
|
||||
cMac.Update((byte)0xff);
|
||||
cMac.Update((byte)0xfe);
|
||||
cMac.Update((byte)(textLength >> 24));
|
||||
cMac.Update((byte)(textLength >> 16));
|
||||
cMac.Update((byte)(textLength >> 8));
|
||||
cMac.Update((byte)textLength);
|
||||
|
||||
extra = 6;
|
||||
}
|
||||
|
||||
if (initialAssociatedText != null)
|
||||
{
|
||||
cMac.BlockUpdate(initialAssociatedText, 0, initialAssociatedText.Length);
|
||||
}
|
||||
if (associatedText.Length > 0)
|
||||
{
|
||||
byte[] input = associatedText.GetBuffer();
|
||||
int len = Convert.ToInt32(associatedText.Length);
|
||||
|
||||
cMac.BlockUpdate(input, 0, len);
|
||||
}
|
||||
|
||||
extra = (extra + textLength) % 16;
|
||||
if (extra != 0)
|
||||
{
|
||||
for (int i = extra; i < 16; ++i)
|
||||
{
|
||||
cMac.Update((byte)0x00);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// add the text
|
||||
//
|
||||
cMac.BlockUpdate(data, dataOff, dataLen);
|
||||
|
||||
return cMac.DoFinal(macBlock, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
private int CalculateMac(ReadOnlySpan<byte> data, Span<byte> macBlock)
|
||||
{
|
||||
IMac cMac = new CbcBlockCipherMac(cipher, macSize * 8);
|
||||
|
||||
cMac.Init(keyParam);
|
||||
|
||||
//
|
||||
// build b0
|
||||
//
|
||||
byte[] b0 = new byte[16];
|
||||
|
||||
if (HasAssociatedText())
|
||||
{
|
||||
b0[0] |= 0x40;
|
||||
}
|
||||
|
||||
b0[0] |= (byte)((((cMac.GetMacSize() - 2) / 2) & 0x7) << 3);
|
||||
|
||||
b0[0] |= (byte)(((15 - nonce.Length) - 1) & 0x7);
|
||||
|
||||
Array.Copy(nonce, 0, b0, 1, nonce.Length);
|
||||
|
||||
int q = data.Length;
|
||||
int count = 1;
|
||||
while (q > 0)
|
||||
{
|
||||
b0[b0.Length - count] = (byte)(q & 0xff);
|
||||
q >>= 8;
|
||||
count++;
|
||||
}
|
||||
|
||||
cMac.BlockUpdate(b0, 0, b0.Length);
|
||||
|
||||
//
|
||||
// process associated text
|
||||
//
|
||||
if (HasAssociatedText())
|
||||
{
|
||||
int extra;
|
||||
|
||||
int textLength = GetAssociatedTextLength();
|
||||
if (textLength < ((1 << 16) - (1 << 8)))
|
||||
{
|
||||
cMac.Update((byte)(textLength >> 8));
|
||||
cMac.Update((byte)textLength);
|
||||
|
||||
extra = 2;
|
||||
}
|
||||
else // can't go any higher than 2^32
|
||||
{
|
||||
cMac.Update((byte)0xff);
|
||||
cMac.Update((byte)0xfe);
|
||||
cMac.Update((byte)(textLength >> 24));
|
||||
cMac.Update((byte)(textLength >> 16));
|
||||
cMac.Update((byte)(textLength >> 8));
|
||||
cMac.Update((byte)textLength);
|
||||
|
||||
extra = 6;
|
||||
}
|
||||
|
||||
if (initialAssociatedText != null)
|
||||
{
|
||||
cMac.BlockUpdate(initialAssociatedText, 0, initialAssociatedText.Length);
|
||||
}
|
||||
if (associatedText.Length > 0)
|
||||
{
|
||||
byte[] input = associatedText.GetBuffer();
|
||||
int len = Convert.ToInt32(associatedText.Length);
|
||||
|
||||
cMac.BlockUpdate(input, 0, len);
|
||||
}
|
||||
|
||||
extra = (extra + textLength) % 16;
|
||||
if (extra != 0)
|
||||
{
|
||||
for (int i = extra; i < 16; ++i)
|
||||
{
|
||||
cMac.Update((byte)0x00);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// add the text
|
||||
//
|
||||
cMac.BlockUpdate(data);
|
||||
|
||||
return cMac.DoFinal(macBlock);
|
||||
}
|
||||
#endif
|
||||
|
||||
private int GetMacSize(bool forEncryption, int requestedMacBits)
|
||||
{
|
||||
if (forEncryption && (requestedMacBits < 32 || requestedMacBits > 128 || 0 != (requestedMacBits & 15)))
|
||||
throw new ArgumentException("tag length in octets must be one of {4,6,8,10,12,14,16}");
|
||||
|
||||
return requestedMacBits >> 3;
|
||||
}
|
||||
|
||||
private int GetAssociatedTextLength()
|
||||
{
|
||||
return Convert.ToInt32(associatedText.Length) +
|
||||
(initialAssociatedText == null ? 0 : initialAssociatedText.Length);
|
||||
}
|
||||
|
||||
private bool HasAssociatedText()
|
||||
{
|
||||
return GetAssociatedTextLength() > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 017b9fa6887917045ac682f80707a9ad
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,531 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
#pragma warning disable
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Engines;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Utilities;
|
||||
|
||||
namespace BestHTTP.Connections.TLS.Crypto.Impl
|
||||
{
|
||||
/// <summary>
|
||||
/// Implementation of Daniel J. Bernstein's ChaCha stream cipher.
|
||||
/// </summary>
|
||||
|
||||
|
||||
|
||||
[BestHTTP.PlatformSupport.IL2CPP.Il2CppEagerStaticClassConstructionAttribute]
|
||||
public sealed class FastChaCha7539Engine
|
||||
: FastSalsa20Engine
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a 20 rounds ChaCha engine.
|
||||
/// </summary>
|
||||
public FastChaCha7539Engine()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
|
||||
public override string AlgorithmName
|
||||
{
|
||||
get { return "ChaCha7539"; }
|
||||
}
|
||||
|
||||
protected override int NonceSize
|
||||
{
|
||||
get { return 12; }
|
||||
}
|
||||
|
||||
protected override void AdvanceCounter()
|
||||
{
|
||||
if (++engineState[12] == 0)
|
||||
throw new InvalidOperationException("attempt to increase counter past 2^32.");
|
||||
}
|
||||
|
||||
protected override void ResetCounter()
|
||||
{
|
||||
engineState[12] = 0;
|
||||
}
|
||||
|
||||
protected override void SetKey(byte[] keyBytes, byte[] ivBytes)
|
||||
{
|
||||
if (keyBytes != null)
|
||||
{
|
||||
if (keyBytes.Length != 32)
|
||||
throw new ArgumentException(AlgorithmName + " requires 256 bit key");
|
||||
|
||||
PackTauOrSigma(keyBytes.Length, engineState, 0);
|
||||
|
||||
// Key
|
||||
Pack.LE_To_UInt32(keyBytes, 0, engineState, 4, 8);
|
||||
}
|
||||
|
||||
// IV
|
||||
Pack.LE_To_UInt32(ivBytes, 0, engineState, 13, 3);
|
||||
}
|
||||
|
||||
protected override void GenerateKeyStream(byte[] output)
|
||||
{
|
||||
FastChaChaEngineHelper.ChachaCore(rounds, engineState, output);
|
||||
}
|
||||
|
||||
internal void DoFinal(byte[] inBuf, int inOff, int inLen, byte[] outBuf, int outOff)
|
||||
{
|
||||
if (!initialised)
|
||||
throw new InvalidOperationException(AlgorithmName + " not initialised");
|
||||
if (index != 0)
|
||||
throw new InvalidOperationException(AlgorithmName + " not in block-aligned state");
|
||||
|
||||
Check.DataLength(inBuf, inOff, inLen, "input buffer too short");
|
||||
Check.OutputLength(outBuf, outOff, inLen, "output buffer too short");
|
||||
|
||||
while (inLen >= 128)
|
||||
{
|
||||
#if BESTHTTP_WITH_BURST
|
||||
FastChaCha7539EngineHelper.ProcessBlocks2(inBuf.AsSpan(inOff), outBuf.AsSpan(outOff), engineState, rounds, keyStream);
|
||||
#elif NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
//ProcessBlocks2(inBuf.AsSpan(inOff), outBuf.AsSpan(outOff));
|
||||
|
||||
var input = inBuf.AsSpan(inOff);
|
||||
var output = outBuf.AsSpan(outOff);
|
||||
ImplProcessBlock(inBuf.AsSpan(inOff), outBuf.AsSpan(outOff));
|
||||
ImplProcessBlock(input[64..], output[64..]);
|
||||
#else
|
||||
ProcessBlocks2(inBuf, inOff, outBuf, outOff);
|
||||
#endif
|
||||
inOff += 128;
|
||||
inLen -= 128;
|
||||
outOff += 128;
|
||||
}
|
||||
|
||||
if (inLen >= 64)
|
||||
{
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
ImplProcessBlock(inBuf.AsSpan(inOff), outBuf.AsSpan(outOff));
|
||||
#else
|
||||
ImplProcessBlock(inBuf, inOff, outBuf, outOff);
|
||||
#endif
|
||||
inOff += 64;
|
||||
inLen -= 64;
|
||||
outOff += 64;
|
||||
}
|
||||
|
||||
if (inLen > 0)
|
||||
{
|
||||
GenerateKeyStream(keyStream);
|
||||
//AdvanceCounter();
|
||||
if (++engineState[12] == 0)
|
||||
throw new InvalidOperationException("attempt to increase counter past 2^32.");
|
||||
|
||||
for (int i = 0; i < inLen; ++i)
|
||||
{
|
||||
outBuf[outOff + i] = (byte)(inBuf[i + inOff] ^ keyStream[i]);
|
||||
}
|
||||
}
|
||||
|
||||
engineState[12] = 0;
|
||||
|
||||
// TODO Prevent re-use if encrypting
|
||||
}
|
||||
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
internal void ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
|
||||
{
|
||||
if (!initialised)
|
||||
throw new InvalidOperationException(AlgorithmName + " not initialised");
|
||||
if (LimitExceeded(64U))
|
||||
throw new MaxBytesExceededException("2^38 byte limit per IV would be exceeded; Change IV");
|
||||
|
||||
UnityEngine.Debug.Assert(index == 0);
|
||||
|
||||
ImplProcessBlock(input, output);
|
||||
}
|
||||
|
||||
internal void ProcessBlocks2(ReadOnlySpan<byte> input, Span<byte> output)
|
||||
{
|
||||
if (!initialised)
|
||||
throw new InvalidOperationException(AlgorithmName + " not initialised");
|
||||
if (LimitExceeded(128U))
|
||||
throw new MaxBytesExceededException("2^38 byte limit per IV would be exceeded; Change IV");
|
||||
|
||||
UnityEngine.Debug.Assert(index == 0);
|
||||
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
if (Avx2.IsSupported)
|
||||
{
|
||||
ImplProcessBlocks2_X86_Avx2(rounds, engineState, input, output);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Sse2.IsSupported)
|
||||
{
|
||||
ImplProcessBlocks2_X86_Sse2(rounds, engineState, input, output);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
{
|
||||
ImplProcessBlock(input, output);
|
||||
ImplProcessBlock(input[64..], output[64..]);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal void ImplProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
|
||||
{
|
||||
FastChaChaEngineHelper.ChachaCore(rounds, engineState, keyStream);
|
||||
|
||||
//AdvanceCounter();
|
||||
if (++engineState[12] == 0)
|
||||
throw new InvalidOperationException("attempt to increase counter past 2^32.");
|
||||
|
||||
FastChaChaEngineHelper.ImplProcessBlock(input, output, keyStream);
|
||||
}
|
||||
#else
|
||||
internal void ProcessBlock(byte[] inBytes, int inOff, byte[] outBytes, int outOff)
|
||||
{
|
||||
if (!initialised)
|
||||
throw new InvalidOperationException(AlgorithmName + " not initialised");
|
||||
if (LimitExceeded(64U))
|
||||
throw new MaxBytesExceededException("2^38 byte limit per IV would be exceeded; Change IV");
|
||||
|
||||
UnityEngine.Debug.Assert(index == 0);
|
||||
|
||||
ImplProcessBlock(inBytes, inOff, outBytes, outOff);
|
||||
}
|
||||
|
||||
internal void ProcessBlocks2(byte[] inBytes, int inOff, byte[] outBytes, int outOff)
|
||||
{
|
||||
if (!initialised)
|
||||
throw new InvalidOperationException(AlgorithmName + " not initialised");
|
||||
if (LimitExceeded(128U))
|
||||
throw new MaxBytesExceededException("2^38 byte limit per IV would be exceeded; Change IV");
|
||||
|
||||
UnityEngine.Debug.Assert(index == 0);
|
||||
|
||||
{
|
||||
ImplProcessBlock(inBytes, inOff, outBytes, outOff);
|
||||
ImplProcessBlock(inBytes, inOff + 64, outBytes, outOff + 64);
|
||||
}
|
||||
}
|
||||
|
||||
#if NETSTANDARD1_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER || UNITY_2021_2_OR_NEWER
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#endif
|
||||
internal void ImplProcessBlock(byte[] inBuf, int inOff, byte[] outBuf, int outOff)
|
||||
{
|
||||
ChaChaEngine.ChachaCore(rounds, engineState, keyStream);
|
||||
AdvanceCounter();
|
||||
|
||||
for (int i = 0; i < 64; ++i)
|
||||
{
|
||||
outBuf[outOff + i] = (byte)(keyStream[i] ^ inBuf[inOff + i]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static void ImplProcessBlocks2_X86_Avx2(int rounds, uint[] state, ReadOnlySpan<byte> input,
|
||||
Span<byte> output)
|
||||
{
|
||||
if (!Avx2.IsSupported)
|
||||
throw new PlatformNotSupportedException();
|
||||
|
||||
Debug.Assert(rounds % 2 == 0);
|
||||
Debug.Assert(state.Length >= 16);
|
||||
Debug.Assert(input.Length >= 128);
|
||||
Debug.Assert(output.Length >= 128);
|
||||
|
||||
var t0 = Load128_UInt32(state.AsSpan());
|
||||
var t1 = Load128_UInt32(state.AsSpan(4));
|
||||
var t2 = Load128_UInt32(state.AsSpan(8));
|
||||
var t3 = Load128_UInt32(state.AsSpan(12));
|
||||
++state[12];
|
||||
var t4 = Load128_UInt32(state.AsSpan(12));
|
||||
++state[12];
|
||||
|
||||
var x0 = Vector256.Create(t0, t0);
|
||||
var x1 = Vector256.Create(t1, t1);
|
||||
var x2 = Vector256.Create(t2, t2);
|
||||
var x3 = Vector256.Create(t3, t4);
|
||||
|
||||
var v0 = x0;
|
||||
var v1 = x1;
|
||||
var v2 = x2;
|
||||
var v3 = x3;
|
||||
|
||||
for (int i = rounds; i > 0; i -= 2)
|
||||
{
|
||||
v0 = Avx2.Add(v0, v1);
|
||||
v3 = Avx2.Xor(v3, v0);
|
||||
v3 = Avx2.Xor(Avx2.ShiftLeftLogical(v3, 16), Avx2.ShiftRightLogical(v3, 16));
|
||||
v2 = Avx2.Add(v2, v3);
|
||||
v1 = Avx2.Xor(v1, v2);
|
||||
v1 = Avx2.Xor(Avx2.ShiftLeftLogical(v1, 12), Avx2.ShiftRightLogical(v1, 20));
|
||||
v0 = Avx2.Add(v0, v1);
|
||||
v3 = Avx2.Xor(v3, v0);
|
||||
v3 = Avx2.Xor(Avx2.ShiftLeftLogical(v3, 8), Avx2.ShiftRightLogical(v3, 24));
|
||||
v2 = Avx2.Add(v2, v3);
|
||||
v1 = Avx2.Xor(v1, v2);
|
||||
v1 = Avx2.Xor(Avx2.ShiftLeftLogical(v1, 7), Avx2.ShiftRightLogical(v1, 25));
|
||||
|
||||
v1 = Avx2.Shuffle(v1, 0x39);
|
||||
v2 = Avx2.Shuffle(v2, 0x4E);
|
||||
v3 = Avx2.Shuffle(v3, 0x93);
|
||||
|
||||
v0 = Avx2.Add(v0, v1);
|
||||
v3 = Avx2.Xor(v3, v0);
|
||||
v3 = Avx2.Xor(Avx2.ShiftLeftLogical(v3, 16), Avx2.ShiftRightLogical(v3, 16));
|
||||
v2 = Avx2.Add(v2, v3);
|
||||
v1 = Avx2.Xor(v1, v2);
|
||||
v1 = Avx2.Xor(Avx2.ShiftLeftLogical(v1, 12), Avx2.ShiftRightLogical(v1, 20));
|
||||
v0 = Avx2.Add(v0, v1);
|
||||
v3 = Avx2.Xor(v3, v0);
|
||||
v3 = Avx2.Xor(Avx2.ShiftLeftLogical(v3, 8), Avx2.ShiftRightLogical(v3, 24));
|
||||
v2 = Avx2.Add(v2, v3);
|
||||
v1 = Avx2.Xor(v1, v2);
|
||||
v1 = Avx2.Xor(Avx2.ShiftLeftLogical(v1, 7), Avx2.ShiftRightLogical(v1, 25));
|
||||
|
||||
v1 = Avx2.Shuffle(v1, 0x93);
|
||||
v2 = Avx2.Shuffle(v2, 0x4E);
|
||||
v3 = Avx2.Shuffle(v3, 0x39);
|
||||
}
|
||||
|
||||
v0 = Avx2.Add(v0, x0);
|
||||
v1 = Avx2.Add(v1, x1);
|
||||
v2 = Avx2.Add(v2, x2);
|
||||
v3 = Avx2.Add(v3, x3);
|
||||
|
||||
var n0 = Avx2.Permute2x128(v0, v1, 0x20).AsByte();
|
||||
var n1 = Avx2.Permute2x128(v2, v3, 0x20).AsByte();
|
||||
var n2 = Avx2.Permute2x128(v0, v1, 0x31).AsByte();
|
||||
var n3 = Avx2.Permute2x128(v2, v3, 0x31).AsByte();
|
||||
|
||||
n0 = Avx2.Xor(n0, Load256_Byte(input));
|
||||
n1 = Avx2.Xor(n1, Load256_Byte(input[0x20..]));
|
||||
n2 = Avx2.Xor(n2, Load256_Byte(input[0x40..]));
|
||||
n3 = Avx2.Xor(n3, Load256_Byte(input[0x60..]));
|
||||
|
||||
Store256_Byte(n0, output);
|
||||
Store256_Byte(n1, output[0x20..]);
|
||||
Store256_Byte(n2, output[0x40..]);
|
||||
Store256_Byte(n3, output[0x60..]);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static void ImplProcessBlocks2_X86_Sse2(int rounds, uint[] state, ReadOnlySpan<byte> input,
|
||||
Span<byte> output)
|
||||
{
|
||||
if (!Sse2.IsSupported)
|
||||
throw new PlatformNotSupportedException();
|
||||
|
||||
Debug.Assert(rounds % 2 == 0);
|
||||
Debug.Assert(state.Length >= 16);
|
||||
Debug.Assert(input.Length >= 128);
|
||||
Debug.Assert(output.Length >= 128);
|
||||
|
||||
var x0 = Load128_UInt32(state.AsSpan());
|
||||
var x1 = Load128_UInt32(state.AsSpan(4));
|
||||
var x2 = Load128_UInt32(state.AsSpan(8));
|
||||
var x3 = Load128_UInt32(state.AsSpan(12));
|
||||
++state[12];
|
||||
|
||||
var v0 = x0;
|
||||
var v1 = x1;
|
||||
var v2 = x2;
|
||||
var v3 = x3;
|
||||
|
||||
for (int i = rounds; i > 0; i -= 2)
|
||||
{
|
||||
v0 = Sse2.Add(v0, v1);
|
||||
v3 = Sse2.Xor(v3, v0);
|
||||
v3 = Sse2.Xor(Sse2.ShiftLeftLogical(v3, 16), Sse2.ShiftRightLogical(v3, 16));
|
||||
v2 = Sse2.Add(v2, v3);
|
||||
v1 = Sse2.Xor(v1, v2);
|
||||
v1 = Sse2.Xor(Sse2.ShiftLeftLogical(v1, 12), Sse2.ShiftRightLogical(v1, 20));
|
||||
v0 = Sse2.Add(v0, v1);
|
||||
v3 = Sse2.Xor(v3, v0);
|
||||
v3 = Sse2.Xor(Sse2.ShiftLeftLogical(v3, 8), Sse2.ShiftRightLogical(v3, 24));
|
||||
v2 = Sse2.Add(v2, v3);
|
||||
v1 = Sse2.Xor(v1, v2);
|
||||
v1 = Sse2.Xor(Sse2.ShiftLeftLogical(v1, 7), Sse2.ShiftRightLogical(v1, 25));
|
||||
|
||||
v1 = Sse2.Shuffle(v1, 0x39);
|
||||
v2 = Sse2.Shuffle(v2, 0x4E);
|
||||
v3 = Sse2.Shuffle(v3, 0x93);
|
||||
|
||||
v0 = Sse2.Add(v0, v1);
|
||||
v3 = Sse2.Xor(v3, v0);
|
||||
v3 = Sse2.Xor(Sse2.ShiftLeftLogical(v3, 16), Sse2.ShiftRightLogical(v3, 16));
|
||||
v2 = Sse2.Add(v2, v3);
|
||||
v1 = Sse2.Xor(v1, v2);
|
||||
v1 = Sse2.Xor(Sse2.ShiftLeftLogical(v1, 12), Sse2.ShiftRightLogical(v1, 20));
|
||||
v0 = Sse2.Add(v0, v1);
|
||||
v3 = Sse2.Xor(v3, v0);
|
||||
v3 = Sse2.Xor(Sse2.ShiftLeftLogical(v3, 8), Sse2.ShiftRightLogical(v3, 24));
|
||||
v2 = Sse2.Add(v2, v3);
|
||||
v1 = Sse2.Xor(v1, v2);
|
||||
v1 = Sse2.Xor(Sse2.ShiftLeftLogical(v1, 7), Sse2.ShiftRightLogical(v1, 25));
|
||||
|
||||
v1 = Sse2.Shuffle(v1, 0x93);
|
||||
v2 = Sse2.Shuffle(v2, 0x4E);
|
||||
v3 = Sse2.Shuffle(v3, 0x39);
|
||||
}
|
||||
|
||||
v0 = Sse2.Add(v0, x0);
|
||||
v1 = Sse2.Add(v1, x1);
|
||||
v2 = Sse2.Add(v2, x2);
|
||||
v3 = Sse2.Add(v3, x3);
|
||||
|
||||
var n0 = Load128_Byte(input);
|
||||
var n1 = Load128_Byte(input[0x10..]);
|
||||
var n2 = Load128_Byte(input[0x20..]);
|
||||
var n3 = Load128_Byte(input[0x30..]);
|
||||
|
||||
n0 = Sse2.Xor(n0, v0.AsByte());
|
||||
n1 = Sse2.Xor(n1, v1.AsByte());
|
||||
n2 = Sse2.Xor(n2, v2.AsByte());
|
||||
n3 = Sse2.Xor(n3, v3.AsByte());
|
||||
|
||||
Store128_Byte(n0, output);
|
||||
Store128_Byte(n1, output[0x10..]);
|
||||
Store128_Byte(n2, output[0x20..]);
|
||||
Store128_Byte(n3, output[0x30..]);
|
||||
|
||||
x3 = Load128_UInt32(state.AsSpan(12));
|
||||
++state[12];
|
||||
|
||||
v0 = x0;
|
||||
v1 = x1;
|
||||
v2 = x2;
|
||||
v3 = x3;
|
||||
|
||||
for (int i = rounds; i > 0; i -= 2)
|
||||
{
|
||||
v0 = Sse2.Add(v0, v1);
|
||||
v3 = Sse2.Xor(v3, v0);
|
||||
v3 = Sse2.Xor(Sse2.ShiftLeftLogical(v3, 16), Sse2.ShiftRightLogical(v3, 16));
|
||||
v2 = Sse2.Add(v2, v3);
|
||||
v1 = Sse2.Xor(v1, v2);
|
||||
v1 = Sse2.Xor(Sse2.ShiftLeftLogical(v1, 12), Sse2.ShiftRightLogical(v1, 20));
|
||||
v0 = Sse2.Add(v0, v1);
|
||||
v3 = Sse2.Xor(v3, v0);
|
||||
v3 = Sse2.Xor(Sse2.ShiftLeftLogical(v3, 8), Sse2.ShiftRightLogical(v3, 24));
|
||||
v2 = Sse2.Add(v2, v3);
|
||||
v1 = Sse2.Xor(v1, v2);
|
||||
v1 = Sse2.Xor(Sse2.ShiftLeftLogical(v1, 7), Sse2.ShiftRightLogical(v1, 25));
|
||||
|
||||
v1 = Sse2.Shuffle(v1, 0x39);
|
||||
v2 = Sse2.Shuffle(v2, 0x4E);
|
||||
v3 = Sse2.Shuffle(v3, 0x93);
|
||||
|
||||
v0 = Sse2.Add(v0, v1);
|
||||
v3 = Sse2.Xor(v3, v0);
|
||||
v3 = Sse2.Xor(Sse2.ShiftLeftLogical(v3, 16), Sse2.ShiftRightLogical(v3, 16));
|
||||
v2 = Sse2.Add(v2, v3);
|
||||
v1 = Sse2.Xor(v1, v2);
|
||||
v1 = Sse2.Xor(Sse2.ShiftLeftLogical(v1, 12), Sse2.ShiftRightLogical(v1, 20));
|
||||
v0 = Sse2.Add(v0, v1);
|
||||
v3 = Sse2.Xor(v3, v0);
|
||||
v3 = Sse2.Xor(Sse2.ShiftLeftLogical(v3, 8), Sse2.ShiftRightLogical(v3, 24));
|
||||
v2 = Sse2.Add(v2, v3);
|
||||
v1 = Sse2.Xor(v1, v2);
|
||||
v1 = Sse2.Xor(Sse2.ShiftLeftLogical(v1, 7), Sse2.ShiftRightLogical(v1, 25));
|
||||
|
||||
v1 = Sse2.Shuffle(v1, 0x93);
|
||||
v2 = Sse2.Shuffle(v2, 0x4E);
|
||||
v3 = Sse2.Shuffle(v3, 0x39);
|
||||
}
|
||||
|
||||
v0 = Sse2.Add(v0, x0);
|
||||
v1 = Sse2.Add(v1, x1);
|
||||
v2 = Sse2.Add(v2, x2);
|
||||
v3 = Sse2.Add(v3, x3);
|
||||
|
||||
n0 = Load128_Byte(input[0x40..]);
|
||||
n1 = Load128_Byte(input[0x50..]);
|
||||
n2 = Load128_Byte(input[0x60..]);
|
||||
n3 = Load128_Byte(input[0x70..]);
|
||||
|
||||
n0 = Sse2.Xor(n0, v0.AsByte());
|
||||
n1 = Sse2.Xor(n1, v1.AsByte());
|
||||
n2 = Sse2.Xor(n2, v2.AsByte());
|
||||
n3 = Sse2.Xor(n3, v3.AsByte());
|
||||
|
||||
Store128_Byte(n0, output[0x40..]);
|
||||
Store128_Byte(n1, output[0x50..]);
|
||||
Store128_Byte(n2, output[0x60..]);
|
||||
Store128_Byte(n3, output[0x70..]);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static Vector128<byte> Load128_Byte(ReadOnlySpan<byte> t)
|
||||
{
|
||||
if (BitConverter.IsLittleEndian && Unsafe.SizeOf<Vector128<byte>>() == 16)
|
||||
return MemoryMarshal.Read<Vector128<byte>>(t);
|
||||
|
||||
return Vector128.Create(
|
||||
BinaryPrimitives.ReadUInt64LittleEndian(t[..8]),
|
||||
BinaryPrimitives.ReadUInt64LittleEndian(t[8..])
|
||||
).AsByte();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static Vector128<uint> Load128_UInt32(ReadOnlySpan<uint> t)
|
||||
{
|
||||
if (BitConverter.IsLittleEndian && Unsafe.SizeOf<Vector128<uint>>() == 16)
|
||||
return MemoryMarshal.Read<Vector128<uint>>(MemoryMarshal.AsBytes(t));
|
||||
|
||||
return Vector128.Create(t[0], t[1], t[2], t[3]);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static Vector256<byte> Load256_Byte(ReadOnlySpan<byte> t)
|
||||
{
|
||||
if (BitConverter.IsLittleEndian && Unsafe.SizeOf<Vector256<byte>>() == 32)
|
||||
return MemoryMarshal.Read<Vector256<byte>>(t);
|
||||
|
||||
return Vector256.Create(
|
||||
BinaryPrimitives.ReadUInt64LittleEndian(t[ 0.. 8]),
|
||||
BinaryPrimitives.ReadUInt64LittleEndian(t[ 8..16]),
|
||||
BinaryPrimitives.ReadUInt64LittleEndian(t[16..24]),
|
||||
BinaryPrimitives.ReadUInt64LittleEndian(t[24..32])
|
||||
).AsByte();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void Store128_Byte(Vector128<byte> s, Span<byte> t)
|
||||
{
|
||||
if (BitConverter.IsLittleEndian && Unsafe.SizeOf<Vector128<byte>>() == 16)
|
||||
{
|
||||
MemoryMarshal.Write(t, ref s);
|
||||
return;
|
||||
}
|
||||
|
||||
var u = s.AsUInt64();
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(t[..8], u.GetElement(0));
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(t[8..], u.GetElement(1));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void Store256_Byte(Vector256<byte> s, Span<byte> t)
|
||||
{
|
||||
if (BitConverter.IsLittleEndian && Unsafe.SizeOf<Vector256<byte>>() == 32)
|
||||
{
|
||||
MemoryMarshal.Write(t, ref s);
|
||||
return;
|
||||
}
|
||||
|
||||
var u = s.AsUInt64();
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(t[ 0.. 8], u.GetElement(0));
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(t[ 8..16], u.GetElement(1));
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(t[16..24], u.GetElement(2));
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(t[24..32], u.GetElement(3));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#pragma warning restore
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 265fa53fa94a97e4fb0c6ab738d53f4b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,484 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR) && BESTHTTP_WITH_BURST
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
using Unity.Burst;
|
||||
|
||||
using Unity.Burst.Intrinsics;
|
||||
|
||||
using static Unity.Burst.Intrinsics.X86;
|
||||
using static Unity.Burst.Intrinsics.Arm;
|
||||
|
||||
// https://github.com/sschoener/burst-simd-exercises/blob/main/Assets/Examples/2-sum-small-numbers-sse3/SumSmallNumbers_SSE3.cs
|
||||
// https://github.com/jratcliff63367/sse2neon/blob/master/SSE2NEON.h#L789
|
||||
|
||||
namespace BestHTTP.Connections.TLS.Crypto.Impl
|
||||
{
|
||||
[BurstCompile]
|
||||
public unsafe static class FastChaCha7539EngineHelper
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void ProcessBlocks2(ReadOnlySpan<byte> input, Span<byte> output, uint[] state, int rounds, byte[] keyStream)
|
||||
{
|
||||
fixed (byte* pinput = input)
|
||||
fixed (byte* poutput = output)
|
||||
fixed (uint* pstate = state)
|
||||
fixed(byte* pkeyStream = keyStream)
|
||||
ProcessBlocks2Impl(pinput, input.Length, poutput, output.Length, pstate, state.Length, rounds, pkeyStream);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[BurstCompile(CompileSynchronously = true)]
|
||||
private static void ProcessBlocks2Impl([NoAlias] byte* input, int inputLen, [NoAlias] byte* output, int outLen, [NoAlias] uint* state, int stateLen, int rounds, [NoAlias] byte* keyStream)
|
||||
{
|
||||
if (Avx2.IsAvx2Supported)
|
||||
{
|
||||
var t0 = new v128(state[0], state[1], state[2], state[3]); //Load128_UInt32(state.AsSpan());
|
||||
var t1 = new v128(state[4], state[5], state[6], state[7]); //Load128_UInt32(state.AsSpan(4));
|
||||
var t2 = new v128(state[8], state[9], state[10], state[11]); //Load128_UInt32(state.AsSpan(8));
|
||||
var t3 = new v128(state[12], state[13], state[14], state[15]); //Load128_UInt32(state.AsSpan(12));
|
||||
++state[12];
|
||||
var t4 = new v128(state[12], state[13], state[14], state[15]); //Load128_UInt32(state.AsSpan(12));
|
||||
++state[12];
|
||||
|
||||
var x0 = new v256(t0, t0); //Vector256.Create(t0, t0);
|
||||
var x1 = new v256(t1, t1); //Vector256.Create(t1, t1);
|
||||
var x2 = new v256(t2, t2); //Vector256.Create(t2, t2);
|
||||
var x3 = new v256(t3, t4); //Vector256.Create(t3, t4);
|
||||
|
||||
var v0 = x0;
|
||||
var v1 = x1;
|
||||
var v2 = x2;
|
||||
var v3 = x3;
|
||||
|
||||
for (int i = rounds; i > 0; i -= 2)
|
||||
{
|
||||
v0 = Avx2.mm256_add_epi32(v0, v1);
|
||||
v3 = Avx2.mm256_xor_si256(v3, v0);
|
||||
v3 = Avx2.mm256_xor_si256(Avx2.mm256_slli_epi32(v3, 16), Avx2.mm256_srli_epi32(v3, 16));
|
||||
v2 = Avx2.mm256_add_epi32(v2, v3);
|
||||
v1 = Avx2.mm256_xor_si256(v1, v2);
|
||||
v1 = Avx2.mm256_xor_si256(Avx2.mm256_slli_epi32(v1, 12), Avx2.mm256_srli_epi32(v1, 20));
|
||||
v0 = Avx2.mm256_add_epi32(v0, v1);
|
||||
v3 = Avx2.mm256_xor_si256(v3, v0);
|
||||
v3 = Avx2.mm256_xor_si256(Avx2.mm256_slli_epi32(v3, 8), Avx2.mm256_srli_epi32(v3, 24));
|
||||
v2 = Avx2.mm256_add_epi32(v2, v3);
|
||||
v1 = Avx2.mm256_xor_si256(v1, v2);
|
||||
v1 = Avx2.mm256_xor_si256(Avx2.mm256_slli_epi32(v1, 7), Avx2.mm256_srli_epi32(v1, 25));
|
||||
|
||||
v1 = Avx2.mm256_shuffle_epi32(v1, 0x39);
|
||||
v2 = Avx2.mm256_shuffle_epi32(v2, 0x4E);
|
||||
v3 = Avx2.mm256_shuffle_epi32(v3, 0x93);
|
||||
|
||||
v0 = Avx2.mm256_add_epi32(v0, v1);
|
||||
v3 = Avx2.mm256_xor_si256(v3, v0);
|
||||
v3 = Avx2.mm256_xor_si256(Avx2.mm256_slli_epi32(v3, 16), Avx2.mm256_srli_epi32(v3, 16));
|
||||
v2 = Avx2.mm256_add_epi32(v2, v3);
|
||||
v1 = Avx2.mm256_xor_si256(v1, v2);
|
||||
v1 = Avx2.mm256_xor_si256(Avx2.mm256_slli_epi32(v1, 12), Avx2.mm256_srli_epi32(v1, 20));
|
||||
v0 = Avx2.mm256_add_epi32(v0, v1);
|
||||
v3 = Avx2.mm256_xor_si256(v3, v0);
|
||||
v3 = Avx2.mm256_xor_si256(Avx2.mm256_slli_epi32(v3, 8), Avx2.mm256_srli_epi32(v3, 24));
|
||||
v2 = Avx2.mm256_add_epi32(v2, v3);
|
||||
v1 = Avx2.mm256_xor_si256(v1, v2);
|
||||
v1 = Avx2.mm256_xor_si256(Avx2.mm256_slli_epi32(v1, 7), Avx2.mm256_srli_epi32(v1, 25));
|
||||
|
||||
v1 = Avx2.mm256_shuffle_epi32(v1, 0x93);
|
||||
v2 = Avx2.mm256_shuffle_epi32(v2, 0x4E);
|
||||
v3 = Avx2.mm256_shuffle_epi32(v3, 0x39);
|
||||
}
|
||||
|
||||
v0 = Avx2.mm256_add_epi32(v0, x0);
|
||||
v1 = Avx2.mm256_add_epi32(v1, x1);
|
||||
v2 = Avx2.mm256_add_epi32(v2, x2);
|
||||
v3 = Avx2.mm256_add_epi32(v3, x3);
|
||||
|
||||
var n0 = Avx2.mm256_permute2x128_si256(v0, v1, 0x20);
|
||||
var n1 = Avx2.mm256_permute2x128_si256(v2, v3, 0x20);
|
||||
var n2 = Avx2.mm256_permute2x128_si256(v0, v1, 0x31);
|
||||
var n3 = Avx2.mm256_permute2x128_si256(v2, v3, 0x31);
|
||||
|
||||
ulong* uInput = (ulong*)input;
|
||||
n0 = Avx2.mm256_xor_si256(n0, new v256(uInput[0], uInput[1], uInput[2], uInput[3])); // Load256_Byte(input)
|
||||
n1 = Avx2.mm256_xor_si256(n1, new v256(uInput[4], uInput[5], uInput[6], uInput[7])); // Load256_Byte(input[0x20..])
|
||||
n2 = Avx2.mm256_xor_si256(n2, new v256(uInput[8], uInput[9], uInput[10], uInput[11])); // Load256_Byte(input[0x40..])
|
||||
n3 = Avx2.mm256_xor_si256(n3, new v256(uInput[12], uInput[13], uInput[14], uInput[15])); // Load256_Byte(input[0x60..])
|
||||
|
||||
ulong* uOutput = (ulong*)output;
|
||||
uOutput[0] = n0.ULong0; uOutput[1] = n0.ULong1; uOutput[2] = n0.ULong2; uOutput[3] = n0.ULong3; //Store256_Byte(n0, output);
|
||||
uOutput[4] = n1.ULong0; uOutput[5] = n1.ULong1; uOutput[6] = n1.ULong2; uOutput[7] = n1.ULong3; //Store256_Byte(n1, output[0x20..]);
|
||||
uOutput[8] = n2.ULong0; uOutput[9] = n2.ULong1; uOutput[10] = n2.ULong2; uOutput[11] = n2.ULong3; //Store256_Byte(n2, output[0x40..]);
|
||||
uOutput[12] = n3.ULong0; uOutput[13] = n3.ULong1; uOutput[14] = n3.ULong2; uOutput[15] = n3.ULong3; //Store256_Byte(n3, output[0x60..]);
|
||||
}
|
||||
else if (Sse2.IsSse2Supported)
|
||||
{
|
||||
var x0 = Sse2.loadu_si128(state); //new v128(state[0], state[1], state[2], state[3]); //Load128_UInt32(state.AsSpan());
|
||||
var x1 = Sse2.loadu_si128(state + 4); //new v128(state[4], state[5], state[6], state[7]); //Load128_UInt32(state.AsSpan(4));
|
||||
var x2 = Sse2.loadu_si128(state + 8); //new v128(state[8], state[9], state[10], state[11]); //Load128_UInt32(state.AsSpan(8));
|
||||
var x3 = Sse2.loadu_si128(state + 12); //new v128(state[12], state[13], state[14], state[15]); //Load128_UInt32(state.AsSpan(12));
|
||||
++state[12];
|
||||
|
||||
var v0 = x0;
|
||||
var v1 = x1;
|
||||
var v2 = x2;
|
||||
var v3 = x3;
|
||||
|
||||
for (int i = rounds; i > 0; i -= 2)
|
||||
{
|
||||
v0 = Sse2.add_epi32(v0, v1);
|
||||
v3 = Sse2.xor_si128(v3, v0);
|
||||
v3 = Sse2.xor_si128(Sse2.slli_epi32(v3, 16), Sse2.srli_epi32(v3, 16));
|
||||
v2 = Sse2.add_epi32(v2, v3);
|
||||
v1 = Sse2.xor_si128(v1, v2);
|
||||
v1 = Sse2.xor_si128(Sse2.slli_epi32(v1, 12), Sse2.srli_epi32(v1, 20));
|
||||
v0 = Sse2.add_epi32(v0, v1);
|
||||
v3 = Sse2.xor_si128(v3, v0);
|
||||
v3 = Sse2.xor_si128(Sse2.slli_epi32(v3, 8), Sse2.srli_epi32(v3, 24));
|
||||
v2 = Sse2.add_epi32(v2, v3);
|
||||
v1 = Sse2.xor_si128(v1, v2);
|
||||
v1 = Sse2.xor_si128(Sse2.slli_epi32(v1, 7), Sse2.srli_epi32(v1, 25));
|
||||
|
||||
v1 = Sse2.shuffle_epi32(v1, 0x39);
|
||||
v2 = Sse2.shuffle_epi32(v2, 0x4E);
|
||||
v3 = Sse2.shuffle_epi32(v3, 0x93);
|
||||
|
||||
v0 = Sse2.add_epi32(v0, v1);
|
||||
v3 = Sse2.xor_si128(v3, v0);
|
||||
v3 = Sse2.xor_si128(Sse2.slli_epi32(v3, 16), Sse2.srli_epi32(v3, 16));
|
||||
v2 = Sse2.add_epi32(v2, v3);
|
||||
v1 = Sse2.xor_si128(v1, v2);
|
||||
v1 = Sse2.xor_si128(Sse2.slli_epi32(v1, 12), Sse2.srli_epi32(v1, 20));
|
||||
v0 = Sse2.add_epi32(v0, v1);
|
||||
v3 = Sse2.xor_si128(v3, v0);
|
||||
v3 = Sse2.xor_si128(Sse2.slli_epi32(v3, 8), Sse2.srli_epi32(v3, 24));
|
||||
v2 = Sse2.add_epi32(v2, v3);
|
||||
v1 = Sse2.xor_si128(v1, v2);
|
||||
v1 = Sse2.xor_si128(Sse2.slli_epi32(v1, 7), Sse2.srli_epi32(v1, 25));
|
||||
|
||||
v1 = Sse2.shuffle_epi32(v1, 0x93);
|
||||
v2 = Sse2.shuffle_epi32(v2, 0x4E);
|
||||
v3 = Sse2.shuffle_epi32(v3, 0x39);
|
||||
}
|
||||
|
||||
v0 = Sse2.add_epi32(v0, x0);
|
||||
v1 = Sse2.add_epi32(v1, x1);
|
||||
v2 = Sse2.add_epi32(v2, x2);
|
||||
v3 = Sse2.add_epi32(v3, x3);
|
||||
|
||||
var n0 = Sse2.loadu_si128(input + 0x00); //Load128_Byte(input);
|
||||
var n1 = Sse2.loadu_si128(input + 0x10); //Load128_Byte(input[0x10..]);
|
||||
var n2 = Sse2.loadu_si128(input + 0x20); //Load128_Byte(input[0x20..]);
|
||||
var n3 = Sse2.loadu_si128(input + 0x30); //Load128_Byte(input[0x30..]);
|
||||
|
||||
n0 = Sse2.xor_si128(n0, v0);
|
||||
n1 = Sse2.xor_si128(n1, v1);
|
||||
n2 = Sse2.xor_si128(n2, v2);
|
||||
n3 = Sse2.xor_si128(n3, v3);
|
||||
|
||||
Sse2.storeu_si128(output + 0x00, n0); //Store128_Byte(n0, output);
|
||||
Sse2.storeu_si128(output + 0x10, n1); //Store128_Byte(n1, output[0x10..]);
|
||||
Sse2.storeu_si128(output + 0x20, n2); //Store128_Byte(n2, output[0x20..]);
|
||||
Sse2.storeu_si128(output + 0x30, n3); //Store128_Byte(n3, output[0x30..]);
|
||||
|
||||
|
||||
x3 = Sse2.loadu_si128(state + 12); // Load128_UInt32(state.AsSpan(12));
|
||||
++state[12];
|
||||
|
||||
v0 = x0;
|
||||
v1 = x1;
|
||||
v2 = x2;
|
||||
v3 = x3;
|
||||
|
||||
for (int i = rounds; i > 0; i -= 2)
|
||||
{
|
||||
v0 = Sse2.add_epi32(v0, v1);
|
||||
v3 = Sse2.xor_si128(v3, v0);
|
||||
v3 = Sse2.xor_si128(Sse2.slli_epi32(v3, 16), Sse2.srli_epi32(v3, 16));
|
||||
v2 = Sse2.add_epi32(v2, v3);
|
||||
v1 = Sse2.xor_si128(v1, v2);
|
||||
v1 = Sse2.xor_si128(Sse2.slli_epi32(v1, 12), Sse2.srli_epi32(v1, 20));
|
||||
v0 = Sse2.add_epi32(v0, v1);
|
||||
v3 = Sse2.xor_si128(v3, v0);
|
||||
v3 = Sse2.xor_si128(Sse2.slli_epi32(v3, 8), Sse2.srli_epi32(v3, 24));
|
||||
v2 = Sse2.add_epi32(v2, v3);
|
||||
v1 = Sse2.xor_si128(v1, v2);
|
||||
v1 = Sse2.xor_si128(Sse2.slli_epi32(v1, 7), Sse2.srli_epi32(v1, 25));
|
||||
|
||||
v1 = Sse2.shuffle_epi32(v1, 0x39);
|
||||
v2 = Sse2.shuffle_epi32(v2, 0x4E);
|
||||
v3 = Sse2.shuffle_epi32(v3, 0x93);
|
||||
|
||||
v0 = Sse2.add_epi32(v0, v1);
|
||||
v3 = Sse2.xor_si128(v3, v0);
|
||||
v3 = Sse2.xor_si128(Sse2.slli_epi32(v3, 16), Sse2.srli_epi32(v3, 16));
|
||||
v2 = Sse2.add_epi32(v2, v3);
|
||||
v1 = Sse2.xor_si128(v1, v2);
|
||||
v1 = Sse2.xor_si128(Sse2.slli_epi32(v1, 12), Sse2.srli_epi32(v1, 20));
|
||||
v0 = Sse2.add_epi32(v0, v1);
|
||||
v3 = Sse2.xor_si128(v3, v0);
|
||||
v3 = Sse2.xor_si128(Sse2.slli_epi32(v3, 8), Sse2.srli_epi32(v3, 24));
|
||||
v2 = Sse2.add_epi32(v2, v3);
|
||||
v1 = Sse2.xor_si128(v1, v2);
|
||||
v1 = Sse2.xor_si128(Sse2.slli_epi32(v1, 7), Sse2.srli_epi32(v1, 25));
|
||||
|
||||
v1 = Sse2.shuffle_epi32(v1, 0x93);
|
||||
v2 = Sse2.shuffle_epi32(v2, 0x4E);
|
||||
v3 = Sse2.shuffle_epi32(v3, 0x39);
|
||||
}
|
||||
|
||||
v0 = Sse2.add_epi32(v0, x0);
|
||||
v1 = Sse2.add_epi32(v1, x1);
|
||||
v2 = Sse2.add_epi32(v2, x2);
|
||||
v3 = Sse2.add_epi32(v3, x3);
|
||||
|
||||
n0 = Sse2.loadu_si128(input + 0x40); //Load128_Byte(input[0x40..]);
|
||||
n1 = Sse2.loadu_si128(input + 0x50); //Load128_Byte(input[0x50..]);
|
||||
n2 = Sse2.loadu_si128(input + 0x60); //Load128_Byte(input[0x60..]);
|
||||
n3 = Sse2.loadu_si128(input + 0x70); //Load128_Byte(input[0x70..]);
|
||||
|
||||
n0 = Sse2.xor_si128(n0, v0);
|
||||
n1 = Sse2.xor_si128(n1, v1);
|
||||
n2 = Sse2.xor_si128(n2, v2);
|
||||
n3 = Sse2.xor_si128(n3, v3);
|
||||
|
||||
Sse2.storeu_si128(output + 0x40, n0); //Store128_Byte(n0, output[0x40..]);
|
||||
Sse2.storeu_si128(output + 0x50, n1); //Store128_Byte(n1, output[0x50..]);
|
||||
Sse2.storeu_si128(output + 0x60, n2); //Store128_Byte(n2, output[0x60..]);
|
||||
Sse2.storeu_si128(output + 0x70, n3); //Store128_Byte(n3, output[0x70..]);
|
||||
}
|
||||
else if (Neon.IsNeonSupported)
|
||||
{
|
||||
var x0 = Neon.vld1q_u32(state); //new v128(state[0], state[1], state[2], state[3]); //Load128_UInt32(state.AsSpan());
|
||||
var x1 = Neon.vld1q_u32(state + 4); //new v128(state[4], state[5], state[6], state[7]); //Load128_UInt32(state.AsSpan(4));
|
||||
var x2 = Neon.vld1q_u32(state + 8); //new v128(state[8], state[9], state[10], state[11]); //Load128_UInt32(state.AsSpan(8));
|
||||
var x3 = Neon.vld1q_u32(state + 12);
|
||||
++state[12];
|
||||
|
||||
var v0 = x0;
|
||||
var v1 = x1;
|
||||
var v2 = x2;
|
||||
var v3 = x3;
|
||||
|
||||
for (int i = rounds; i > 0; i -= 2)
|
||||
{
|
||||
v0 = Neon.vaddq_u32(v0, v1);
|
||||
v3 = Neon.veorq_u32(v3, v0);
|
||||
v3 = Neon.veorq_u32(Neon.vshlq_n_u32(v3, 16), Neon.vshrq_n_u32(v3, 16));
|
||||
v2 = Neon.vaddq_u32(v2, v3);
|
||||
v1 = Neon.veorq_u32(v1, v2);
|
||||
v1 = Neon.veorq_u32(Neon.vshlq_n_u32(v1, 12), Neon.vshrq_n_u32(v1, 20));
|
||||
v0 = Neon.vaddq_u32(v0, v1);
|
||||
v3 = Neon.veorq_u32(v3, v0);
|
||||
v3 = Neon.veorq_u32(Neon.vshlq_n_u32(v3, 8), Neon.vshrq_n_u32(v3, 24));
|
||||
v2 = Neon.vaddq_u32(v2, v3);
|
||||
v1 = Neon.veorq_u32(v1, v2);
|
||||
v1 = Neon.veorq_u32(Neon.vshlq_n_u32(v1, 7), Neon.vshrq_n_u32(v1, 25));
|
||||
|
||||
///*v1 = */Neon_shuffle_epi32(v1, 0x39, out v1);
|
||||
v128 ret;
|
||||
ret = Neon.vmovq_n_u32(Neon.vgetq_lane_u32(v1, (0x39) & 0x3));
|
||||
ret = Neon.vsetq_lane_u32(Neon.vgetq_lane_u32(v1, ((0x39) >> 2) & 0x3), ret, 1);
|
||||
ret = Neon.vsetq_lane_u32(Neon.vgetq_lane_u32(v1, ((0x39) >> 4) & 0x3), ret, 2);
|
||||
v1 = Neon.vsetq_lane_u32(Neon.vgetq_lane_u32(v1, ((0x39) >> 6) & 0x3), ret, 3);
|
||||
|
||||
///*v2 = */Neon_shuffle_epi32(v2, 0x4E, out v2);
|
||||
ret = Neon.vmovq_n_u32(Neon.vgetq_lane_u32(v2, (0x4E) & 0x3));
|
||||
ret = Neon.vsetq_lane_u32(Neon.vgetq_lane_u32(v2, ((0x4E) >> 2) & 0x3), ret, 1);
|
||||
ret = Neon.vsetq_lane_u32(Neon.vgetq_lane_u32(v2, ((0x4E) >> 4) & 0x3), ret, 2);
|
||||
v2 = Neon.vsetq_lane_u32(Neon.vgetq_lane_u32(v2, ((0x4E) >> 6) & 0x3), ret, 3);
|
||||
|
||||
///*v3 = */Neon_shuffle_epi32(v3, 0x93, out v3);
|
||||
ret = Neon.vmovq_n_u32(Neon.vgetq_lane_u32(v3, (0x93) & 0x3));
|
||||
ret = Neon.vsetq_lane_u32(Neon.vgetq_lane_u32(v3, ((0x93) >> 2) & 0x3), ret, 1);
|
||||
ret = Neon.vsetq_lane_u32(Neon.vgetq_lane_u32(v3, ((0x93) >> 4) & 0x3), ret, 2);
|
||||
v3 = Neon.vsetq_lane_u32(Neon.vgetq_lane_u32(v3, ((0x93) >> 6) & 0x3), ret, 3);
|
||||
|
||||
v0 = Neon.vaddq_u32(v0, v1);
|
||||
v3 = Neon.veorq_u32(v3, v0);
|
||||
v3 = Neon.veorq_u32(Neon.vshlq_n_u32(v3, 16), Neon.vshrq_n_u32(v3, 16));
|
||||
v2 = Neon.vaddq_u32(v2, v3);
|
||||
v1 = Neon.veorq_u32(v1, v2);
|
||||
v1 = Neon.veorq_u32(Neon.vshlq_n_u32(v1, 12), Neon.vshrq_n_u32(v1, 20));
|
||||
v0 = Neon.vaddq_u32(v0, v1);
|
||||
v3 = Neon.veorq_u32(v3, v0);
|
||||
v3 = Neon.veorq_u32(Neon.vshlq_n_u32(v3, 8), Neon.vshrq_n_u32(v3, 24));
|
||||
v2 = Neon.vaddq_u32(v2, v3);
|
||||
v1 = Neon.veorq_u32(v1, v2);
|
||||
v1 = Neon.veorq_u32(Neon.vshlq_n_u32(v1, 7), Neon.vshrq_n_u32(v1, 25));
|
||||
|
||||
///*v1 = */Neon_shuffle_epi32(v1, 0x93, out v1);
|
||||
ret = Neon.vmovq_n_u32(Neon.vgetq_lane_u32(v1, (0x93) & 0x3));
|
||||
ret = Neon.vsetq_lane_u32(Neon.vgetq_lane_u32(v1, ((0x93) >> 2) & 0x3), ret, 1);
|
||||
ret = Neon.vsetq_lane_u32(Neon.vgetq_lane_u32(v1, ((0x93) >> 4) & 0x3), ret, 2);
|
||||
v1 = Neon.vsetq_lane_u32(Neon.vgetq_lane_u32(v1, ((0x93) >> 6) & 0x3), ret, 3);
|
||||
|
||||
///*v2 = */Neon_shuffle_epi32(v2, 0x4E, out v2);
|
||||
ret = Neon.vmovq_n_u32(Neon.vgetq_lane_u32(v2, (0x4E) & 0x3));
|
||||
ret = Neon.vsetq_lane_u32(Neon.vgetq_lane_u32(v2, ((0x4E) >> 2) & 0x3), ret, 1);
|
||||
ret = Neon.vsetq_lane_u32(Neon.vgetq_lane_u32(v2, ((0x4E) >> 4) & 0x3), ret, 2);
|
||||
v2 = Neon.vsetq_lane_u32(Neon.vgetq_lane_u32(v2, ((0x4E) >> 6) & 0x3), ret, 3);
|
||||
|
||||
///*v3 = */Neon_shuffle_epi32(v3, 0x39, out v3);
|
||||
ret = Neon.vmovq_n_u32(Neon.vgetq_lane_u32(v3, (0x39) & 0x3));
|
||||
ret = Neon.vsetq_lane_u32(Neon.vgetq_lane_u32(v3, ((0x39) >> 2) & 0x3), ret, 1);
|
||||
ret = Neon.vsetq_lane_u32(Neon.vgetq_lane_u32(v3, ((0x39) >> 4) & 0x3), ret, 2);
|
||||
v3 = Neon.vsetq_lane_u32(Neon.vgetq_lane_u32(v3, ((0x39) >> 6) & 0x3), ret, 3);
|
||||
}
|
||||
|
||||
v0 = Neon.vaddq_u32(v0, x0);
|
||||
v1 = Neon.vaddq_u32(v1, x1);
|
||||
v2 = Neon.vaddq_u32(v2, x2);
|
||||
v3 = Neon.vaddq_u32(v3, x3);
|
||||
|
||||
var n0 = Neon.vld1q_u32((uint*)(input + 0x00)); //Load128_Byte(input);
|
||||
var n1 = Neon.vld1q_u32((uint*)(input + 0x10)); //Load128_Byte(input[0x10..]);
|
||||
var n2 = Neon.vld1q_u32((uint*)(input + 0x20)); //Load128_Byte(input[0x20..]);
|
||||
var n3 = Neon.vld1q_u32((uint*)(input + 0x30)); //Load128_Byte(input[0x30..]);
|
||||
|
||||
n0 = Neon.veorq_u32(n0, v0);
|
||||
n1 = Neon.veorq_u32(n1, v1);
|
||||
n2 = Neon.veorq_u32(n2, v2);
|
||||
n3 = Neon.veorq_u32(n3, v3);
|
||||
|
||||
Neon.vst1q_u32((uint*)(output + 0x00), n0); //Store128_Byte(n0, output);
|
||||
Neon.vst1q_u32((uint*)(output + 0x10), n1); //Store128_Byte(n1, output[0x10..]);
|
||||
Neon.vst1q_u32((uint*)(output + 0x20), n2); //Store128_Byte(n2, output[0x20..]);
|
||||
Neon.vst1q_u32((uint*)(output + 0x30), n3); //Store128_Byte(n3, output[0x30..]);
|
||||
|
||||
|
||||
x3 = Neon.vld1q_u32(state + 12); // Load128_UInt32(state.AsSpan(12));
|
||||
++state[12];
|
||||
|
||||
v0 = x0;
|
||||
v1 = x1;
|
||||
v2 = x2;
|
||||
v3 = x3;
|
||||
|
||||
for (int i = rounds; i > 0; i -= 2)
|
||||
{
|
||||
v0 = Neon.vaddq_u32(v0, v1);
|
||||
v3 = Neon.veorq_u32(v3, v0);
|
||||
v3 = Neon.veorq_u32(Neon.vshlq_n_u32(v3, 16), Neon.vshrq_n_u32(v3, 16));
|
||||
v2 = Neon.vaddq_u32(v2, v3);
|
||||
v1 = Neon.veorq_u32(v1, v2);
|
||||
v1 = Neon.veorq_u32(Neon.vshlq_n_u32(v1, 12), Neon.vshrq_n_u32(v1, 20));
|
||||
v0 = Neon.vaddq_u32(v0, v1);
|
||||
v3 = Neon.veorq_u32(v3, v0);
|
||||
v3 = Neon.veorq_u32(Neon.vshlq_n_u32(v3, 8), Neon.vshrq_n_u32(v3, 24));
|
||||
v2 = Neon.vaddq_u32(v2, v3);
|
||||
v1 = Neon.veorq_u32(v1, v2);
|
||||
v1 = Neon.veorq_u32(Neon.vshlq_n_u32(v1, 7), Neon.vshrq_n_u32(v1, 25));
|
||||
|
||||
///*v1 = */Neon_shuffle_epi32(v1, 0x39, out v1);
|
||||
v128 ret;
|
||||
ret = Neon.vmovq_n_u32(Neon.vgetq_lane_u32(v1, (0x39) & 0x3));
|
||||
ret = Neon.vsetq_lane_u32(Neon.vgetq_lane_u32(v1, ((0x39) >> 2) & 0x3), ret, 1);
|
||||
ret = Neon.vsetq_lane_u32(Neon.vgetq_lane_u32(v1, ((0x39) >> 4) & 0x3), ret, 2);
|
||||
v1 = Neon.vsetq_lane_u32(Neon.vgetq_lane_u32(v1, ((0x39) >> 6) & 0x3), ret, 3);
|
||||
|
||||
///*v2 = */Neon_shuffle_epi32(v2, 0x4E, out v2);
|
||||
ret = Neon.vmovq_n_u32(Neon.vgetq_lane_u32(v2, (0x4E) & 0x3));
|
||||
ret = Neon.vsetq_lane_u32(Neon.vgetq_lane_u32(v2, ((0x4E) >> 2) & 0x3), ret, 1);
|
||||
ret = Neon.vsetq_lane_u32(Neon.vgetq_lane_u32(v2, ((0x4E) >> 4) & 0x3), ret, 2);
|
||||
v2 = Neon.vsetq_lane_u32(Neon.vgetq_lane_u32(v2, ((0x4E) >> 6) & 0x3), ret, 3);
|
||||
|
||||
///*v3 = */Neon_shuffle_epi32(v3, 0x93, out v3);
|
||||
ret = Neon.vmovq_n_u32(Neon.vgetq_lane_u32(v3, (0x93) & 0x3));
|
||||
ret = Neon.vsetq_lane_u32(Neon.vgetq_lane_u32(v3, ((0x93) >> 2) & 0x3), ret, 1);
|
||||
ret = Neon.vsetq_lane_u32(Neon.vgetq_lane_u32(v3, ((0x93) >> 4) & 0x3), ret, 2);
|
||||
v3 = Neon.vsetq_lane_u32(Neon.vgetq_lane_u32(v3, ((0x93) >> 6) & 0x3), ret, 3);
|
||||
|
||||
v0 = Neon.vaddq_u32(v0, v1);
|
||||
v3 = Neon.veorq_u32(v3, v0);
|
||||
v3 = Neon.veorq_u32(Neon.vshlq_n_u32(v3, 16), Neon.vshrq_n_u32(v3, 16));
|
||||
v2 = Neon.vaddq_u32(v2, v3);
|
||||
v1 = Neon.veorq_u32(v1, v2);
|
||||
v1 = Neon.veorq_u32(Neon.vshlq_n_u32(v1, 12), Neon.vshrq_n_u32(v1, 20));
|
||||
v0 = Neon.vaddq_u32(v0, v1);
|
||||
v3 = Neon.veorq_u32(v3, v0);
|
||||
v3 = Neon.veorq_u32(Neon.vshlq_n_u32(v3, 8), Neon.vshrq_n_u32(v3, 24));
|
||||
v2 = Neon.vaddq_u32(v2, v3);
|
||||
v1 = Neon.veorq_u32(v1, v2);
|
||||
v1 = Neon.veorq_u32(Neon.vshlq_n_u32(v1, 7), Neon.vshrq_n_u32(v1, 25));
|
||||
|
||||
///*v1 = */Neon_shuffle_epi32(v1, 0x93, out v1);
|
||||
ret = Neon.vmovq_n_u32(Neon.vgetq_lane_u32(v1, (0x93) & 0x3));
|
||||
ret = Neon.vsetq_lane_u32(Neon.vgetq_lane_u32(v1, ((0x93) >> 2) & 0x3), ret, 1);
|
||||
ret = Neon.vsetq_lane_u32(Neon.vgetq_lane_u32(v1, ((0x93) >> 4) & 0x3), ret, 2);
|
||||
v1 = Neon.vsetq_lane_u32(Neon.vgetq_lane_u32(v1, ((0x93) >> 6) & 0x3), ret, 3);
|
||||
|
||||
///*v2 = */Neon_shuffle_epi32(v2, 0x4E, out v2);
|
||||
ret = Neon.vmovq_n_u32(Neon.vgetq_lane_u32(v2, (0x4E) & 0x3));
|
||||
ret = Neon.vsetq_lane_u32(Neon.vgetq_lane_u32(v2, ((0x4E) >> 2) & 0x3), ret, 1);
|
||||
ret = Neon.vsetq_lane_u32(Neon.vgetq_lane_u32(v2, ((0x4E) >> 4) & 0x3), ret, 2);
|
||||
v2 = Neon.vsetq_lane_u32(Neon.vgetq_lane_u32(v2, ((0x4E) >> 6) & 0x3), ret, 3);
|
||||
|
||||
///*v3 = */Neon_shuffle_epi32(v3, 0x39, out v3);
|
||||
ret = Neon.vmovq_n_u32(Neon.vgetq_lane_u32(v3, (0x39) & 0x3));
|
||||
ret = Neon.vsetq_lane_u32(Neon.vgetq_lane_u32(v3, ((0x39) >> 2) & 0x3), ret, 1);
|
||||
ret = Neon.vsetq_lane_u32(Neon.vgetq_lane_u32(v3, ((0x39) >> 4) & 0x3), ret, 2);
|
||||
v3 = Neon.vsetq_lane_u32(Neon.vgetq_lane_u32(v3, ((0x39) >> 6) & 0x3), ret, 3);
|
||||
}
|
||||
|
||||
v0 = Neon.vaddq_u32(v0, x0);
|
||||
v1 = Neon.vaddq_u32(v1, x1);
|
||||
v2 = Neon.vaddq_u32(v2, x2);
|
||||
v3 = Neon.vaddq_u32(v3, x3);
|
||||
|
||||
n0 = Neon.vld1q_u32((uint*)(input + 0x40)); //Load128_Byte(input[0x40..]);
|
||||
n1 = Neon.vld1q_u32((uint*)(input + 0x50)); //Load128_Byte(input[0x50..]);
|
||||
n2 = Neon.vld1q_u32((uint*)(input + 0x60)); //Load128_Byte(input[0x60..]);
|
||||
n3 = Neon.vld1q_u32((uint*)(input + 0x70)); //Load128_Byte(input[0x70..]);
|
||||
|
||||
n0 = Neon.veorq_u32(n0, v0);
|
||||
n1 = Neon.veorq_u32(n1, v1);
|
||||
n2 = Neon.veorq_u32(n2, v2);
|
||||
n3 = Neon.veorq_u32(n3, v3);
|
||||
|
||||
Neon.vst1q_u32((uint*)(output + 0x40), n0); //Store128_Byte(n0, output[0x40..]);
|
||||
Neon.vst1q_u32((uint*)(output + 0x50), n1); //Store128_Byte(n1, output[0x50..]);
|
||||
Neon.vst1q_u32((uint*)(output + 0x60), n2); //Store128_Byte(n2, output[0x60..]);
|
||||
Neon.vst1q_u32((uint*)(output + 0x70), n3); //Store128_Byte(n3, output[0x70..]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Inlined to two ImplProcessBlock calls:
|
||||
//ImplProcessBlock(input, output);
|
||||
//ImplProcessBlock(input[64..], output[64..]);
|
||||
|
||||
FastChaChaEngineHelper.ChachaCoreImpl(rounds, state, keyStream);
|
||||
++state[12];
|
||||
ulong* pulinput = (ulong*)input;
|
||||
ulong* puloutput = (ulong*)output;
|
||||
ulong* pulkeyStream = (ulong*)keyStream;
|
||||
|
||||
puloutput[0] = pulkeyStream[0] ^ pulinput[0];
|
||||
puloutput[1] = pulkeyStream[1] ^ pulinput[1];
|
||||
puloutput[2] = pulkeyStream[2] ^ pulinput[2];
|
||||
puloutput[3] = pulkeyStream[3] ^ pulinput[3];
|
||||
|
||||
puloutput[4] = pulkeyStream[4] ^ pulinput[4];
|
||||
puloutput[5] = pulkeyStream[5] ^ pulinput[5];
|
||||
puloutput[6] = pulkeyStream[6] ^ pulinput[6];
|
||||
puloutput[7] = pulkeyStream[7] ^ pulinput[7];
|
||||
|
||||
FastChaChaEngineHelper.ChachaCoreImpl(rounds, state, keyStream);
|
||||
++state[12];
|
||||
|
||||
pulinput = (ulong*)&input[64];
|
||||
puloutput = (ulong*)&output[64];
|
||||
|
||||
puloutput[0] = pulkeyStream[0] ^ pulinput[0];
|
||||
puloutput[1] = pulkeyStream[1] ^ pulinput[1];
|
||||
puloutput[2] = pulkeyStream[2] ^ pulinput[2];
|
||||
puloutput[3] = pulkeyStream[3] ^ pulinput[3];
|
||||
|
||||
puloutput[4] = pulkeyStream[4] ^ pulinput[4];
|
||||
puloutput[5] = pulkeyStream[5] ^ pulinput[5];
|
||||
puloutput[6] = pulkeyStream[6] ^ pulinput[6];
|
||||
puloutput[7] = pulkeyStream[7] ^ pulinput[7];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f659fd4b1e907e64c82229d4a0b83341
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,249 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
#pragma warning disable
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
using System.Buffers.Binary;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Intrinsics;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
#endif
|
||||
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Utilities;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities;
|
||||
|
||||
namespace BestHTTP.Connections.TLS.Crypto.Impl
|
||||
{
|
||||
/// <summary>
|
||||
/// Implementation of Daniel J. Bernstein's ChaCha stream cipher.
|
||||
/// </summary>
|
||||
|
||||
|
||||
|
||||
[BestHTTP.PlatformSupport.IL2CPP.Il2CppEagerStaticClassConstructionAttribute]
|
||||
public sealed class FastChaChaEngine
|
||||
: FastSalsa20Engine
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a 20 rounds ChaCha engine.
|
||||
/// </summary>
|
||||
public FastChaChaEngine()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a ChaCha engine with a specific number of rounds.
|
||||
/// </summary>
|
||||
/// <param name="rounds">the number of rounds (must be an even number).</param>
|
||||
public FastChaChaEngine(int rounds)
|
||||
: base(rounds)
|
||||
{
|
||||
}
|
||||
|
||||
public override string AlgorithmName
|
||||
{
|
||||
get { return "ChaCha" + rounds; }
|
||||
}
|
||||
|
||||
protected override void AdvanceCounter()
|
||||
{
|
||||
if (++engineState[12] == 0)
|
||||
{
|
||||
++engineState[13];
|
||||
}
|
||||
}
|
||||
|
||||
protected override void ResetCounter()
|
||||
{
|
||||
engineState[12] = engineState[13] = 0;
|
||||
}
|
||||
|
||||
protected override void SetKey(byte[] keyBytes, byte[] ivBytes)
|
||||
{
|
||||
if (keyBytes != null)
|
||||
{
|
||||
if ((keyBytes.Length != 16) && (keyBytes.Length != 32))
|
||||
throw new ArgumentException(AlgorithmName + " requires 128 bit or 256 bit key");
|
||||
|
||||
PackTauOrSigma(keyBytes.Length, engineState, 0);
|
||||
|
||||
// Key
|
||||
Pack.LE_To_UInt32(keyBytes, 0, engineState, 4, 4);
|
||||
Pack.LE_To_UInt32(keyBytes, keyBytes.Length - 16, engineState, 8, 4);
|
||||
}
|
||||
|
||||
// IV
|
||||
Pack.LE_To_UInt32(ivBytes, 0, engineState, 14, 2);
|
||||
}
|
||||
|
||||
protected override void GenerateKeyStream(byte[] output)
|
||||
{
|
||||
//ChachaCore(rounds, engineState, output);
|
||||
FastChaChaEngineHelper.ChachaCore(rounds, engineState, output);
|
||||
}
|
||||
|
||||
internal static void ChachaCore(int rounds, uint[] input, byte[] output)
|
||||
{
|
||||
Debug.Assert(rounds % 2 == 0);
|
||||
Debug.Assert(input.Length >= 16);
|
||||
Debug.Assert(output.Length >= 64);
|
||||
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
if (Sse2.IsSupported)
|
||||
{
|
||||
var x0 = Load128_UInt32(input.AsSpan());
|
||||
var x1 = Load128_UInt32(input.AsSpan(4));
|
||||
var x2 = Load128_UInt32(input.AsSpan(8));
|
||||
var x3 = Load128_UInt32(input.AsSpan(12));
|
||||
|
||||
var v0 = x0;
|
||||
var v1 = x1;
|
||||
var v2 = x2;
|
||||
var v3 = x3;
|
||||
|
||||
for (int i = rounds; i > 0; i -= 2)
|
||||
{
|
||||
v0 = Sse2.Add(v0, v1);
|
||||
v3 = Sse2.Xor(v3, v0);
|
||||
v3 = Sse2.Xor(Sse2.ShiftLeftLogical(v3, 16), Sse2.ShiftRightLogical(v3, 16));
|
||||
v2 = Sse2.Add(v2, v3);
|
||||
v1 = Sse2.Xor(v1, v2);
|
||||
v1 = Sse2.Xor(Sse2.ShiftLeftLogical(v1, 12), Sse2.ShiftRightLogical(v1, 20));
|
||||
v0 = Sse2.Add(v0, v1);
|
||||
v3 = Sse2.Xor(v3, v0);
|
||||
v3 = Sse2.Xor(Sse2.ShiftLeftLogical(v3, 8), Sse2.ShiftRightLogical(v3, 24));
|
||||
v2 = Sse2.Add(v2, v3);
|
||||
v1 = Sse2.Xor(v1, v2);
|
||||
v1 = Sse2.Xor(Sse2.ShiftLeftLogical(v1, 7), Sse2.ShiftRightLogical(v1, 25));
|
||||
|
||||
v1 = Sse2.Shuffle(v1, 0x39);
|
||||
v2 = Sse2.Shuffle(v2, 0x4E);
|
||||
v3 = Sse2.Shuffle(v3, 0x93);
|
||||
|
||||
v0 = Sse2.Add(v0, v1);
|
||||
v3 = Sse2.Xor(v3, v0);
|
||||
v3 = Sse2.Xor(Sse2.ShiftLeftLogical(v3, 16), Sse2.ShiftRightLogical(v3, 16));
|
||||
v2 = Sse2.Add(v2, v3);
|
||||
v1 = Sse2.Xor(v1, v2);
|
||||
v1 = Sse2.Xor(Sse2.ShiftLeftLogical(v1, 12), Sse2.ShiftRightLogical(v1, 20));
|
||||
v0 = Sse2.Add(v0, v1);
|
||||
v3 = Sse2.Xor(v3, v0);
|
||||
v3 = Sse2.Xor(Sse2.ShiftLeftLogical(v3, 8), Sse2.ShiftRightLogical(v3, 24));
|
||||
v2 = Sse2.Add(v2, v3);
|
||||
v1 = Sse2.Xor(v1, v2);
|
||||
v1 = Sse2.Xor(Sse2.ShiftLeftLogical(v1, 7), Sse2.ShiftRightLogical(v1, 25));
|
||||
|
||||
v1 = Sse2.Shuffle(v1, 0x93);
|
||||
v2 = Sse2.Shuffle(v2, 0x4E);
|
||||
v3 = Sse2.Shuffle(v3, 0x39);
|
||||
}
|
||||
|
||||
v0 = Sse2.Add(v0, x0);
|
||||
v1 = Sse2.Add(v1, x1);
|
||||
v2 = Sse2.Add(v2, x2);
|
||||
v3 = Sse2.Add(v3, x3);
|
||||
|
||||
Store128_UInt32(v0, output.AsSpan());
|
||||
Store128_UInt32(v1, output.AsSpan(0x10));
|
||||
Store128_UInt32(v2, output.AsSpan(0x20));
|
||||
Store128_UInt32(v3, output.AsSpan(0x30));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
{
|
||||
uint x00 = input[0], x01 = input[1], x02 = input[2], x03 = input[3];
|
||||
uint x04 = input[4], x05 = input[5], x06 = input[6], x07 = input[7];
|
||||
uint x08 = input[8], x09 = input[9], x10 = input[10], x11 = input[11];
|
||||
uint x12 = input[12], x13 = input[13], x14 = input[14], x15 = input[15];
|
||||
|
||||
for (int i = rounds; i > 0; i -= 2)
|
||||
{
|
||||
x00 += x04; x12 = Integers.RotateLeft(x12 ^ x00, 16);
|
||||
x01 += x05; x13 = Integers.RotateLeft(x13 ^ x01, 16);
|
||||
x02 += x06; x14 = Integers.RotateLeft(x14 ^ x02, 16);
|
||||
x03 += x07; x15 = Integers.RotateLeft(x15 ^ x03, 16);
|
||||
|
||||
x08 += x12; x04 = Integers.RotateLeft(x04 ^ x08, 12);
|
||||
x09 += x13; x05 = Integers.RotateLeft(x05 ^ x09, 12);
|
||||
x10 += x14; x06 = Integers.RotateLeft(x06 ^ x10, 12);
|
||||
x11 += x15; x07 = Integers.RotateLeft(x07 ^ x11, 12);
|
||||
|
||||
x00 += x04; x12 = Integers.RotateLeft(x12 ^ x00, 8);
|
||||
x01 += x05; x13 = Integers.RotateLeft(x13 ^ x01, 8);
|
||||
x02 += x06; x14 = Integers.RotateLeft(x14 ^ x02, 8);
|
||||
x03 += x07; x15 = Integers.RotateLeft(x15 ^ x03, 8);
|
||||
|
||||
x08 += x12; x04 = Integers.RotateLeft(x04 ^ x08, 7);
|
||||
x09 += x13; x05 = Integers.RotateLeft(x05 ^ x09, 7);
|
||||
x10 += x14; x06 = Integers.RotateLeft(x06 ^ x10, 7);
|
||||
x11 += x15; x07 = Integers.RotateLeft(x07 ^ x11, 7);
|
||||
x00 += x05; x15 = Integers.RotateLeft(x15 ^ x00, 16);
|
||||
x01 += x06; x12 = Integers.RotateLeft(x12 ^ x01, 16);
|
||||
x02 += x07; x13 = Integers.RotateLeft(x13 ^ x02, 16);
|
||||
x03 += x04; x14 = Integers.RotateLeft(x14 ^ x03, 16);
|
||||
|
||||
x10 += x15; x05 = Integers.RotateLeft(x05 ^ x10, 12);
|
||||
x11 += x12; x06 = Integers.RotateLeft(x06 ^ x11, 12);
|
||||
x08 += x13; x07 = Integers.RotateLeft(x07 ^ x08, 12);
|
||||
x09 += x14; x04 = Integers.RotateLeft(x04 ^ x09, 12);
|
||||
|
||||
x00 += x05; x15 = Integers.RotateLeft(x15 ^ x00, 8);
|
||||
x01 += x06; x12 = Integers.RotateLeft(x12 ^ x01, 8);
|
||||
x02 += x07; x13 = Integers.RotateLeft(x13 ^ x02, 8);
|
||||
x03 += x04; x14 = Integers.RotateLeft(x14 ^ x03, 8);
|
||||
|
||||
x10 += x15; x05 = Integers.RotateLeft(x05 ^ x10, 7);
|
||||
x11 += x12; x06 = Integers.RotateLeft(x06 ^ x11, 7);
|
||||
x08 += x13; x07 = Integers.RotateLeft(x07 ^ x08, 7);
|
||||
x09 += x14; x04 = Integers.RotateLeft(x04 ^ x09, 7);
|
||||
}
|
||||
|
||||
Pack.UInt32_To_LE(x00 + input[0], output, 0);
|
||||
Pack.UInt32_To_LE(x01 + input[1], output, 4);
|
||||
Pack.UInt32_To_LE(x02 + input[2], output, 8);
|
||||
Pack.UInt32_To_LE(x03 + input[3], output, 12);
|
||||
Pack.UInt32_To_LE(x04 + input[4], output, 16);
|
||||
Pack.UInt32_To_LE(x05 + input[5], output, 20);
|
||||
Pack.UInt32_To_LE(x06 + input[6], output, 24);
|
||||
Pack.UInt32_To_LE(x07 + input[7], output, 28);
|
||||
Pack.UInt32_To_LE(x08 + input[8], output, 32);
|
||||
Pack.UInt32_To_LE(x09 + input[9], output, 36);
|
||||
Pack.UInt32_To_LE(x10 + input[10], output, 40);
|
||||
Pack.UInt32_To_LE(x11 + input[11], output, 44);
|
||||
Pack.UInt32_To_LE(x12 + input[12], output, 48);
|
||||
Pack.UInt32_To_LE(x13 + input[13], output, 52);
|
||||
Pack.UInt32_To_LE(x14 + input[14], output, 56);
|
||||
Pack.UInt32_To_LE(x15 + input[15], output, 60);
|
||||
}
|
||||
}
|
||||
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static Vector128<uint> Load128_UInt32(ReadOnlySpan<uint> t)
|
||||
{
|
||||
if (BitConverter.IsLittleEndian && Unsafe.SizeOf<Vector128<uint>>() == 16)
|
||||
return MemoryMarshal.Read<Vector128<uint>>(MemoryMarshal.AsBytes(t));
|
||||
|
||||
return Vector128.Create(t[0], t[1], t[2], t[3]);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void Store128_UInt32(Vector128<uint> s, Span<byte> t)
|
||||
{
|
||||
if (BitConverter.IsLittleEndian && Unsafe.SizeOf<Vector128<uint>>() == 16)
|
||||
{
|
||||
MemoryMarshal.Write(t, ref s);
|
||||
return;
|
||||
}
|
||||
|
||||
var u = s.AsUInt64();
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(t[..8], u.GetElement(0));
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(t[8..], u.GetElement(1));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#pragma warning restore
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: da7d76cfa0d2599459593226dc3959aa
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,124 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Utilities;
|
||||
|
||||
#if BESTHTTP_WITH_BURST
|
||||
using Unity.Burst;
|
||||
#endif
|
||||
|
||||
namespace BestHTTP.Connections.TLS.Crypto.Impl
|
||||
{
|
||||
#if BESTHTTP_WITH_BURST
|
||||
[Unity.Burst.BurstCompile]
|
||||
#endif
|
||||
internal static class FastChaChaEngineHelper
|
||||
{
|
||||
internal unsafe static void ChachaCore(int rounds, uint[] input, byte[] output)
|
||||
{
|
||||
fixed (uint* pinput = input)
|
||||
fixed (byte* poutput = output)
|
||||
ChachaCoreImpl(rounds, pinput, poutput);
|
||||
}
|
||||
|
||||
#if BESTHTTP_WITH_BURST
|
||||
[Unity.Burst.BurstCompile]
|
||||
[Unity.Burst.CompilerServices.SkipLocalsInit]
|
||||
#endif
|
||||
internal unsafe static void ChachaCoreImpl(int rounds,
|
||||
#if BESTHTTP_WITH_BURST
|
||||
[NoAlias]
|
||||
#endif
|
||||
uint* input,
|
||||
#if BESTHTTP_WITH_BURST
|
||||
[NoAlias]
|
||||
#endif
|
||||
byte* output)
|
||||
{
|
||||
uint* x = stackalloc uint[16];
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
x[i] = input[i];
|
||||
|
||||
uint tmp = 0;
|
||||
for (int i = rounds; i > 0; i -= 2)
|
||||
{
|
||||
x[00] += x[04]; tmp = x[12] ^ x[00]; x[12] = (tmp << 16) | (tmp >> -16); // Integers.RotateLeft(x[12] ^ x[00], 16);
|
||||
x[01] += x[05]; tmp = x[13] ^ x[01]; x[13] = (tmp << 16) | (tmp >> -16); // Integers.RotateLeft(x[13] ^ x[01], 16);
|
||||
x[02] += x[06]; tmp = x[14] ^ x[02]; x[14] = (tmp << 16) | (tmp >> -16); // Integers.RotateLeft(x[14] ^ x[02], 16);
|
||||
x[03] += x[07]; tmp = x[15] ^ x[03]; x[15] = (tmp << 16) | (tmp >> -16); // Integers.RotateLeft(x[15] ^ x[03], 16);
|
||||
|
||||
x[08] += x[12]; tmp = x[04] ^ x[08]; x[04] = (tmp << 12) | (tmp >> -12); // Integers.RotateLeft(x[04] ^ x[08], 12);
|
||||
x[09] += x[13]; tmp = x[05] ^ x[09]; x[05] = (tmp << 12) | (tmp >> -12); // Integers.RotateLeft(x[05] ^ x[09], 12);
|
||||
x[10] += x[14]; tmp = x[06] ^ x[10]; x[06] = (tmp << 12) | (tmp >> -12); // Integers.RotateLeft(x[06] ^ x[10], 12);
|
||||
x[11] += x[15]; tmp = x[07] ^ x[11]; x[07] = (tmp << 12) | (tmp >> -12); // Integers.RotateLeft(x[07] ^ x[11], 12);
|
||||
|
||||
x[00] += x[04]; tmp = x[12] ^ x[00]; x[12] = (tmp << 8) | (tmp >> -8); // Integers.RotateLeft(x[12] ^ x[00], 8);
|
||||
x[01] += x[05]; tmp = x[13] ^ x[01]; x[13] = (tmp << 8) | (tmp >> -8); // Integers.RotateLeft(x[13] ^ x[01], 8);
|
||||
x[02] += x[06]; tmp = x[14] ^ x[02]; x[14] = (tmp << 8) | (tmp >> -8); // Integers.RotateLeft(x[14] ^ x[02], 8);
|
||||
x[03] += x[07]; tmp = x[15] ^ x[03]; x[15] = (tmp << 8) | (tmp >> -8); // Integers.RotateLeft(x[15] ^ x[03], 8);
|
||||
|
||||
x[08] += x[12]; tmp = x[04] ^ x[08]; x[04] = (tmp << 7) | (tmp >> -7); // Integers.RotateLeft(x[04] ^ x[08], 7);
|
||||
x[09] += x[13]; tmp = x[05] ^ x[09]; x[05] = (tmp << 7) | (tmp >> -7); // Integers.RotateLeft(x[05] ^ x[09], 7);
|
||||
x[10] += x[14]; tmp = x[06] ^ x[10]; x[06] = (tmp << 7) | (tmp >> -7); // Integers.RotateLeft(x[06] ^ x[10], 7);
|
||||
x[11] += x[15]; tmp = x[07] ^ x[11]; x[07] = (tmp << 7) | (tmp >> -7); // Integers.RotateLeft(x[07] ^ x[11], 7);
|
||||
x[00] += x[05]; tmp = x[15] ^ x[00]; x[15] = (tmp << 16) | (tmp >> -16); // Integers.RotateLeft(x[15] ^ x[00], 16);
|
||||
x[01] += x[06]; tmp = x[12] ^ x[01]; x[12] = (tmp << 16) | (tmp >> -16); // Integers.RotateLeft(x[12] ^ x[01], 16);
|
||||
x[02] += x[07]; tmp = x[13] ^ x[02]; x[13] = (tmp << 16) | (tmp >> -16); // Integers.RotateLeft(x[13] ^ x[02], 16);
|
||||
x[03] += x[04]; tmp = x[14] ^ x[03]; x[14] = (tmp << 16) | (tmp >> -16); // Integers.RotateLeft(x[14] ^ x[03], 16);
|
||||
|
||||
x[10] += x[15]; tmp = x[05] ^ x[10]; x[05] = (tmp << 12) | (tmp >> -12); // Integers.RotateLeft(x[05] ^ x[10], 12);
|
||||
x[11] += x[12]; tmp = x[06] ^ x[11]; x[06] = (tmp << 12) | (tmp >> -12); // Integers.RotateLeft(x[06] ^ x[11], 12);
|
||||
x[08] += x[13]; tmp = x[07] ^ x[08]; x[07] = (tmp << 12) | (tmp >> -12); // Integers.RotateLeft(x[07] ^ x[08], 12);
|
||||
x[09] += x[14]; tmp = x[04] ^ x[09]; x[04] = (tmp << 12) | (tmp >> -12); // Integers.RotateLeft(x[04] ^ x[09], 12);
|
||||
|
||||
x[00] += x[05]; tmp = x[15] ^ x[00]; x[15] = (tmp << 8) | (tmp >> -8); // Integers.RotateLeft(x[15] ^ x[00], 8);
|
||||
x[01] += x[06]; tmp = x[12] ^ x[01]; x[12] = (tmp << 8) | (tmp >> -8); // Integers.RotateLeft(x[12] ^ x[01], 8);
|
||||
x[02] += x[07]; tmp = x[13] ^ x[02]; x[13] = (tmp << 8) | (tmp >> -8); // Integers.RotateLeft(x[13] ^ x[02], 8);
|
||||
x[03] += x[04]; tmp = x[14] ^ x[03]; x[14] = (tmp << 8) | (tmp >> -8); // Integers.RotateLeft(x[14] ^ x[03], 8);
|
||||
|
||||
x[10] += x[15]; tmp = x[05] ^ x[10]; x[05] = (tmp << 7) | (tmp >> -7); // Integers.RotateLeft(x[05] ^ x[10], 7);
|
||||
x[11] += x[12]; tmp = x[06] ^ x[11]; x[06] = (tmp << 7) | (tmp >> -7); // Integers.RotateLeft(x[06] ^ x[11], 7);
|
||||
x[08] += x[13]; tmp = x[07] ^ x[08]; x[07] = (tmp << 7) | (tmp >> -7); // Integers.RotateLeft(x[07] ^ x[08], 7);
|
||||
x[09] += x[14]; tmp = x[04] ^ x[09]; x[04] = (tmp << 7) | (tmp >> -7); // Integers.RotateLeft(x[04] ^ x[09], 7);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
uint n = x[i] + input[i];
|
||||
|
||||
output[(i * 4)] = (byte)n;
|
||||
output[(i * 4) + 1] = (byte)(n >> 8);
|
||||
output[(i * 4) + 2] = (byte)(n >> 16);
|
||||
output[(i * 4) + 3] = (byte)(n >> 24);
|
||||
}
|
||||
}
|
||||
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static unsafe void ImplProcessBlock(ReadOnlySpan<byte> input, Span<byte> output, byte[] keyStream)
|
||||
{
|
||||
fixed (byte* pinput = input)
|
||||
fixed (byte* poutput = output)
|
||||
fixed (byte* pkeyStream = keyStream)
|
||||
{
|
||||
ulong* pulinput = (ulong*)pinput;
|
||||
ulong* puloutput = (ulong*)poutput;
|
||||
ulong* pulkeyStream = (ulong*)pkeyStream;
|
||||
|
||||
puloutput[0] = pulkeyStream[0] ^ pulinput[0];
|
||||
puloutput[1] = pulkeyStream[1] ^ pulinput[1];
|
||||
puloutput[2] = pulkeyStream[2] ^ pulinput[2];
|
||||
puloutput[3] = pulkeyStream[3] ^ pulinput[3];
|
||||
|
||||
puloutput[4] = pulkeyStream[4] ^ pulinput[4];
|
||||
puloutput[5] = pulkeyStream[5] ^ pulinput[5];
|
||||
puloutput[6] = pulkeyStream[6] ^ pulinput[6];
|
||||
puloutput[7] = pulkeyStream[7] ^ pulinput[7];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: be2ccde693e75344ca2f2e8f707edec4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aee2eea618479f042ac5f8d3339f3005
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,78 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR) && BESTHTTP_WITH_BURST
|
||||
using System;
|
||||
using static Unity.Burst.Intrinsics.X86.Sse2;
|
||||
using static Unity.Burst.Intrinsics.Arm.Neon;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Unity.Burst;
|
||||
|
||||
// https://github.com/sschoener/burst-simd-exercises/blob/main/Assets/Examples/2-sum-small-numbers-sse3/SumSmallNumbers_SSE3.cs
|
||||
|
||||
namespace BestHTTP.Connections.TLS.Crypto.Impl
|
||||
{
|
||||
[BurstCompile]
|
||||
internal sealed unsafe class FastGcmBlockCipherHelper
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe void DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output, Span<byte> ctrBlock, Span<byte> S, int BlockSize)
|
||||
{
|
||||
fixed (byte* pInput = input)
|
||||
fixed (byte* pOutput = output)
|
||||
fixed (byte* pctrBlock = ctrBlock)
|
||||
fixed (byte* pS = S) {
|
||||
DecryptBlock_Impl(pInput, input.Length, pOutput, output.Length, pctrBlock, pS, BlockSize);
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile(CompileSynchronously = true)]
|
||||
private unsafe static void DecryptBlock_Impl([NoAlias] byte* pinput, int inLen, [NoAlias] byte* poutput, int outLen, [NoAlias] byte* pctrBlock, [NoAlias] byte* pS, int BlockSize)
|
||||
{
|
||||
if (IsSse2Supported)
|
||||
{
|
||||
var vInput = loadu_si128(pinput);
|
||||
var vCtrBlock = loadu_si128(pctrBlock);
|
||||
var vS = loadu_si128(pS);
|
||||
|
||||
vS = xor_si128(vS, vInput);
|
||||
vCtrBlock = xor_si128(vInput, vCtrBlock);
|
||||
|
||||
storeu_si128(pS, vS);
|
||||
storeu_si128(poutput, vCtrBlock);
|
||||
}
|
||||
else if (IsNeonSupported)
|
||||
{
|
||||
var vInput = vld1q_u8(pinput);
|
||||
var vCtrBlock = vld1q_u8(pctrBlock);
|
||||
var vS = vld1q_u8(pS);
|
||||
|
||||
vS = veorq_u8(vS, vInput);
|
||||
vCtrBlock = veorq_u8(vInput, vCtrBlock);
|
||||
|
||||
vst1q_u8(pS, vS);
|
||||
vst1q_u8(poutput, vCtrBlock);
|
||||
}
|
||||
else
|
||||
{
|
||||
Unity.Burst.CompilerServices.Hint.Assume(BlockSize == 16);
|
||||
|
||||
for (int i = 0; i < BlockSize; i += 4)
|
||||
{
|
||||
byte c0 = pinput[i + 0];
|
||||
byte c1 = pinput[i + 1];
|
||||
byte c2 = pinput[i + 2];
|
||||
byte c3 = pinput[i + 3];
|
||||
|
||||
pS[i + 0] ^= c0;
|
||||
pS[i + 1] ^= c1;
|
||||
pS[i + 2] ^= c2;
|
||||
pS[i + 3] ^= c3;
|
||||
|
||||
poutput[i + 0] = (byte)(c0 ^ pctrBlock[i + 0]);
|
||||
poutput[i + 1] = (byte)(c1 ^ pctrBlock[i + 1]);
|
||||
poutput[i + 2] = (byte)(c2 ^ pctrBlock[i + 2]);
|
||||
poutput[i + 3] = (byte)(c3 ^ pctrBlock[i + 3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0bc09b804bfd7824c90f995674bdad11
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,438 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
#pragma warning disable
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Generators;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Utilities;
|
||||
|
||||
namespace BestHTTP.Connections.TLS.Crypto.Impl
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Poly1305 message authentication code, designed by D. J. Bernstein.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Poly1305 computes a 128-bit (16 bytes) authenticator, using a 128 bit nonce and a 256 bit key
|
||||
/// consisting of a 128 bit key applied to an underlying cipher, and a 128 bit key (with 106
|
||||
/// effective key bits) used in the authenticator.
|
||||
///
|
||||
/// The polynomial calculation in this implementation is adapted from the public domain <a
|
||||
/// href="https://github.com/floodyberry/poly1305-donna">poly1305-donna-unrolled</a> C implementation
|
||||
/// by Andrew M (@floodyberry).
|
||||
/// </remarks>
|
||||
/// <seealso cref="BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Generators.Poly1305KeyGenerator"/>
|
||||
|
||||
|
||||
|
||||
[BestHTTP.PlatformSupport.IL2CPP.Il2CppEagerStaticClassConstructionAttribute]
|
||||
public sealed class FastPoly1305 : IMac
|
||||
{
|
||||
private const int BlockSize = 16;
|
||||
|
||||
private readonly IBlockCipher cipher;
|
||||
|
||||
// Initialised state
|
||||
|
||||
/** Polynomial key */
|
||||
private uint r0, r1, r2, r3, r4;
|
||||
|
||||
/** Precomputed 5 * r[1..4] */
|
||||
private uint s1, s2, s3, s4;
|
||||
|
||||
/** Encrypted nonce */
|
||||
private uint k0, k1, k2, k3;
|
||||
|
||||
// Accumulating state
|
||||
|
||||
/** Current block of buffered input */
|
||||
private byte[] currentBlock = new byte[BlockSize];
|
||||
|
||||
/** Current offset in input buffer */
|
||||
private int currentBlockOffset = 0;
|
||||
|
||||
/** Polynomial accumulator */
|
||||
private uint h0, h1, h2, h3, h4;
|
||||
|
||||
/**
|
||||
* Constructs a Poly1305 MAC, where the key passed to init() will be used directly.
|
||||
*/
|
||||
public FastPoly1305()
|
||||
{
|
||||
this.cipher = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a Poly1305 MAC, using a 128 bit block cipher.
|
||||
*/
|
||||
public FastPoly1305(IBlockCipher cipher)
|
||||
{
|
||||
if (cipher.GetBlockSize() != BlockSize)
|
||||
{
|
||||
throw new ArgumentException("Poly1305 requires a 128 bit block cipher.");
|
||||
}
|
||||
this.cipher = cipher;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialises the Poly1305 MAC.
|
||||
/// </summary>
|
||||
/// <param name="parameters">a {@link ParametersWithIV} containing a 128 bit nonce and a {@link KeyParameter} with
|
||||
/// a 256 bit key complying to the {@link Poly1305KeyGenerator Poly1305 key format}.</param>
|
||||
public void Init(ICipherParameters parameters)
|
||||
{
|
||||
byte[] nonce = null;
|
||||
|
||||
if (cipher != null)
|
||||
{
|
||||
if (!(parameters is ParametersWithIV))
|
||||
throw new ArgumentException("Poly1305 requires an IV when used with a block cipher.", "parameters");
|
||||
|
||||
ParametersWithIV ivParams = (ParametersWithIV)parameters;
|
||||
nonce = ivParams.GetIV();
|
||||
parameters = ivParams.Parameters;
|
||||
}
|
||||
|
||||
if (!(parameters is KeyParameter))
|
||||
throw new ArgumentException("Poly1305 requires a key.");
|
||||
|
||||
KeyParameter keyParams = (KeyParameter)parameters;
|
||||
|
||||
SetKey(keyParams.GetKey(), nonce);
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
private void SetKey(byte[] key, byte[] nonce)
|
||||
{
|
||||
if (key.Length != 32)
|
||||
throw new ArgumentException("Poly1305 key must be 256 bits.");
|
||||
|
||||
if (cipher != null && (nonce == null || nonce.Length != BlockSize))
|
||||
throw new ArgumentException("Poly1305 requires a 128 bit IV.");
|
||||
|
||||
// Extract r portion of key (and "clamp" the values)
|
||||
uint t0 = Pack.LE_To_UInt32(key, 0);
|
||||
uint t1 = Pack.LE_To_UInt32(key, 4);
|
||||
uint t2 = Pack.LE_To_UInt32(key, 8);
|
||||
uint t3 = Pack.LE_To_UInt32(key, 12);
|
||||
|
||||
// NOTE: The masks perform the key "clamping" implicitly
|
||||
r0 = t0 & 0x03FFFFFFU;
|
||||
r1 = ((t0 >> 26) | (t1 << 6)) & 0x03FFFF03U;
|
||||
r2 = ((t1 >> 20) | (t2 << 12)) & 0x03FFC0FFU;
|
||||
r3 = ((t2 >> 14) | (t3 << 18)) & 0x03F03FFFU;
|
||||
r4 = (t3 >> 8) & 0x000FFFFFU;
|
||||
|
||||
// Precompute multipliers
|
||||
s1 = r1 * 5;
|
||||
s2 = r2 * 5;
|
||||
s3 = r3 * 5;
|
||||
s4 = r4 * 5;
|
||||
|
||||
byte[] kBytes;
|
||||
int kOff;
|
||||
|
||||
if (cipher == null)
|
||||
{
|
||||
kBytes = key;
|
||||
kOff = BlockSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Compute encrypted nonce
|
||||
kBytes = new byte[BlockSize];
|
||||
kOff = 0;
|
||||
|
||||
cipher.Init(true, new KeyParameter(key, BlockSize, BlockSize));
|
||||
cipher.ProcessBlock(nonce, 0, kBytes, 0);
|
||||
}
|
||||
|
||||
k0 = Pack.LE_To_UInt32(kBytes, kOff + 0);
|
||||
k1 = Pack.LE_To_UInt32(kBytes, kOff + 4);
|
||||
k2 = Pack.LE_To_UInt32(kBytes, kOff + 8);
|
||||
k3 = Pack.LE_To_UInt32(kBytes, kOff + 12);
|
||||
}
|
||||
|
||||
public string AlgorithmName
|
||||
{
|
||||
get { return cipher == null ? "Poly1305" : "Poly1305-" + cipher.AlgorithmName; }
|
||||
}
|
||||
|
||||
public int GetMacSize()
|
||||
{
|
||||
return BlockSize;
|
||||
}
|
||||
|
||||
public void Update(byte input)
|
||||
{
|
||||
currentBlock[currentBlockOffset++] = input;
|
||||
if (currentBlockOffset == BlockSize)
|
||||
{
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
ProcessBlock(currentBlock);
|
||||
#else
|
||||
ProcessBlock(currentBlock, 0);
|
||||
#endif
|
||||
currentBlockOffset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#endif
|
||||
public void BlockUpdate(byte[] input, int inOff, int len)
|
||||
{
|
||||
Check.DataLength(input, inOff, len, "input buffer too short");
|
||||
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
BlockUpdate(input.AsSpan(inOff, len));
|
||||
#else
|
||||
int available = BlockSize - currentBlockOffset;
|
||||
if (len < available)
|
||||
{
|
||||
Array.Copy(input, inOff, currentBlock, currentBlockOffset, len);
|
||||
currentBlockOffset += len;
|
||||
return;
|
||||
}
|
||||
|
||||
int pos = 0;
|
||||
if (currentBlockOffset > 0)
|
||||
{
|
||||
Array.Copy(input, inOff, currentBlock, currentBlockOffset, available);
|
||||
pos = available;
|
||||
ProcessBlock(currentBlock, 0);
|
||||
}
|
||||
|
||||
int remaining;
|
||||
while ((remaining = len - pos) >= BlockSize)
|
||||
{
|
||||
ProcessBlock(input, inOff + pos);
|
||||
pos += BlockSize;
|
||||
}
|
||||
|
||||
Array.Copy(input, inOff + pos, currentBlock, 0, remaining);
|
||||
currentBlockOffset = remaining;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#if !UNITY_ANDROID || UNITY_EDITOR
|
||||
unsafe
|
||||
#endif
|
||||
public void BlockUpdate(ReadOnlySpan<byte> input)
|
||||
{
|
||||
int available = BlockSize - currentBlockOffset;
|
||||
if (input.Length < available)
|
||||
{
|
||||
input.CopyTo(currentBlock.AsSpan(currentBlockOffset));
|
||||
currentBlockOffset += input.Length;
|
||||
return;
|
||||
}
|
||||
|
||||
int pos = 0;
|
||||
if (currentBlockOffset > 0)
|
||||
{
|
||||
input[..available].CopyTo(currentBlock.AsSpan(currentBlockOffset));
|
||||
pos = available;
|
||||
ProcessBlock(currentBlock);
|
||||
}
|
||||
|
||||
int remaining;
|
||||
while ((remaining = input.Length - pos) >= BlockSize)
|
||||
{
|
||||
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||
uint t0 = System.Buffers.Binary.BinaryPrimitives.ReadUInt32LittleEndian(input[pos..]);
|
||||
uint t1 = System.Buffers.Binary.BinaryPrimitives.ReadUInt32LittleEndian(input[(pos + 4)..]);
|
||||
uint t2 = System.Buffers.Binary.BinaryPrimitives.ReadUInt32LittleEndian(input[(pos + 8)..]);
|
||||
uint t3 = System.Buffers.Binary.BinaryPrimitives.ReadUInt32LittleEndian(input[(pos + 12)..]);
|
||||
#else
|
||||
uint t0 = 0;
|
||||
uint t1 = 0;
|
||||
uint t2 = 0;
|
||||
uint t3 = 0;
|
||||
|
||||
fixed (byte* pblock = &input[pos])
|
||||
{
|
||||
uint* publock = (uint*)pblock;
|
||||
t0 = publock[0];
|
||||
t1 = publock[1];
|
||||
t2 = publock[2];
|
||||
t3 = publock[3];
|
||||
}
|
||||
#endif
|
||||
|
||||
h0 += t0 & 0x3ffffffU;
|
||||
h1 += ((t1 << 6) | (t0 >> 26)) & 0x3ffffffU;
|
||||
h2 += ((t2 << 12) | (t1 >> 20)) & 0x3ffffffU;
|
||||
h3 += ((t3 << 18) | (t2 >> 14)) & 0x3ffffffU;
|
||||
h4 += (1 << 24) | (t3 >> 8);
|
||||
|
||||
ulong tp0 = (ulong)h0 * r0 + (ulong)h1 * s4 + (ulong)h2 * s3 + (ulong)h3 * s2 + (ulong)h4 * s1;
|
||||
ulong tp1 = (ulong)h0 * r1 + (ulong)h1 * r0 + (ulong)h2 * s4 + (ulong)h3 * s3 + (ulong)h4 * s2;
|
||||
ulong tp2 = (ulong)h0 * r2 + (ulong)h1 * r1 + (ulong)h2 * r0 + (ulong)h3 * s4 + (ulong)h4 * s3;
|
||||
ulong tp3 = (ulong)h0 * r3 + (ulong)h1 * r2 + (ulong)h2 * r1 + (ulong)h3 * r0 + (ulong)h4 * s4;
|
||||
ulong tp4 = (ulong)h0 * r4 + (ulong)h1 * r3 + (ulong)h2 * r2 + (ulong)h3 * r1 + (ulong)h4 * r0;
|
||||
|
||||
h0 = (uint)tp0 & 0x3ffffff; tp1 += (tp0 >> 26);
|
||||
h1 = (uint)tp1 & 0x3ffffff; tp2 += (tp1 >> 26);
|
||||
h2 = (uint)tp2 & 0x3ffffff; tp3 += (tp2 >> 26);
|
||||
h3 = (uint)tp3 & 0x3ffffff; tp4 += (tp3 >> 26);
|
||||
h4 = (uint)tp4 & 0x3ffffff;
|
||||
h0 += (uint)(tp4 >> 26) * 5;
|
||||
h1 += h0 >> 26; h0 &= 0x3ffffff;
|
||||
|
||||
pos += BlockSize;
|
||||
}
|
||||
|
||||
input[pos..].CopyTo(currentBlock);
|
||||
currentBlockOffset = remaining;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void ProcessBlock(ReadOnlySpan<byte> block)
|
||||
{
|
||||
uint t0 = System.Buffers.Binary.BinaryPrimitives.ReadUInt32LittleEndian(block);
|
||||
uint t1 = System.Buffers.Binary.BinaryPrimitives.ReadUInt32LittleEndian(block[4..]);
|
||||
uint t2 = System.Buffers.Binary.BinaryPrimitives.ReadUInt32LittleEndian(block[8..]);
|
||||
uint t3 = System.Buffers.Binary.BinaryPrimitives.ReadUInt32LittleEndian(block[12..]);
|
||||
#else
|
||||
private void ProcessBlock(byte[] buf, int off)
|
||||
{
|
||||
uint t0 = Pack.LE_To_UInt32(buf, off + 0);
|
||||
uint t1 = Pack.LE_To_UInt32(buf, off + 4);
|
||||
uint t2 = Pack.LE_To_UInt32(buf, off + 8);
|
||||
uint t3 = Pack.LE_To_UInt32(buf, off + 12);
|
||||
#endif
|
||||
|
||||
h0 += t0 & 0x3ffffffU;
|
||||
h1 += ((t1 << 6) | (t0 >> 26)) & 0x3ffffffU;
|
||||
h2 += ((t2 << 12) | (t1 >> 20)) & 0x3ffffffU;
|
||||
h3 += ((t3 << 18) | (t2 >> 14)) & 0x3ffffffU;
|
||||
h4 += (1 << 24) | (t3 >> 8);
|
||||
|
||||
ulong tp0 = (ulong)h0 * r0 + (ulong)h1 * s4 + (ulong)h2 * s3 + (ulong)h3 * s2 + (ulong)h4 * s1;
|
||||
ulong tp1 = (ulong)h0 * r1 + (ulong)h1 * r0 + (ulong)h2 * s4 + (ulong)h3 * s3 + (ulong)h4 * s2;
|
||||
ulong tp2 = (ulong)h0 * r2 + (ulong)h1 * r1 + (ulong)h2 * r0 + (ulong)h3 * s4 + (ulong)h4 * s3;
|
||||
ulong tp3 = (ulong)h0 * r3 + (ulong)h1 * r2 + (ulong)h2 * r1 + (ulong)h3 * r0 + (ulong)h4 * s4;
|
||||
ulong tp4 = (ulong)h0 * r4 + (ulong)h1 * r3 + (ulong)h2 * r2 + (ulong)h3 * r1 + (ulong)h4 * r0;
|
||||
|
||||
h0 = (uint)tp0 & 0x3ffffff; tp1 += (tp0 >> 26);
|
||||
h1 = (uint)tp1 & 0x3ffffff; tp2 += (tp1 >> 26);
|
||||
h2 = (uint)tp2 & 0x3ffffff; tp3 += (tp2 >> 26);
|
||||
h3 = (uint)tp3 & 0x3ffffff; tp4 += (tp3 >> 26);
|
||||
h4 = (uint)tp4 & 0x3ffffff;
|
||||
h0 += (uint)(tp4 >> 26) * 5;
|
||||
h1 += h0 >> 26; h0 &= 0x3ffffff;
|
||||
}
|
||||
|
||||
public int DoFinal(byte[] output, int outOff)
|
||||
{
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
return DoFinal(output.AsSpan(outOff));
|
||||
#else
|
||||
Check.OutputLength(output, outOff, BlockSize, "output buffer is too short.");
|
||||
|
||||
if (currentBlockOffset > 0)
|
||||
{
|
||||
// Process padded block
|
||||
if (currentBlockOffset < BlockSize)
|
||||
{
|
||||
currentBlock[currentBlockOffset++] = 1;
|
||||
while (currentBlockOffset < BlockSize)
|
||||
{
|
||||
currentBlock[currentBlockOffset++] = 0;
|
||||
}
|
||||
|
||||
h4 -= (1 << 24);
|
||||
}
|
||||
|
||||
ProcessBlock(currentBlock, 0);
|
||||
}
|
||||
|
||||
UnityEngine.Debug.Assert(h4 >> 26 == 0);
|
||||
|
||||
//h0 += (h4 >> 26) * 5U + 5U; h4 &= 0x3ffffff;
|
||||
h0 += 5U;
|
||||
h1 += h0 >> 26; h0 &= 0x3ffffff;
|
||||
h2 += h1 >> 26; h1 &= 0x3ffffff;
|
||||
h3 += h2 >> 26; h2 &= 0x3ffffff;
|
||||
h4 += h3 >> 26; h3 &= 0x3ffffff;
|
||||
|
||||
long c = ((int)(h4 >> 26) - 1) * 5;
|
||||
c += (long)k0 + ((h0) | (h1 << 26));
|
||||
Pack.UInt32_To_LE((uint)c, output, outOff); c >>= 32;
|
||||
c += (long)k1 + ((h1 >> 6) | (h2 << 20));
|
||||
Pack.UInt32_To_LE((uint)c, output, outOff + 4); c >>= 32;
|
||||
c += (long)k2 + ((h2 >> 12) | (h3 << 14));
|
||||
Pack.UInt32_To_LE((uint)c, output, outOff + 8); c >>= 32;
|
||||
c += (long)k3 + ((h3 >> 18) | (h4 << 8));
|
||||
Pack.UInt32_To_LE((uint)c, output, outOff + 12);
|
||||
|
||||
Reset();
|
||||
return BlockSize;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
public int DoFinal(Span<byte> output)
|
||||
{
|
||||
Check.OutputLength(output, BlockSize, "output buffer is too short.");
|
||||
|
||||
if (currentBlockOffset > 0)
|
||||
{
|
||||
// Process padded block
|
||||
if (currentBlockOffset < BlockSize)
|
||||
{
|
||||
currentBlock[currentBlockOffset++] = 1;
|
||||
while (currentBlockOffset < BlockSize)
|
||||
{
|
||||
currentBlock[currentBlockOffset++] = 0;
|
||||
}
|
||||
|
||||
h4 -= (1 << 24);
|
||||
}
|
||||
|
||||
ProcessBlock(currentBlock);
|
||||
}
|
||||
|
||||
UnityEngine.Debug.Assert(h4 >> 26 == 0);
|
||||
|
||||
//h0 += (h4 >> 26) * 5U + 5U; h4 &= 0x3ffffff;
|
||||
h0 += 5U;
|
||||
h1 += h0 >> 26; h0 &= 0x3ffffff;
|
||||
h2 += h1 >> 26; h1 &= 0x3ffffff;
|
||||
h3 += h2 >> 26; h2 &= 0x3ffffff;
|
||||
h4 += h3 >> 26; h3 &= 0x3ffffff;
|
||||
|
||||
long c = ((int)(h4 >> 26) - 1) * 5;
|
||||
c += (long)k0 + ((h0) | (h1 << 26));
|
||||
Pack.UInt32_To_LE((uint)c, output); c >>= 32;
|
||||
c += (long)k1 + ((h1 >> 6) | (h2 << 20));
|
||||
Pack.UInt32_To_LE((uint)c, output[4..]); c >>= 32;
|
||||
c += (long)k2 + ((h2 >> 12) | (h3 << 14));
|
||||
Pack.UInt32_To_LE((uint)c, output[8..]); c >>= 32;
|
||||
c += (long)k3 + ((h3 >> 18) | (h4 << 8));
|
||||
Pack.UInt32_To_LE((uint)c, output[12..]);
|
||||
|
||||
Reset();
|
||||
return BlockSize;
|
||||
}
|
||||
#endif
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Reset()
|
||||
{
|
||||
currentBlockOffset = 0;
|
||||
|
||||
h0 = h1 = h2 = h3 = h4 = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 37a51744d555ed842b5a324a257460ee
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,560 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
#pragma warning disable
|
||||
using System;
|
||||
#if NETSTANDARD1_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER || UNITY_2021_2_OR_NEWER
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
#endif
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Intrinsics;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
#endif
|
||||
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Utilities;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities;
|
||||
|
||||
namespace BestHTTP.Connections.TLS.Crypto.Impl
|
||||
{
|
||||
/// <summary>
|
||||
/// Implementation of Daniel J. Bernstein's Salsa20 stream cipher, Snuffle 2005
|
||||
/// </summary>
|
||||
|
||||
|
||||
|
||||
[BestHTTP.PlatformSupport.IL2CPP.Il2CppEagerStaticClassConstructionAttribute]
|
||||
public class FastSalsa20Engine
|
||||
: IStreamCipher
|
||||
{
|
||||
public static readonly int DEFAULT_ROUNDS = 20;
|
||||
|
||||
/** Constants */
|
||||
private const int StateSize = 16; // 16, 32 bit ints = 64 bytes
|
||||
|
||||
private readonly static uint[] TAU_SIGMA = Pack.LE_To_UInt32(Strings.ToAsciiByteArray("expand 16-byte k" + "expand 32-byte k"), 0, 8);
|
||||
|
||||
internal void PackTauOrSigma(int keyLength, uint[] state, int stateOffset)
|
||||
{
|
||||
int tsOff = (keyLength - 16) / 4;
|
||||
state[stateOffset] = TAU_SIGMA[tsOff];
|
||||
state[stateOffset + 1] = TAU_SIGMA[tsOff + 1];
|
||||
state[stateOffset + 2] = TAU_SIGMA[tsOff + 2];
|
||||
state[stateOffset + 3] = TAU_SIGMA[tsOff + 3];
|
||||
}
|
||||
|
||||
protected int rounds;
|
||||
|
||||
/*
|
||||
* variables to hold the state of the engine
|
||||
* during encryption and decryption
|
||||
*/
|
||||
internal int index = 0;
|
||||
internal uint[] engineState = new uint[StateSize]; // state
|
||||
internal uint[] x = new uint[StateSize]; // internal buffer
|
||||
internal byte[] keyStream = new byte[StateSize * 4]; // expanded state, 64 bytes
|
||||
internal bool initialised = false;
|
||||
|
||||
/*
|
||||
* internal counter
|
||||
*/
|
||||
private uint cW0, cW1, cW2;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a 20 round Salsa20 engine.
|
||||
/// </summary>
|
||||
public FastSalsa20Engine()
|
||||
: this(DEFAULT_ROUNDS)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a Salsa20 engine with a specific number of rounds.
|
||||
/// </summary>
|
||||
/// <param name="rounds">the number of rounds (must be an even number).</param>
|
||||
public FastSalsa20Engine(int rounds)
|
||||
{
|
||||
if (rounds <= 0 || (rounds & 1) != 0)
|
||||
{
|
||||
throw new ArgumentException("'rounds' must be a positive, even number");
|
||||
}
|
||||
|
||||
this.rounds = rounds;
|
||||
}
|
||||
|
||||
public virtual void Init(
|
||||
bool forEncryption,
|
||||
ICipherParameters parameters)
|
||||
{
|
||||
/*
|
||||
* Salsa20 encryption and decryption is completely
|
||||
* symmetrical, so the 'forEncryption' is
|
||||
* irrelevant. (Like 90% of stream ciphers)
|
||||
*/
|
||||
|
||||
ParametersWithIV ivParams = parameters as ParametersWithIV;
|
||||
if (ivParams == null)
|
||||
throw new ArgumentException(AlgorithmName + " Init requires an IV", "parameters");
|
||||
|
||||
byte[] iv = ivParams.GetIV();
|
||||
if (iv == null || iv.Length != NonceSize)
|
||||
throw new ArgumentException(AlgorithmName + " requires exactly " + NonceSize + " bytes of IV");
|
||||
|
||||
ICipherParameters keyParam = ivParams.Parameters;
|
||||
if (keyParam == null)
|
||||
{
|
||||
if (!initialised)
|
||||
throw new InvalidOperationException(AlgorithmName + " KeyParameter can not be null for first initialisation");
|
||||
|
||||
SetKey(null, iv);
|
||||
}
|
||||
else if (keyParam is KeyParameter)
|
||||
{
|
||||
SetKey(((KeyParameter)keyParam).GetKey(), iv);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException(AlgorithmName + " Init parameters must contain a KeyParameter (or null for re-init)");
|
||||
}
|
||||
|
||||
Reset();
|
||||
initialised = true;
|
||||
}
|
||||
|
||||
protected virtual int NonceSize
|
||||
{
|
||||
get { return 8; }
|
||||
}
|
||||
|
||||
public virtual string AlgorithmName
|
||||
{
|
||||
get
|
||||
{
|
||||
string name = "Salsa20";
|
||||
if (rounds != DEFAULT_ROUNDS)
|
||||
{
|
||||
name += "/" + rounds;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual byte ReturnByte(
|
||||
byte input)
|
||||
{
|
||||
if (LimitExceeded())
|
||||
{
|
||||
throw new MaxBytesExceededException("2^70 byte limit per IV; Change IV");
|
||||
}
|
||||
|
||||
if (index == 0)
|
||||
{
|
||||
GenerateKeyStream(keyStream);
|
||||
AdvanceCounter();
|
||||
}
|
||||
|
||||
byte output = (byte)(keyStream[index] ^ input);
|
||||
index = (index + 1) & 63;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
protected virtual void AdvanceCounter()
|
||||
{
|
||||
if (++engineState[8] == 0)
|
||||
{
|
||||
++engineState[9];
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe virtual void ProcessBytes(
|
||||
byte[] inBytes,
|
||||
int inOff,
|
||||
int len,
|
||||
byte[] outBytes,
|
||||
int outOff)
|
||||
{
|
||||
if (!initialised)
|
||||
throw new InvalidOperationException(AlgorithmName + " not initialised");
|
||||
|
||||
Check.DataLength(inBytes, inOff, len, "input buffer too short");
|
||||
Check.OutputLength(outBytes, outOff, len, "output buffer too short");
|
||||
|
||||
if (LimitExceeded((uint)len))
|
||||
throw new MaxBytesExceededException("2^70 byte limit per IV would be exceeded; Change IV");
|
||||
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
if (index == 0)
|
||||
{
|
||||
GenerateKeyStream(keyStream);
|
||||
AdvanceCounter();
|
||||
|
||||
if (len - i >= 64)
|
||||
{
|
||||
fixed (byte* pbout = outBytes)
|
||||
fixed (byte* pbin = inBytes)
|
||||
fixed (byte* pbkey = keyStream)
|
||||
{
|
||||
#if BESTHTTP_WITH_BURST
|
||||
FastSalsa20EngineHelper.ProcessBytes(pbout, outOff, pbin, inOff, pbkey);
|
||||
#else
|
||||
ulong* pulOut = (ulong*)&pbout[outOff];
|
||||
ulong* pulIn = (ulong*)&pbin[inOff];
|
||||
ulong* pulKeyStream = (ulong*)pbkey;
|
||||
|
||||
pulOut[0] = pulKeyStream[0] ^ pulIn[0];
|
||||
pulOut[1] = pulKeyStream[1] ^ pulIn[1];
|
||||
pulOut[2] = pulKeyStream[2] ^ pulIn[2];
|
||||
pulOut[3] = pulKeyStream[3] ^ pulIn[3];
|
||||
#endif
|
||||
}
|
||||
|
||||
i += 63;
|
||||
index = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
outBytes[i + outOff] = (byte)(keyStream[index] ^ inBytes[i + inOff]);
|
||||
index = (index + 1) & 63;
|
||||
}
|
||||
}
|
||||
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
public virtual void ProcessBytes(ReadOnlySpan<byte> input, Span<byte> output)
|
||||
{
|
||||
if (!initialised)
|
||||
throw new InvalidOperationException(AlgorithmName + " not initialised");
|
||||
|
||||
Check.OutputLength(output, input.Length, "output buffer too short");
|
||||
|
||||
if (LimitExceeded((uint)input.Length))
|
||||
throw new MaxBytesExceededException("2^70 byte limit per IV would be exceeded; Change IV");
|
||||
|
||||
for (int i = 0; i < input.Length; i++)
|
||||
{
|
||||
if (index == 0)
|
||||
{
|
||||
GenerateKeyStream(keyStream);
|
||||
AdvanceCounter();
|
||||
|
||||
if (input.Length - i >= 64)
|
||||
{
|
||||
Span<ulong> lOutput = MemoryMarshal.Cast<byte, ulong>(output.Slice(i));
|
||||
ReadOnlySpan<ulong> lKeyStream = MemoryMarshal.Cast<byte, ulong>(keyStream);
|
||||
ReadOnlySpan<ulong> lInput = MemoryMarshal.Cast<byte, ulong>(input.Slice(i));
|
||||
|
||||
lOutput[0] = lKeyStream[0] ^ lInput[0];
|
||||
lOutput[1] = lKeyStream[1] ^ lInput[1];
|
||||
lOutput[2] = lKeyStream[2] ^ lInput[2];
|
||||
lOutput[3] = lKeyStream[3] ^ lInput[3];
|
||||
|
||||
i += 63;
|
||||
index = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
output[i] = (byte)(keyStream[index++] ^ input[i]);
|
||||
index &= 63;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
public virtual void Reset()
|
||||
{
|
||||
index = 0;
|
||||
ResetLimitCounter();
|
||||
ResetCounter();
|
||||
}
|
||||
|
||||
protected virtual void ResetCounter()
|
||||
{
|
||||
engineState[8] = engineState[9] = 0;
|
||||
}
|
||||
|
||||
protected virtual void SetKey(byte[] keyBytes, byte[] ivBytes)
|
||||
{
|
||||
if (keyBytes != null)
|
||||
{
|
||||
if ((keyBytes.Length != 16) && (keyBytes.Length != 32))
|
||||
throw new ArgumentException(AlgorithmName + " requires 128 bit or 256 bit key");
|
||||
|
||||
int tsOff = (keyBytes.Length - 16) / 4;
|
||||
engineState[0] = TAU_SIGMA[tsOff];
|
||||
engineState[5] = TAU_SIGMA[tsOff + 1];
|
||||
engineState[10] = TAU_SIGMA[tsOff + 2];
|
||||
engineState[15] = TAU_SIGMA[tsOff + 3];
|
||||
|
||||
// Key
|
||||
Pack.LE_To_UInt32(keyBytes, 0, engineState, 1, 4);
|
||||
Pack.LE_To_UInt32(keyBytes, keyBytes.Length - 16, engineState, 11, 4);
|
||||
}
|
||||
|
||||
// IV
|
||||
Pack.LE_To_UInt32(ivBytes, 0, engineState, 6, 2);
|
||||
}
|
||||
|
||||
protected virtual void GenerateKeyStream(byte[] output)
|
||||
{
|
||||
SalsaCore(rounds, engineState, x);
|
||||
Pack.UInt32_To_LE(x, output, 0);
|
||||
}
|
||||
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
internal static void SalsaCore(int rounds, ReadOnlySpan<uint> input, Span<uint> output)
|
||||
{
|
||||
if (input.Length < 16)
|
||||
throw new ArgumentException();
|
||||
if (output.Length < 16)
|
||||
throw new ArgumentException();
|
||||
if (rounds % 2 != 0)
|
||||
throw new ArgumentException("Number of rounds must be even");
|
||||
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
if (Sse41.IsSupported && BitConverter.IsLittleEndian && Unsafe.SizeOf<Vector128<short>>() == 16)
|
||||
{
|
||||
Vector128<uint> b0, b1, b2, b3;
|
||||
{
|
||||
var I = MemoryMarshal.AsBytes(input[..16]);
|
||||
var t0 = MemoryMarshal.Read<Vector128<short>>(I[0x00..0x10]);
|
||||
var t1 = MemoryMarshal.Read<Vector128<short>>(I[0x10..0x20]);
|
||||
var t2 = MemoryMarshal.Read<Vector128<short>>(I[0x20..0x30]);
|
||||
var t3 = MemoryMarshal.Read<Vector128<short>>(I[0x30..0x40]);
|
||||
|
||||
var u0 = Sse41.Blend(t0, t2, 0xF0);
|
||||
var u1 = Sse41.Blend(t1, t3, 0xC3);
|
||||
var u2 = Sse41.Blend(t0, t2, 0x0F);
|
||||
var u3 = Sse41.Blend(t1, t3, 0x3C);
|
||||
|
||||
b0 = Sse41.Blend(u0, u1, 0xCC).AsUInt32();
|
||||
b1 = Sse41.Blend(u0, u1, 0x33).AsUInt32();
|
||||
b2 = Sse41.Blend(u2, u3, 0xCC).AsUInt32();
|
||||
b3 = Sse41.Blend(u2, u3, 0x33).AsUInt32();
|
||||
}
|
||||
|
||||
var c0 = b0;
|
||||
var c1 = b1;
|
||||
var c2 = b2;
|
||||
var c3 = b3;
|
||||
|
||||
for (int i = rounds; i > 0; i -= 2)
|
||||
{
|
||||
QuarterRound_Sse2(ref c0, ref c3, ref c2, ref c1);
|
||||
QuarterRound_Sse2(ref c0, ref c1, ref c2, ref c3);
|
||||
}
|
||||
|
||||
b0 = Sse2.Add(b0, c0);
|
||||
b1 = Sse2.Add(b1, c1);
|
||||
b2 = Sse2.Add(b2, c2);
|
||||
b3 = Sse2.Add(b3, c3);
|
||||
|
||||
{
|
||||
var t0 = b0.AsUInt16();
|
||||
var t1 = b1.AsUInt16();
|
||||
var t2 = b2.AsUInt16();
|
||||
var t3 = b3.AsUInt16();
|
||||
|
||||
var u0 = Sse41.Blend(t0, t1, 0xCC);
|
||||
var u1 = Sse41.Blend(t0, t1, 0x33);
|
||||
var u2 = Sse41.Blend(t2, t3, 0xCC);
|
||||
var u3 = Sse41.Blend(t2, t3, 0x33);
|
||||
|
||||
var v0 = Sse41.Blend(u0, u2, 0xF0);
|
||||
var v1 = Sse41.Blend(u1, u3, 0xC3);
|
||||
var v2 = Sse41.Blend(u0, u2, 0x0F);
|
||||
var v3 = Sse41.Blend(u1, u3, 0x3C);
|
||||
|
||||
var X = MemoryMarshal.AsBytes(output[..16]);
|
||||
MemoryMarshal.Write(X[0x00..0x10], ref v0);
|
||||
MemoryMarshal.Write(X[0x10..0x20], ref v1);
|
||||
MemoryMarshal.Write(X[0x20..0x30], ref v2);
|
||||
MemoryMarshal.Write(X[0x30..0x40], ref v3);
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
uint x00 = input[ 0];
|
||||
uint x01 = input[ 1];
|
||||
uint x02 = input[ 2];
|
||||
uint x03 = input[ 3];
|
||||
uint x04 = input[ 4];
|
||||
uint x05 = input[ 5];
|
||||
uint x06 = input[ 6];
|
||||
uint x07 = input[ 7];
|
||||
uint x08 = input[ 8];
|
||||
uint x09 = input[ 9];
|
||||
uint x10 = input[10];
|
||||
uint x11 = input[11];
|
||||
uint x12 = input[12];
|
||||
uint x13 = input[13];
|
||||
uint x14 = input[14];
|
||||
uint x15 = input[15];
|
||||
|
||||
for (int i = rounds; i > 0; i -= 2)
|
||||
{
|
||||
QuarterRound(ref x00, ref x04, ref x08, ref x12);
|
||||
QuarterRound(ref x05, ref x09, ref x13, ref x01);
|
||||
QuarterRound(ref x10, ref x14, ref x02, ref x06);
|
||||
QuarterRound(ref x15, ref x03, ref x07, ref x11);
|
||||
|
||||
QuarterRound(ref x00, ref x01, ref x02, ref x03);
|
||||
QuarterRound(ref x05, ref x06, ref x07, ref x04);
|
||||
QuarterRound(ref x10, ref x11, ref x08, ref x09);
|
||||
QuarterRound(ref x15, ref x12, ref x13, ref x14);
|
||||
}
|
||||
|
||||
output[ 0] = x00 + input[ 0];
|
||||
output[ 1] = x01 + input[ 1];
|
||||
output[ 2] = x02 + input[ 2];
|
||||
output[ 3] = x03 + input[ 3];
|
||||
output[ 4] = x04 + input[ 4];
|
||||
output[ 5] = x05 + input[ 5];
|
||||
output[ 6] = x06 + input[ 6];
|
||||
output[ 7] = x07 + input[ 7];
|
||||
output[ 8] = x08 + input[ 8];
|
||||
output[ 9] = x09 + input[ 9];
|
||||
output[10] = x10 + input[10];
|
||||
output[11] = x11 + input[11];
|
||||
output[12] = x12 + input[12];
|
||||
output[13] = x13 + input[13];
|
||||
output[14] = x14 + input[14];
|
||||
output[15] = x15 + input[15];
|
||||
}
|
||||
#else
|
||||
internal static void SalsaCore(int rounds, uint[] input, uint[] output)
|
||||
{
|
||||
if (input.Length < 16)
|
||||
throw new ArgumentException();
|
||||
if (output.Length < 16)
|
||||
throw new ArgumentException();
|
||||
if (rounds % 2 != 0)
|
||||
throw new ArgumentException("Number of rounds must be even");
|
||||
|
||||
uint x00 = input[0];
|
||||
uint x01 = input[1];
|
||||
uint x02 = input[2];
|
||||
uint x03 = input[3];
|
||||
uint x04 = input[4];
|
||||
uint x05 = input[5];
|
||||
uint x06 = input[6];
|
||||
uint x07 = input[7];
|
||||
uint x08 = input[8];
|
||||
uint x09 = input[9];
|
||||
uint x10 = input[10];
|
||||
uint x11 = input[11];
|
||||
uint x12 = input[12];
|
||||
uint x13 = input[13];
|
||||
uint x14 = input[14];
|
||||
uint x15 = input[15];
|
||||
|
||||
for (int i = rounds; i > 0; i -= 2)
|
||||
{
|
||||
QuarterRound(ref x00, ref x04, ref x08, ref x12);
|
||||
QuarterRound(ref x05, ref x09, ref x13, ref x01);
|
||||
QuarterRound(ref x10, ref x14, ref x02, ref x06);
|
||||
QuarterRound(ref x15, ref x03, ref x07, ref x11);
|
||||
|
||||
QuarterRound(ref x00, ref x01, ref x02, ref x03);
|
||||
QuarterRound(ref x05, ref x06, ref x07, ref x04);
|
||||
QuarterRound(ref x10, ref x11, ref x08, ref x09);
|
||||
QuarterRound(ref x15, ref x12, ref x13, ref x14);
|
||||
}
|
||||
|
||||
output[ 0] = x00 + input[ 0];
|
||||
output[ 1] = x01 + input[ 1];
|
||||
output[ 2] = x02 + input[ 2];
|
||||
output[ 3] = x03 + input[ 3];
|
||||
output[ 4] = x04 + input[ 4];
|
||||
output[ 5] = x05 + input[ 5];
|
||||
output[ 6] = x06 + input[ 6];
|
||||
output[ 7] = x07 + input[ 7];
|
||||
output[ 8] = x08 + input[ 8];
|
||||
output[ 9] = x09 + input[ 9];
|
||||
output[10] = x10 + input[10];
|
||||
output[11] = x11 + input[11];
|
||||
output[12] = x12 + input[12];
|
||||
output[13] = x13 + input[13];
|
||||
output[14] = x14 + input[14];
|
||||
output[15] = x15 + input[15];
|
||||
}
|
||||
#endif
|
||||
|
||||
internal void ResetLimitCounter()
|
||||
{
|
||||
cW0 = 0;
|
||||
cW1 = 0;
|
||||
cW2 = 0;
|
||||
}
|
||||
|
||||
internal bool LimitExceeded()
|
||||
{
|
||||
if (++cW0 == 0)
|
||||
{
|
||||
if (++cW1 == 0)
|
||||
{
|
||||
return (++cW2 & 0x20) != 0; // 2^(32 + 32 + 6)
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* this relies on the fact len will always be positive.
|
||||
*/
|
||||
internal bool LimitExceeded(
|
||||
uint len)
|
||||
{
|
||||
uint old = cW0;
|
||||
cW0 += len;
|
||||
if (cW0 < old)
|
||||
{
|
||||
if (++cW1 == 0)
|
||||
{
|
||||
return (++cW2 & 0x20) != 0; // 2^(32 + 32 + 6)
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#if NETSTANDARD1_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER || UNITY_2021_2_OR_NEWER
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#endif
|
||||
private static void QuarterRound(ref uint a, ref uint b, ref uint c, ref uint d)
|
||||
{
|
||||
b ^= Integers.RotateLeft(a + d, 7);
|
||||
c ^= Integers.RotateLeft(b + a, 9);
|
||||
d ^= Integers.RotateLeft(c + b, 13);
|
||||
a ^= Integers.RotateLeft(d + c, 18);
|
||||
}
|
||||
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void QuarterRound_Sse2(ref Vector128<uint> a, ref Vector128<uint> b, ref Vector128<uint> c,
|
||||
ref Vector128<uint> d)
|
||||
{
|
||||
b = Sse2.Xor(b, Rotate_Sse2(Sse2.Add(a, d), 7));
|
||||
c = Sse2.Xor(c, Rotate_Sse2(Sse2.Add(b, a), 9));
|
||||
d = Sse2.Xor(d, Rotate_Sse2(Sse2.Add(c, b), 13));
|
||||
a = Sse2.Xor(a, Rotate_Sse2(Sse2.Add(d, c), 18));
|
||||
|
||||
b = Sse2.Shuffle(b, 0x93);
|
||||
c = Sse2.Shuffle(c, 0x4E);
|
||||
d = Sse2.Shuffle(d, 0x39);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static Vector128<uint> Rotate_Sse2(Vector128<uint> x, byte sl)
|
||||
{
|
||||
byte sr = (byte)(32 - sl);
|
||||
return Sse2.Xor(Sse2.ShiftLeftLogical(x, sl), Sse2.ShiftRightLogical(x, sr));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#pragma warning restore
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d00c50c4ba4b7eb469b4940b7174779c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,31 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
using System;
|
||||
|
||||
namespace BestHTTP.Connections.TLS.Crypto.Impl
|
||||
{
|
||||
#if BESTHTTP_WITH_BURST
|
||||
[Unity.Burst.BurstCompile]
|
||||
#endif
|
||||
internal static class FastSalsa20EngineHelper
|
||||
{
|
||||
#if BESTHTTP_WITH_BURST
|
||||
[Unity.Burst.BurstCompile]
|
||||
public unsafe static void ProcessBytes([Unity.Burst.NoAlias] byte* outBytes, int outOff, [Unity.Burst.NoAlias] byte* inBytes, int inOff, [Unity.Burst.NoAlias] byte* keyStream)
|
||||
{
|
||||
//for (int i = 0; i < 64; ++i)
|
||||
// outBytes[idx + i + outOff] = (byte)(keyStream[i] ^ inBytes[idx + i + inOff]);
|
||||
|
||||
ulong* pulOut = (ulong*)&outBytes[outOff];
|
||||
ulong* pulIn = (ulong*)&inBytes[inOff];
|
||||
ulong* pulKeyStream = (ulong*)keyStream;
|
||||
|
||||
pulOut[0] = pulKeyStream[0] ^ pulIn[0];
|
||||
pulOut[1] = pulKeyStream[1] ^ pulIn[1];
|
||||
pulOut[2] = pulKeyStream[2] ^ pulIn[2];
|
||||
pulOut[3] = pulKeyStream[3] ^ pulIn[3];
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 07c49c8cf55d75e4fa1d12af126757d3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,141 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
#pragma warning disable
|
||||
using System;
|
||||
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Math;
|
||||
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities;
|
||||
|
||||
namespace BestHTTP.Connections.TLS.Crypto.Impl
|
||||
{
|
||||
/**
|
||||
* Implements the Segmented Integer Counter (SIC) mode on top of a simple
|
||||
* block cipher.
|
||||
*/
|
||||
public class FastSicBlockCipher
|
||||
: IBlockCipherMode
|
||||
{
|
||||
private readonly IBlockCipher cipher;
|
||||
private readonly int blockSize;
|
||||
private readonly byte[] counter;
|
||||
private readonly byte[] counterOut;
|
||||
private byte[] IV;
|
||||
|
||||
/**
|
||||
* Basic constructor.
|
||||
*
|
||||
* @param c the block cipher to be used.
|
||||
*/
|
||||
public FastSicBlockCipher(IBlockCipher cipher)
|
||||
{
|
||||
this.cipher = cipher;
|
||||
this.blockSize = cipher.GetBlockSize();
|
||||
this.counter = new byte[blockSize];
|
||||
this.counterOut = new byte[blockSize];
|
||||
this.IV = new byte[blockSize];
|
||||
}
|
||||
|
||||
/**
|
||||
* return the underlying block cipher that we are wrapping.
|
||||
*
|
||||
* @return the underlying block cipher that we are wrapping.
|
||||
*/
|
||||
public IBlockCipher UnderlyingCipher => cipher;
|
||||
|
||||
public virtual void Init(
|
||||
bool forEncryption, //ignored by this CTR mode
|
||||
ICipherParameters parameters)
|
||||
{
|
||||
ParametersWithIV ivParam = parameters as ParametersWithIV;
|
||||
if (ivParam == null)
|
||||
throw new ArgumentException("CTR/SIC mode requires ParametersWithIV", "parameters");
|
||||
|
||||
this.IV = Arrays.Clone(ivParam.GetIV());
|
||||
|
||||
if (blockSize < IV.Length)
|
||||
throw new ArgumentException("CTR/SIC mode requires IV no greater than: " + blockSize + " bytes.");
|
||||
|
||||
int maxCounterSize = System.Math.Min(8, blockSize / 2);
|
||||
if (blockSize - IV.Length > maxCounterSize)
|
||||
throw new ArgumentException("CTR/SIC mode requires IV of at least: " + (blockSize - maxCounterSize) + " bytes.");
|
||||
|
||||
// if null it's an IV changed only.
|
||||
if (ivParam.Parameters != null)
|
||||
{
|
||||
cipher.Init(true, ivParam.Parameters);
|
||||
}
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
public virtual string AlgorithmName
|
||||
{
|
||||
get { return cipher.AlgorithmName + "/SIC"; }
|
||||
}
|
||||
|
||||
public virtual bool IsPartialBlockOkay
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public virtual int GetBlockSize()
|
||||
{
|
||||
return cipher.GetBlockSize();
|
||||
}
|
||||
|
||||
public virtual int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
|
||||
{
|
||||
cipher.ProcessBlock(counter, 0, counterOut, 0);
|
||||
|
||||
//
|
||||
// XOR the counterOut with the plaintext producing the cipher text
|
||||
//
|
||||
for (int i = 0; i < counterOut.Length; i++)
|
||||
{
|
||||
output[outOff + i] = (byte)(counterOut[i] ^ input[inOff + i]);
|
||||
}
|
||||
|
||||
// Increment the counter
|
||||
int j = counter.Length;
|
||||
while (--j >= 0 && ++counter[j] == 0)
|
||||
{
|
||||
}
|
||||
|
||||
return counter.Length;
|
||||
}
|
||||
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
|
||||
{
|
||||
cipher.ProcessBlock(counter, 0, counterOut, 0);
|
||||
|
||||
//
|
||||
// XOR the counterOut with the plaintext producing the cipher text
|
||||
//
|
||||
for (int i = 0; i < counterOut.Length; i++)
|
||||
{
|
||||
output[i] = (byte)(counterOut[i] ^ input[i]);
|
||||
}
|
||||
|
||||
// Increment the counter
|
||||
int j = counter.Length;
|
||||
while (--j >= 0 && ++counter[j] == 0)
|
||||
{
|
||||
}
|
||||
|
||||
return counter.Length;
|
||||
}
|
||||
#endif
|
||||
|
||||
public virtual void Reset()
|
||||
{
|
||||
Arrays.Fill(counter, (byte)0);
|
||||
Array.Copy(IV, 0, counter, 0, IV.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f774e66db9dd3d54ba629d80531016f2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,499 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
#pragma warning disable
|
||||
using System;
|
||||
using System.IO;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Tls.Crypto.Impl;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Tls.Crypto;
|
||||
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Tls;
|
||||
using BestHTTP.PlatformSupport.Memory;
|
||||
|
||||
namespace BestHTTP.Connections.TLS.Crypto.Impl
|
||||
{
|
||||
/// <summary>A generic TLS 1.2 AEAD cipher.</summary>
|
||||
|
||||
|
||||
|
||||
[BestHTTP.PlatformSupport.IL2CPP.Il2CppEagerStaticClassConstructionAttribute]
|
||||
public sealed class FastTlsAeadCipher
|
||||
: TlsCipher
|
||||
{
|
||||
public const int AEAD_CCM = 1;
|
||||
public const int AEAD_CHACHA20_POLY1305 = 2;
|
||||
public const int AEAD_GCM = 3;
|
||||
|
||||
private const int NONCE_RFC5288 = 1;
|
||||
private const int NONCE_RFC7905 = 2;
|
||||
|
||||
private readonly TlsCryptoParameters m_cryptoParams;
|
||||
private readonly int m_keySize;
|
||||
private readonly int m_macSize;
|
||||
private readonly int m_fixed_iv_length;
|
||||
private readonly int m_record_iv_length;
|
||||
|
||||
private readonly TlsAeadCipherImpl m_decryptCipher, m_encryptCipher;
|
||||
private readonly byte[] m_decryptNonce, m_encryptNonce;
|
||||
|
||||
private readonly bool m_isTlsV13;
|
||||
private readonly int m_nonceMode;
|
||||
|
||||
/// <exception cref="IOException"/>
|
||||
public FastTlsAeadCipher(TlsCryptoParameters cryptoParams, TlsAeadCipherImpl encryptCipher,
|
||||
TlsAeadCipherImpl decryptCipher, int keySize, int macSize, int aeadType)
|
||||
{
|
||||
SecurityParameters securityParameters = cryptoParams.SecurityParameters;
|
||||
ProtocolVersion negotiatedVersion = securityParameters.NegotiatedVersion;
|
||||
|
||||
if (!TlsImplUtilities.IsTlsV12(negotiatedVersion))
|
||||
throw new TlsFatalAlert(AlertDescription.internal_error);
|
||||
|
||||
this.m_isTlsV13 = TlsImplUtilities.IsTlsV13(negotiatedVersion);
|
||||
this.m_nonceMode = GetNonceMode(m_isTlsV13, aeadType);
|
||||
|
||||
switch (m_nonceMode)
|
||||
{
|
||||
case NONCE_RFC5288:
|
||||
this.m_fixed_iv_length = 4;
|
||||
this.m_record_iv_length = 8;
|
||||
break;
|
||||
case NONCE_RFC7905:
|
||||
this.m_fixed_iv_length = 12;
|
||||
this.m_record_iv_length = 0;
|
||||
break;
|
||||
default:
|
||||
throw new TlsFatalAlert(AlertDescription.internal_error);
|
||||
}
|
||||
|
||||
this.m_cryptoParams = cryptoParams;
|
||||
this.m_keySize = keySize;
|
||||
this.m_macSize = macSize;
|
||||
|
||||
this.m_decryptCipher = decryptCipher;
|
||||
this.m_encryptCipher = encryptCipher;
|
||||
|
||||
this.m_decryptNonce = new byte[m_fixed_iv_length];
|
||||
this.m_encryptNonce = new byte[m_fixed_iv_length];
|
||||
|
||||
bool isServer = cryptoParams.IsServer;
|
||||
if (m_isTlsV13)
|
||||
{
|
||||
RekeyCipher(securityParameters, decryptCipher, m_decryptNonce, !isServer);
|
||||
RekeyCipher(securityParameters, encryptCipher, m_encryptNonce, isServer);
|
||||
return;
|
||||
}
|
||||
|
||||
int keyBlockSize = (2 * keySize) + (2 * m_fixed_iv_length);
|
||||
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
Span<byte> keyBlock = keyBlockSize <= 512
|
||||
? stackalloc byte[keyBlockSize]
|
||||
: new byte[keyBlockSize];
|
||||
TlsImplUtilities.CalculateKeyBlock(cryptoParams, keyBlock);
|
||||
|
||||
if (isServer)
|
||||
{
|
||||
decryptCipher.SetKey(keyBlock[..keySize]); keyBlock = keyBlock[keySize..];
|
||||
encryptCipher.SetKey(keyBlock[..keySize]); keyBlock = keyBlock[keySize..];
|
||||
|
||||
keyBlock[..m_fixed_iv_length].CopyTo(m_decryptNonce); keyBlock = keyBlock[m_fixed_iv_length..];
|
||||
keyBlock[..m_fixed_iv_length].CopyTo(m_encryptNonce); keyBlock = keyBlock[m_fixed_iv_length..];
|
||||
}
|
||||
else
|
||||
{
|
||||
encryptCipher.SetKey(keyBlock[..keySize]); keyBlock = keyBlock[keySize..];
|
||||
decryptCipher.SetKey(keyBlock[..keySize]); keyBlock = keyBlock[keySize..];
|
||||
|
||||
keyBlock[..m_fixed_iv_length].CopyTo(m_encryptNonce); keyBlock = keyBlock[m_fixed_iv_length..];
|
||||
keyBlock[..m_fixed_iv_length].CopyTo(m_decryptNonce); keyBlock = keyBlock[m_fixed_iv_length..];
|
||||
}
|
||||
|
||||
if (!keyBlock.IsEmpty)
|
||||
throw new TlsFatalAlert(AlertDescription.internal_error);
|
||||
#else
|
||||
byte[] keyBlock = TlsImplUtilities.CalculateKeyBlock(cryptoParams, keyBlockSize);
|
||||
int pos = 0;
|
||||
|
||||
if (isServer)
|
||||
{
|
||||
decryptCipher.SetKey(keyBlock, pos, keySize); pos += keySize;
|
||||
encryptCipher.SetKey(keyBlock, pos, keySize); pos += keySize;
|
||||
|
||||
Array.Copy(keyBlock, pos, m_decryptNonce, 0, m_fixed_iv_length); pos += m_fixed_iv_length;
|
||||
Array.Copy(keyBlock, pos, m_encryptNonce, 0, m_fixed_iv_length); pos += m_fixed_iv_length;
|
||||
}
|
||||
else
|
||||
{
|
||||
encryptCipher.SetKey(keyBlock, pos, keySize); pos += keySize;
|
||||
decryptCipher.SetKey(keyBlock, pos, keySize); pos += keySize;
|
||||
|
||||
Array.Copy(keyBlock, pos, m_encryptNonce, 0, m_fixed_iv_length); pos += m_fixed_iv_length;
|
||||
Array.Copy(keyBlock, pos, m_decryptNonce, 0, m_fixed_iv_length); pos += m_fixed_iv_length;
|
||||
}
|
||||
|
||||
if (pos != keyBlockSize)
|
||||
throw new TlsFatalAlert(AlertDescription.internal_error);
|
||||
#endif
|
||||
|
||||
int nonceLength = m_fixed_iv_length + m_record_iv_length;
|
||||
|
||||
// NOTE: Ensure dummy nonce is not part of the generated sequence(s)
|
||||
byte[] dummyNonce = new byte[nonceLength];
|
||||
dummyNonce[0] = (byte)~m_encryptNonce[0];
|
||||
dummyNonce[1] = (byte)~m_decryptNonce[1];
|
||||
|
||||
encryptCipher.Init(dummyNonce, macSize, null);
|
||||
decryptCipher.Init(dummyNonce, macSize, null);
|
||||
}
|
||||
|
||||
public int GetCiphertextDecodeLimit(int plaintextLimit)
|
||||
{
|
||||
return plaintextLimit + m_macSize + m_record_iv_length + (m_isTlsV13 ? 1 : 0);
|
||||
}
|
||||
|
||||
public int GetCiphertextEncodeLimit(int plaintextLength, int plaintextLimit)
|
||||
{
|
||||
int innerPlaintextLimit = plaintextLength;
|
||||
if (m_isTlsV13)
|
||||
{
|
||||
// TODO[tls13] Add support for padding
|
||||
int maxPadding = 0;
|
||||
|
||||
innerPlaintextLimit = 1 + System.Math.Min(plaintextLimit, plaintextLength + maxPadding);
|
||||
}
|
||||
|
||||
return innerPlaintextLimit + m_macSize + m_record_iv_length;
|
||||
}
|
||||
|
||||
public int GetPlaintextLimit(int ciphertextLimit)
|
||||
{
|
||||
return ciphertextLimit - m_macSize - m_record_iv_length - (m_isTlsV13 ? 1 : 0);
|
||||
}
|
||||
|
||||
public TlsEncodeResult EncodePlaintext(long seqNo, short contentType, ProtocolVersion recordVersion,
|
||||
int headerAllocation, byte[] plaintext, int plaintextOffset, int plaintextLength)
|
||||
{
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
return EncodePlaintext(seqNo, contentType, recordVersion, headerAllocation,
|
||||
plaintext.AsSpan(plaintextOffset, plaintextLength));
|
||||
#else
|
||||
byte[] nonce = new byte[m_encryptNonce.Length + m_record_iv_length];
|
||||
|
||||
switch (m_nonceMode)
|
||||
{
|
||||
case NONCE_RFC5288:
|
||||
Array.Copy(m_encryptNonce, 0, nonce, 0, m_encryptNonce.Length);
|
||||
// RFC 5288/6655: The nonce_explicit MAY be the 64-bit sequence number.
|
||||
TlsUtilities.WriteUint64(seqNo, nonce, m_encryptNonce.Length);
|
||||
break;
|
||||
case NONCE_RFC7905:
|
||||
TlsUtilities.WriteUint64(seqNo, nonce, nonce.Length - 8);
|
||||
for (int i = 0; i < m_encryptNonce.Length; ++i)
|
||||
{
|
||||
nonce[i] ^= m_encryptNonce[i];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new TlsFatalAlert(AlertDescription.internal_error);
|
||||
}
|
||||
|
||||
int extraLength = m_isTlsV13 ? 1 : 0;
|
||||
|
||||
// TODO[tls13] If we support adding padding to TLSInnerPlaintext, this will need review
|
||||
int encryptionLength = m_encryptCipher.GetOutputSize(plaintextLength + extraLength);
|
||||
int ciphertextLength = m_record_iv_length + encryptionLength;
|
||||
|
||||
byte[] output = BufferPool.Get(headerAllocation + ciphertextLength, true); //new byte[headerAllocation + ciphertextLength];
|
||||
int outputPos = headerAllocation;
|
||||
|
||||
if (m_record_iv_length != 0)
|
||||
{
|
||||
Array.Copy(nonce, nonce.Length - m_record_iv_length, output, outputPos, m_record_iv_length);
|
||||
outputPos += m_record_iv_length;
|
||||
}
|
||||
|
||||
short recordType = m_isTlsV13 ? ContentType.application_data : contentType;
|
||||
|
||||
byte[] additionalData = GetAdditionalData(seqNo, recordType, recordVersion, ciphertextLength,
|
||||
plaintextLength);
|
||||
|
||||
try
|
||||
{
|
||||
Array.Copy(plaintext, plaintextOffset, output, outputPos, plaintextLength);
|
||||
if (m_isTlsV13)
|
||||
{
|
||||
output[outputPos + plaintextLength] = (byte)contentType;
|
||||
}
|
||||
|
||||
m_encryptCipher.Init(nonce, m_macSize, additionalData);
|
||||
outputPos += m_encryptCipher.DoFinal(output, outputPos, plaintextLength + extraLength, output,
|
||||
outputPos);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new TlsFatalAlert(AlertDescription.internal_error, e);
|
||||
}
|
||||
|
||||
if (outputPos != headerAllocation + ciphertextLength)
|
||||
{
|
||||
// NOTE: The additional data mechanism for AEAD ciphers requires exact output size prediction.
|
||||
throw new TlsFatalAlert(AlertDescription.internal_error);
|
||||
}
|
||||
|
||||
return new TlsEncodeResult(output, 0, outputPos, recordType, true);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
public TlsEncodeResult EncodePlaintext(long seqNo, short contentType, ProtocolVersion recordVersion,
|
||||
int headerAllocation, ReadOnlySpan<byte> plaintext)
|
||||
{
|
||||
byte[] nonce = new byte[m_encryptNonce.Length + m_record_iv_length];
|
||||
|
||||
switch (m_nonceMode)
|
||||
{
|
||||
case NONCE_RFC5288:
|
||||
Array.Copy(m_encryptNonce, 0, nonce, 0, m_encryptNonce.Length);
|
||||
// RFC 5288/6655: The nonce_explicit MAY be the 64-bit sequence number.
|
||||
TlsUtilities.WriteUint64(seqNo, nonce, m_encryptNonce.Length);
|
||||
break;
|
||||
case NONCE_RFC7905:
|
||||
TlsUtilities.WriteUint64(seqNo, nonce, nonce.Length - 8);
|
||||
for (int i = 0; i < m_encryptNonce.Length; ++i)
|
||||
{
|
||||
nonce[i] ^= m_encryptNonce[i];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new TlsFatalAlert(AlertDescription.internal_error);
|
||||
}
|
||||
|
||||
int extraLength = m_isTlsV13 ? 1 : 0;
|
||||
|
||||
// TODO[tls13] If we support adding padding to TLSInnerPlaintext, this will need review
|
||||
int encryptionLength = m_encryptCipher.GetOutputSize(plaintext.Length + extraLength);
|
||||
int ciphertextLength = m_record_iv_length + encryptionLength;
|
||||
|
||||
byte[] output = new byte[headerAllocation + ciphertextLength];
|
||||
int outputPos = headerAllocation;
|
||||
|
||||
if (m_record_iv_length != 0)
|
||||
{
|
||||
Array.Copy(nonce, nonce.Length - m_record_iv_length, output, outputPos, m_record_iv_length);
|
||||
outputPos += m_record_iv_length;
|
||||
}
|
||||
|
||||
short recordType = m_isTlsV13 ? ContentType.application_data : contentType;
|
||||
|
||||
byte[] additionalData = GetAdditionalData(seqNo, recordType, recordVersion, ciphertextLength,
|
||||
plaintext.Length);
|
||||
|
||||
try
|
||||
{
|
||||
plaintext.CopyTo(output.AsSpan(outputPos));
|
||||
if (m_isTlsV13)
|
||||
{
|
||||
output[outputPos + plaintext.Length] = (byte)contentType;
|
||||
}
|
||||
|
||||
m_encryptCipher.Init(nonce, m_macSize, additionalData);
|
||||
outputPos += m_encryptCipher.DoFinal(output, outputPos, plaintext.Length + extraLength, output,
|
||||
outputPos);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new TlsFatalAlert(AlertDescription.internal_error, e);
|
||||
}
|
||||
|
||||
if (outputPos != output.Length)
|
||||
{
|
||||
// NOTE: The additional data mechanism for AEAD ciphers requires exact output size prediction.
|
||||
throw new TlsFatalAlert(AlertDescription.internal_error);
|
||||
}
|
||||
|
||||
return new TlsEncodeResult(output, 0, output.Length, recordType);
|
||||
}
|
||||
#endif
|
||||
|
||||
public TlsDecodeResult DecodeCiphertext(long seqNo, short recordType, ProtocolVersion recordVersion,
|
||||
byte[] ciphertext, int ciphertextOffset, int ciphertextLength)
|
||||
{
|
||||
if (GetPlaintextLimit(ciphertextLength) < 0)
|
||||
throw new TlsFatalAlert(AlertDescription.decode_error);
|
||||
|
||||
byte[] nonce = new byte[m_decryptNonce.Length + m_record_iv_length];
|
||||
|
||||
switch (m_nonceMode)
|
||||
{
|
||||
case NONCE_RFC5288:
|
||||
Array.Copy(m_decryptNonce, 0, nonce, 0, m_decryptNonce.Length);
|
||||
Array.Copy(ciphertext, ciphertextOffset, nonce, nonce.Length - m_record_iv_length,
|
||||
m_record_iv_length);
|
||||
break;
|
||||
case NONCE_RFC7905:
|
||||
TlsUtilities.WriteUint64(seqNo, nonce, nonce.Length - 8);
|
||||
for (int i = 0; i < m_decryptNonce.Length; ++i)
|
||||
{
|
||||
nonce[i] ^= m_decryptNonce[i];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new TlsFatalAlert(AlertDescription.internal_error);
|
||||
}
|
||||
|
||||
int encryptionOffset = ciphertextOffset + m_record_iv_length;
|
||||
int encryptionLength = ciphertextLength - m_record_iv_length;
|
||||
int plaintextLength = m_decryptCipher.GetOutputSize(encryptionLength);
|
||||
|
||||
byte[] additionalData = GetAdditionalData(seqNo, recordType, recordVersion, ciphertextLength,
|
||||
plaintextLength);
|
||||
|
||||
int outputPos;
|
||||
try
|
||||
{
|
||||
m_decryptCipher.Init(nonce, m_macSize, additionalData);
|
||||
outputPos = m_decryptCipher.DoFinal(ciphertext, encryptionOffset, encryptionLength, ciphertext,
|
||||
encryptionOffset);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new TlsFatalAlert(AlertDescription.bad_record_mac, e);
|
||||
}
|
||||
|
||||
if (outputPos != plaintextLength)
|
||||
{
|
||||
// NOTE: The additional data mechanism for AEAD ciphers requires exact output size prediction.
|
||||
throw new TlsFatalAlert(AlertDescription.internal_error);
|
||||
}
|
||||
|
||||
short contentType = recordType;
|
||||
if (m_isTlsV13)
|
||||
{
|
||||
// Strip padding and read true content type from TLSInnerPlaintext
|
||||
int pos = plaintextLength;
|
||||
for (; ; )
|
||||
{
|
||||
if (--pos < 0)
|
||||
throw new TlsFatalAlert(AlertDescription.unexpected_message);
|
||||
|
||||
byte octet = ciphertext[encryptionOffset + pos];
|
||||
if (0 != octet)
|
||||
{
|
||||
contentType = (short)(octet & 0xFF);
|
||||
plaintextLength = pos;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new TlsDecodeResult(ciphertext, encryptionOffset, plaintextLength, contentType);
|
||||
}
|
||||
|
||||
public void RekeyDecoder()
|
||||
{
|
||||
RekeyCipher(m_cryptoParams.SecurityParameters, m_decryptCipher, m_decryptNonce, !m_cryptoParams.IsServer);
|
||||
}
|
||||
|
||||
public void RekeyEncoder()
|
||||
{
|
||||
RekeyCipher(m_cryptoParams.SecurityParameters, m_encryptCipher, m_encryptNonce, m_cryptoParams.IsServer);
|
||||
}
|
||||
|
||||
public bool UsesOpaqueRecordType
|
||||
{
|
||||
get { return m_isTlsV13; }
|
||||
}
|
||||
|
||||
private byte[] GetAdditionalData(long seqNo, short recordType, ProtocolVersion recordVersion,
|
||||
int ciphertextLength, int plaintextLength)
|
||||
{
|
||||
if (m_isTlsV13)
|
||||
{
|
||||
/*
|
||||
* TLSCiphertext.opaque_type || TLSCiphertext.legacy_record_version || TLSCiphertext.length
|
||||
*/
|
||||
byte[] additional_data = new byte[5];
|
||||
|
||||
TlsUtilities.WriteUint8(recordType, additional_data, 0);
|
||||
TlsUtilities.WriteVersion(recordVersion, additional_data, 1);
|
||||
TlsUtilities.WriteUint16(ciphertextLength, additional_data, 3);
|
||||
return additional_data;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* seq_num + TLSCompressed.type + TLSCompressed.version + TLSCompressed.length
|
||||
*/
|
||||
byte[] additional_data = new byte[13];
|
||||
|
||||
TlsUtilities.WriteUint64(seqNo, additional_data, 0);
|
||||
TlsUtilities.WriteUint8(recordType, additional_data, 8);
|
||||
TlsUtilities.WriteVersion(recordVersion, additional_data, 9);
|
||||
TlsUtilities.WriteUint16(plaintextLength, additional_data, 11);
|
||||
return additional_data;
|
||||
}
|
||||
}
|
||||
|
||||
private void RekeyCipher(SecurityParameters securityParameters, TlsAeadCipherImpl cipher,
|
||||
byte[] nonce, bool serverSecret)
|
||||
{
|
||||
if (!m_isTlsV13)
|
||||
throw new TlsFatalAlert(AlertDescription.internal_error);
|
||||
|
||||
TlsSecret secret = serverSecret
|
||||
? securityParameters.TrafficSecretServer
|
||||
: securityParameters.TrafficSecretClient;
|
||||
|
||||
// TODO[tls13] For early data, have to disable server->client
|
||||
if (null == secret)
|
||||
throw new TlsFatalAlert(AlertDescription.internal_error);
|
||||
|
||||
Setup13Cipher(cipher, nonce, secret, securityParameters.PrfCryptoHashAlgorithm);
|
||||
}
|
||||
|
||||
private void Setup13Cipher(TlsAeadCipherImpl cipher, byte[] nonce, TlsSecret secret,
|
||||
int cryptoHashAlgorithm)
|
||||
{
|
||||
byte[] key = TlsCryptoUtilities.HkdfExpandLabel(secret, cryptoHashAlgorithm, "key",
|
||||
TlsUtilities.EmptyBytes, m_keySize).Extract();
|
||||
byte[] iv = TlsCryptoUtilities.HkdfExpandLabel(secret, cryptoHashAlgorithm, "iv", TlsUtilities.EmptyBytes,
|
||||
m_fixed_iv_length).Extract();
|
||||
|
||||
cipher.SetKey(key, 0, m_keySize);
|
||||
Array.Copy(iv, 0, nonce, 0, m_fixed_iv_length);
|
||||
|
||||
// NOTE: Ensure dummy nonce is not part of the generated sequence(s)
|
||||
iv[0] ^= 0x80;
|
||||
cipher.Init(iv, m_macSize, null);
|
||||
}
|
||||
|
||||
private static int GetNonceMode(bool isTLSv13, int aeadType)
|
||||
{
|
||||
switch (aeadType)
|
||||
{
|
||||
case AEAD_CCM:
|
||||
case AEAD_GCM:
|
||||
return isTLSv13 ? NONCE_RFC7905 : NONCE_RFC5288;
|
||||
|
||||
case AEAD_CHACHA20_POLY1305:
|
||||
return NONCE_RFC7905;
|
||||
|
||||
default:
|
||||
throw new TlsFatalAlert(AlertDescription.internal_error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a70fdf89e60be4541bbe5027ef19b3c6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,205 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
#pragma warning disable
|
||||
using System;
|
||||
|
||||
using BestHTTP.PlatformSupport.Memory;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Tls;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Tls.Crypto.Impl;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities;
|
||||
|
||||
namespace BestHTTP.Connections.TLS.Crypto.Impl
|
||||
{
|
||||
//public sealed class NoCopyKeyParameter
|
||||
// : ICipherParameters
|
||||
//{
|
||||
// private readonly byte[] key;
|
||||
//
|
||||
// public NoCopyKeyParameter(byte[] key)
|
||||
// :this(key, 0, key.Length)
|
||||
// {
|
||||
// }
|
||||
//
|
||||
// public NoCopyKeyParameter(
|
||||
// byte[] key,
|
||||
// int keyOff,
|
||||
// int keyLen)
|
||||
// {
|
||||
// if (key == null)
|
||||
// throw new ArgumentNullException("key");
|
||||
// if (keyOff < 0 || keyOff > key.Length)
|
||||
// throw new ArgumentOutOfRangeException("keyOff");
|
||||
// if (keyLen < 0 || keyLen > (key.Length - keyOff))
|
||||
// throw new ArgumentOutOfRangeException("keyLen");
|
||||
//
|
||||
// this.key = new byte[keyLen];
|
||||
// Array.Copy(key, keyOff, this.key, 0, keyLen);
|
||||
// }
|
||||
//
|
||||
// public byte[] GetKey()
|
||||
// {
|
||||
// return key;// (byte[])key.Clone();
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//public sealed class FastAeadParameters
|
||||
// : ICipherParameters
|
||||
//{
|
||||
// private readonly byte[] associatedText;
|
||||
// private readonly byte[] nonce;
|
||||
// private readonly NoCopyKeyParameter key;
|
||||
// private readonly int macSize;
|
||||
//
|
||||
// /**
|
||||
// * Base constructor.
|
||||
// *
|
||||
// * @param key key to be used by underlying cipher
|
||||
// * @param macSize macSize in bits
|
||||
// * @param nonce nonce to be used
|
||||
// */
|
||||
// public FastAeadParameters(NoCopyKeyParameter key, int macSize, byte[] nonce)
|
||||
// : this(key, macSize, nonce, null)
|
||||
// {
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Base constructor.
|
||||
// *
|
||||
// * @param key key to be used by underlying cipher
|
||||
// * @param macSize macSize in bits
|
||||
// * @param nonce nonce to be used
|
||||
// * @param associatedText associated text, if any
|
||||
// */
|
||||
// public FastAeadParameters(
|
||||
// NoCopyKeyParameter key,
|
||||
// int macSize,
|
||||
// byte[] nonce,
|
||||
// byte[] associatedText)
|
||||
// {
|
||||
// this.key = key;
|
||||
// this.nonce = nonce;
|
||||
// this.macSize = macSize;
|
||||
// this.associatedText = associatedText;
|
||||
// }
|
||||
//
|
||||
// public NoCopyKeyParameter Key
|
||||
// {
|
||||
// get { return key; }
|
||||
// }
|
||||
//
|
||||
// public int MacSize
|
||||
// {
|
||||
// get { return macSize; }
|
||||
// }
|
||||
//
|
||||
// public byte[] GetAssociatedText()
|
||||
// {
|
||||
// return associatedText;
|
||||
// }
|
||||
//
|
||||
// public byte[] GetNonce()
|
||||
// {
|
||||
// return nonce;
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//public sealed class FastParametersWithIV
|
||||
// : ICipherParameters
|
||||
//{
|
||||
// private readonly ICipherParameters parameters;
|
||||
// private readonly byte[] iv;
|
||||
//
|
||||
// public FastParametersWithIV(ICipherParameters parameters,
|
||||
// byte[] iv)
|
||||
// : this(parameters, iv, 0, iv.Length)
|
||||
// {
|
||||
// }
|
||||
//
|
||||
// public FastParametersWithIV(ICipherParameters parameters,
|
||||
// byte[] iv, int ivOff, int ivLen)
|
||||
// {
|
||||
// // NOTE: 'parameters' may be null to imply key re-use
|
||||
// if (iv == null)
|
||||
// throw new ArgumentNullException("iv");
|
||||
//
|
||||
// this.parameters = parameters;
|
||||
// this.iv = Arrays.CopyOfRange(iv, ivOff, ivOff + ivLen);
|
||||
// }
|
||||
//
|
||||
// public byte[] GetIV()
|
||||
// {
|
||||
// return iv; // (byte[])iv.Clone();
|
||||
// }
|
||||
//
|
||||
// public ICipherParameters Parameters
|
||||
// {
|
||||
// get { return parameters; }
|
||||
// }
|
||||
//}
|
||||
|
||||
internal sealed class FastTlsAeadCipherImpl
|
||||
: TlsAeadCipherImpl
|
||||
{
|
||||
private readonly bool m_isEncrypting;
|
||||
private readonly IAeadCipher m_cipher;
|
||||
|
||||
private KeyParameter key;
|
||||
|
||||
internal FastTlsAeadCipherImpl(IAeadCipher cipher, bool isEncrypting)
|
||||
{
|
||||
this.m_cipher = cipher;
|
||||
this.m_isEncrypting = isEncrypting;
|
||||
}
|
||||
|
||||
public void SetKey(byte[] key, int keyOff, int keyLen)
|
||||
{
|
||||
this.key = new KeyParameter(key, keyOff, keyLen);
|
||||
}
|
||||
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
public void SetKey(ReadOnlySpan<byte> key)
|
||||
{
|
||||
this.key = new KeyParameter(key);
|
||||
}
|
||||
#endif
|
||||
|
||||
public void Init(byte[] nonce, int macSize, byte[] additionalData)
|
||||
{
|
||||
m_cipher.Init(m_isEncrypting, new AeadParameters(key, macSize * 8, nonce, additionalData));
|
||||
}
|
||||
|
||||
public int GetOutputSize(int inputLength)
|
||||
{
|
||||
return m_cipher.GetOutputSize(inputLength);
|
||||
}
|
||||
|
||||
public int DoFinal(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset)
|
||||
{
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
int len = m_cipher.ProcessBytes(input.AsSpan(inputOffset, inputLength), Spans.FromNullable(output, outputOffset));
|
||||
#else
|
||||
int len = m_cipher.ProcessBytes(input, inputOffset, inputLength, output, outputOffset);
|
||||
#endif
|
||||
|
||||
try
|
||||
{
|
||||
len += m_cipher.DoFinal(output, outputOffset + len);
|
||||
}
|
||||
catch (InvalidCipherTextException e)
|
||||
{
|
||||
throw new TlsFatalAlert(AlertDescription.bad_record_mac, e);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
m_cipher.Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6918471f7390c92418f038c9d91d42fd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,535 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
#pragma warning disable
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Utilities;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Tls;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Tls.Crypto;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Tls.Crypto.Impl;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities;
|
||||
|
||||
namespace BestHTTP.Connections.TLS.Crypto.Impl
|
||||
{
|
||||
/// <summary>A generic TLS 1.0-1.2 block cipher. This can be used for AES or 3DES for example.</summary>
|
||||
public class FastTlsBlockCipher
|
||||
: TlsCipher
|
||||
{
|
||||
protected readonly TlsCryptoParameters m_cryptoParams;
|
||||
protected readonly byte[] m_randomData;
|
||||
protected readonly bool m_encryptThenMac;
|
||||
protected readonly bool m_useExplicitIV;
|
||||
protected readonly bool m_acceptExtraPadding;
|
||||
protected readonly bool m_useExtraPadding;
|
||||
|
||||
protected readonly TlsBlockCipherImpl m_decryptCipher, m_encryptCipher;
|
||||
protected readonly TlsSuiteMac m_readMac, m_writeMac;
|
||||
|
||||
/// <exception cref="IOException"/>
|
||||
public FastTlsBlockCipher(TlsCryptoParameters cryptoParams, TlsBlockCipherImpl encryptCipher,
|
||||
TlsBlockCipherImpl decryptCipher, TlsHmac clientMac, TlsHmac serverMac, int cipherKeySize)
|
||||
{
|
||||
SecurityParameters securityParameters = cryptoParams.SecurityParameters;
|
||||
ProtocolVersion negotiatedVersion = securityParameters.NegotiatedVersion;
|
||||
|
||||
if (TlsImplUtilities.IsTlsV13(negotiatedVersion))
|
||||
throw new TlsFatalAlert(AlertDescription.internal_error);
|
||||
|
||||
this.m_cryptoParams = cryptoParams;
|
||||
this.m_randomData = cryptoParams.NonceGenerator.GenerateNonce(256);
|
||||
|
||||
this.m_encryptThenMac = securityParameters.IsEncryptThenMac;
|
||||
this.m_useExplicitIV = TlsImplUtilities.IsTlsV11(negotiatedVersion);
|
||||
|
||||
this.m_acceptExtraPadding = !negotiatedVersion.IsSsl;
|
||||
|
||||
/*
|
||||
* Don't use variable-length padding with truncated MACs.
|
||||
*
|
||||
* See "Tag Size Does Matter: Attacks and Proofs for the TLS Record Protocol", Paterson,
|
||||
* Ristenpart, Shrimpton.
|
||||
*
|
||||
* TODO[DTLS] Consider supporting in DTLS (without exceeding send limit though)
|
||||
*/
|
||||
this.m_useExtraPadding = securityParameters.IsExtendedPadding
|
||||
&& ProtocolVersion.TLSv10.IsEqualOrEarlierVersionOf(negotiatedVersion)
|
||||
&& (m_encryptThenMac || !securityParameters.IsTruncatedHmac);
|
||||
|
||||
this.m_encryptCipher = encryptCipher;
|
||||
this.m_decryptCipher = decryptCipher;
|
||||
|
||||
TlsBlockCipherImpl clientCipher, serverCipher;
|
||||
if (cryptoParams.IsServer)
|
||||
{
|
||||
clientCipher = decryptCipher;
|
||||
serverCipher = encryptCipher;
|
||||
}
|
||||
else
|
||||
{
|
||||
clientCipher = encryptCipher;
|
||||
serverCipher = decryptCipher;
|
||||
}
|
||||
|
||||
int keyBlockSize = (2 * cipherKeySize) + clientMac.MacLength + serverMac.MacLength;
|
||||
|
||||
// From TLS 1.1 onwards, block ciphers don't need IVs from the key_block
|
||||
if (!m_useExplicitIV)
|
||||
{
|
||||
keyBlockSize += clientCipher.GetBlockSize() + serverCipher.GetBlockSize();
|
||||
}
|
||||
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
Span<byte> keyBlock = keyBlockSize <= 512
|
||||
? stackalloc byte[keyBlockSize]
|
||||
: new byte[keyBlockSize];
|
||||
TlsImplUtilities.CalculateKeyBlock(cryptoParams, keyBlock);
|
||||
|
||||
clientMac.SetKey(keyBlock[..clientMac.MacLength]); keyBlock = keyBlock[clientMac.MacLength..];
|
||||
serverMac.SetKey(keyBlock[..serverMac.MacLength]); keyBlock = keyBlock[serverMac.MacLength..];
|
||||
|
||||
clientCipher.SetKey(keyBlock[..cipherKeySize]); keyBlock = keyBlock[cipherKeySize..];
|
||||
serverCipher.SetKey(keyBlock[..cipherKeySize]); keyBlock = keyBlock[cipherKeySize..];
|
||||
|
||||
int clientIVLength = clientCipher.GetBlockSize();
|
||||
int serverIVLength = serverCipher.GetBlockSize();
|
||||
|
||||
if (m_useExplicitIV)
|
||||
{
|
||||
clientCipher.Init(clientIVLength <= 64 ? stackalloc byte[clientIVLength] : new byte[clientIVLength]);
|
||||
serverCipher.Init(serverIVLength <= 64 ? stackalloc byte[serverIVLength] : new byte[serverIVLength]);
|
||||
}
|
||||
else
|
||||
{
|
||||
clientCipher.Init(keyBlock[..clientIVLength]); keyBlock = keyBlock[clientIVLength..];
|
||||
serverCipher.Init(keyBlock[..serverIVLength]); keyBlock = keyBlock[serverIVLength..];
|
||||
}
|
||||
|
||||
if (!keyBlock.IsEmpty)
|
||||
throw new TlsFatalAlert(AlertDescription.internal_error);
|
||||
#else
|
||||
byte[] keyBlock = TlsImplUtilities.CalculateKeyBlock(cryptoParams, keyBlockSize);
|
||||
int pos = 0;
|
||||
|
||||
clientMac.SetKey(keyBlock, pos, clientMac.MacLength);
|
||||
pos += clientMac.MacLength;
|
||||
serverMac.SetKey(keyBlock, pos, serverMac.MacLength);
|
||||
pos += serverMac.MacLength;
|
||||
|
||||
clientCipher.SetKey(keyBlock, pos, cipherKeySize);
|
||||
pos += cipherKeySize;
|
||||
serverCipher.SetKey(keyBlock, pos, cipherKeySize);
|
||||
pos += cipherKeySize;
|
||||
|
||||
int clientIVLength = clientCipher.GetBlockSize();
|
||||
int serverIVLength = serverCipher.GetBlockSize();
|
||||
|
||||
if (m_useExplicitIV)
|
||||
{
|
||||
clientCipher.Init(new byte[clientIVLength], 0, clientIVLength);
|
||||
serverCipher.Init(new byte[serverIVLength], 0, serverIVLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
clientCipher.Init(keyBlock, pos, clientIVLength);
|
||||
pos += clientIVLength;
|
||||
serverCipher.Init(keyBlock, pos, serverIVLength);
|
||||
pos += serverIVLength;
|
||||
}
|
||||
|
||||
if (pos != keyBlockSize)
|
||||
throw new TlsFatalAlert(AlertDescription.internal_error);
|
||||
#endif
|
||||
|
||||
if (cryptoParams.IsServer)
|
||||
{
|
||||
this.m_writeMac = new TlsSuiteHmac(cryptoParams, serverMac);
|
||||
this.m_readMac = new TlsSuiteHmac(cryptoParams, clientMac);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.m_writeMac = new TlsSuiteHmac(cryptoParams, clientMac);
|
||||
this.m_readMac = new TlsSuiteHmac(cryptoParams, serverMac);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual int GetCiphertextDecodeLimit(int plaintextLimit)
|
||||
{
|
||||
int blockSize = m_decryptCipher.GetBlockSize();
|
||||
int macSize = m_readMac.Size;
|
||||
int maxPadding = 256;
|
||||
|
||||
return GetCiphertextLength(blockSize, macSize, maxPadding, plaintextLimit);
|
||||
}
|
||||
|
||||
public virtual int GetCiphertextEncodeLimit(int plaintextLength, int plaintextLimit)
|
||||
{
|
||||
int blockSize = m_encryptCipher.GetBlockSize();
|
||||
int macSize = m_writeMac.Size;
|
||||
int maxPadding = m_useExtraPadding ? 256 : blockSize;
|
||||
|
||||
return GetCiphertextLength(blockSize, macSize, maxPadding, plaintextLength);
|
||||
}
|
||||
|
||||
public virtual int GetPlaintextLimit(int ciphertextLimit)
|
||||
{
|
||||
int blockSize = m_encryptCipher.GetBlockSize();
|
||||
int macSize = m_writeMac.Size;
|
||||
|
||||
int plaintextLimit = ciphertextLimit;
|
||||
|
||||
// Leave room for the MAC, and require block-alignment
|
||||
if (m_encryptThenMac)
|
||||
{
|
||||
plaintextLimit -= macSize;
|
||||
plaintextLimit -= plaintextLimit % blockSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
plaintextLimit -= plaintextLimit % blockSize;
|
||||
plaintextLimit -= macSize;
|
||||
}
|
||||
|
||||
// Minimum 1 byte of padding
|
||||
--plaintextLimit;
|
||||
|
||||
// An explicit IV consumes 1 block
|
||||
if (m_useExplicitIV)
|
||||
{
|
||||
plaintextLimit -= blockSize;
|
||||
}
|
||||
|
||||
return plaintextLimit;
|
||||
}
|
||||
|
||||
public virtual TlsEncodeResult EncodePlaintext(long seqNo, short contentType, ProtocolVersion recordVersion,
|
||||
int headerAllocation, byte[] plaintext, int offset, int len)
|
||||
{
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
return EncodePlaintext(seqNo, contentType, recordVersion, headerAllocation, plaintext.AsSpan(offset, len));
|
||||
#else
|
||||
int blockSize = m_encryptCipher.GetBlockSize();
|
||||
int macSize = m_writeMac.Size;
|
||||
|
||||
int enc_input_length = len;
|
||||
if (!m_encryptThenMac)
|
||||
{
|
||||
enc_input_length += macSize;
|
||||
}
|
||||
|
||||
int padding_length = blockSize - (enc_input_length % blockSize);
|
||||
if (m_useExtraPadding)
|
||||
{
|
||||
// Add a random number of extra blocks worth of padding
|
||||
int maxExtraPadBlocks = (256 - padding_length) / blockSize;
|
||||
int actualExtraPadBlocks = ChooseExtraPadBlocks(maxExtraPadBlocks);
|
||||
padding_length += actualExtraPadBlocks * blockSize;
|
||||
}
|
||||
|
||||
int totalSize = len + macSize + padding_length;
|
||||
if (m_useExplicitIV)
|
||||
{
|
||||
totalSize += blockSize;
|
||||
}
|
||||
|
||||
byte[] outBuf = new byte[headerAllocation + totalSize];
|
||||
int outOff = headerAllocation;
|
||||
|
||||
if (m_useExplicitIV)
|
||||
{
|
||||
// Technically the explicit IV will be the encryption of this nonce
|
||||
byte[] explicitIV = m_cryptoParams.NonceGenerator.GenerateNonce(blockSize);
|
||||
Array.Copy(explicitIV, 0, outBuf, outOff, blockSize);
|
||||
outOff += blockSize;
|
||||
}
|
||||
|
||||
Array.Copy(plaintext, offset, outBuf, outOff, len);
|
||||
outOff += len;
|
||||
|
||||
if (!m_encryptThenMac)
|
||||
{
|
||||
byte[] mac = m_writeMac.CalculateMac(seqNo, contentType, plaintext, offset, len);
|
||||
Array.Copy(mac, 0, outBuf, outOff, mac.Length);
|
||||
outOff += mac.Length;
|
||||
}
|
||||
|
||||
byte padByte = (byte)(padding_length - 1);
|
||||
for (int i = 0; i < padding_length; ++i)
|
||||
{
|
||||
outBuf[outOff++] = padByte;
|
||||
}
|
||||
|
||||
m_encryptCipher.DoFinal(outBuf, headerAllocation, outOff - headerAllocation, outBuf, headerAllocation);
|
||||
|
||||
if (m_encryptThenMac)
|
||||
{
|
||||
byte[] mac = m_writeMac.CalculateMac(seqNo, contentType, outBuf, headerAllocation,
|
||||
outOff - headerAllocation);
|
||||
Array.Copy(mac, 0, outBuf, outOff, mac.Length);
|
||||
outOff += mac.Length;
|
||||
}
|
||||
|
||||
if (outOff != outBuf.Length)
|
||||
throw new TlsFatalAlert(AlertDescription.internal_error);
|
||||
|
||||
return new TlsEncodeResult(outBuf, 0, outBuf.Length, contentType);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
public virtual TlsEncodeResult EncodePlaintext(long seqNo, short contentType, ProtocolVersion recordVersion,
|
||||
int headerAllocation, ReadOnlySpan<byte> plaintext)
|
||||
{
|
||||
int blockSize = m_encryptCipher.GetBlockSize();
|
||||
int macSize = m_writeMac.Size;
|
||||
|
||||
int enc_input_length = plaintext.Length;
|
||||
if (!m_encryptThenMac)
|
||||
{
|
||||
enc_input_length += macSize;
|
||||
}
|
||||
|
||||
int padding_length = blockSize - (enc_input_length % blockSize);
|
||||
if (m_useExtraPadding)
|
||||
{
|
||||
// Add a random number of extra blocks worth of padding
|
||||
int maxExtraPadBlocks = (256 - padding_length) / blockSize;
|
||||
int actualExtraPadBlocks = ChooseExtraPadBlocks(maxExtraPadBlocks);
|
||||
padding_length += actualExtraPadBlocks * blockSize;
|
||||
}
|
||||
|
||||
int totalSize = plaintext.Length + macSize + padding_length;
|
||||
if (m_useExplicitIV)
|
||||
{
|
||||
totalSize += blockSize;
|
||||
}
|
||||
|
||||
byte[] outBuf = new byte[headerAllocation + totalSize];
|
||||
int outOff = headerAllocation;
|
||||
|
||||
if (m_useExplicitIV)
|
||||
{
|
||||
// Technically the explicit IV will be the encryption of this nonce
|
||||
byte[] explicitIV = m_cryptoParams.NonceGenerator.GenerateNonce(blockSize);
|
||||
Array.Copy(explicitIV, 0, outBuf, outOff, blockSize);
|
||||
outOff += blockSize;
|
||||
}
|
||||
|
||||
plaintext.CopyTo(outBuf.AsSpan(outOff));
|
||||
outOff += plaintext.Length;
|
||||
|
||||
if (!m_encryptThenMac)
|
||||
{
|
||||
byte[] mac = m_writeMac.CalculateMac(seqNo, contentType, plaintext);
|
||||
mac.CopyTo(outBuf.AsSpan(outOff));
|
||||
outOff += mac.Length;
|
||||
}
|
||||
|
||||
byte padByte = (byte)(padding_length - 1);
|
||||
for (int i = 0; i < padding_length; ++i)
|
||||
{
|
||||
outBuf[outOff++] = padByte;
|
||||
}
|
||||
|
||||
m_encryptCipher.DoFinal(outBuf, headerAllocation, outOff - headerAllocation, outBuf, headerAllocation);
|
||||
|
||||
if (m_encryptThenMac)
|
||||
{
|
||||
byte[] mac = m_writeMac.CalculateMac(seqNo, contentType, outBuf, headerAllocation,
|
||||
outOff - headerAllocation);
|
||||
Array.Copy(mac, 0, outBuf, outOff, mac.Length);
|
||||
outOff += mac.Length;
|
||||
}
|
||||
|
||||
if (outOff != outBuf.Length)
|
||||
throw new TlsFatalAlert(AlertDescription.internal_error);
|
||||
|
||||
return new TlsEncodeResult(outBuf, 0, outBuf.Length, contentType);
|
||||
}
|
||||
#endif
|
||||
|
||||
public virtual TlsDecodeResult DecodeCiphertext(long seqNo, short recordType, ProtocolVersion recordVersion,
|
||||
byte[] ciphertext, int offset, int len)
|
||||
{
|
||||
int blockSize = m_decryptCipher.GetBlockSize();
|
||||
int macSize = m_readMac.Size;
|
||||
|
||||
int minLen = blockSize;
|
||||
if (m_encryptThenMac)
|
||||
{
|
||||
minLen += macSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
minLen = System.Math.Max(minLen, macSize + 1);
|
||||
}
|
||||
|
||||
if (m_useExplicitIV)
|
||||
{
|
||||
minLen += blockSize;
|
||||
}
|
||||
|
||||
if (len < minLen)
|
||||
throw new TlsFatalAlert(AlertDescription.decode_error);
|
||||
|
||||
int blocks_length = len;
|
||||
if (m_encryptThenMac)
|
||||
{
|
||||
blocks_length -= macSize;
|
||||
}
|
||||
|
||||
if (blocks_length % blockSize != 0)
|
||||
throw new TlsFatalAlert(AlertDescription.decryption_failed);
|
||||
|
||||
if (m_encryptThenMac)
|
||||
{
|
||||
byte[] expectedMac = m_readMac.CalculateMac(seqNo, recordType, ciphertext, offset, len - macSize);
|
||||
|
||||
bool checkMac = TlsUtilities.ConstantTimeAreEqual(macSize, expectedMac, 0, ciphertext,
|
||||
offset + len - macSize);
|
||||
if (!checkMac)
|
||||
{
|
||||
/*
|
||||
* RFC 7366 3. The MAC SHALL be evaluated before any further processing such as
|
||||
* decryption is performed, and if the MAC verification fails, then processing SHALL
|
||||
* terminate immediately. For TLS, a fatal bad_record_mac MUST be generated [2]. For
|
||||
* DTLS, the record MUST be discarded, and a fatal bad_record_mac MAY be generated
|
||||
* [4]. This immediate response to a bad MAC eliminates any timing channels that may
|
||||
* be available through the use of manipulated packet data.
|
||||
*/
|
||||
throw new TlsFatalAlert(AlertDescription.bad_record_mac);
|
||||
}
|
||||
}
|
||||
|
||||
m_decryptCipher.DoFinal(ciphertext, offset, blocks_length, ciphertext, offset);
|
||||
|
||||
if (m_useExplicitIV)
|
||||
{
|
||||
offset += blockSize;
|
||||
blocks_length -= blockSize;
|
||||
}
|
||||
|
||||
// If there's anything wrong with the padding, this will return zero
|
||||
int totalPad = CheckPaddingConstantTime(ciphertext, offset, blocks_length, blockSize,
|
||||
m_encryptThenMac ? 0 : macSize);
|
||||
bool badMac = (totalPad == 0);
|
||||
|
||||
int dec_output_length = blocks_length - totalPad;
|
||||
|
||||
if (!m_encryptThenMac)
|
||||
{
|
||||
dec_output_length -= macSize;
|
||||
|
||||
byte[] expectedMac = m_readMac.CalculateMacConstantTime(seqNo, recordType, ciphertext, offset,
|
||||
dec_output_length, blocks_length - macSize, m_randomData);
|
||||
|
||||
badMac |= !TlsUtilities.ConstantTimeAreEqual(macSize, expectedMac, 0, ciphertext,
|
||||
offset + dec_output_length);
|
||||
}
|
||||
|
||||
if (badMac)
|
||||
throw new TlsFatalAlert(AlertDescription.bad_record_mac);
|
||||
|
||||
return new TlsDecodeResult(ciphertext, offset, dec_output_length, recordType);
|
||||
}
|
||||
|
||||
public virtual void RekeyDecoder()
|
||||
{
|
||||
throw new TlsFatalAlert(AlertDescription.internal_error);
|
||||
}
|
||||
|
||||
public virtual void RekeyEncoder()
|
||||
{
|
||||
throw new TlsFatalAlert(AlertDescription.internal_error);
|
||||
}
|
||||
|
||||
public virtual bool UsesOpaqueRecordType
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
protected virtual int CheckPaddingConstantTime(byte[] buf, int off, int len, int blockSize, int macSize)
|
||||
{
|
||||
int end = off + len;
|
||||
byte lastByte = buf[end - 1];
|
||||
int padlen = lastByte & 0xff;
|
||||
int totalPad = padlen + 1;
|
||||
|
||||
int dummyIndex = 0;
|
||||
byte padDiff = 0;
|
||||
|
||||
int totalPadLimit = System.Math.Min(m_acceptExtraPadding ? 256 : blockSize, len - macSize);
|
||||
|
||||
if (totalPad > totalPadLimit)
|
||||
{
|
||||
totalPad = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
int padPos = end - totalPad;
|
||||
do
|
||||
{
|
||||
padDiff |= (byte)(buf[padPos++] ^ lastByte);
|
||||
}
|
||||
while (padPos < end);
|
||||
|
||||
dummyIndex = totalPad;
|
||||
|
||||
if (padDiff != 0)
|
||||
{
|
||||
totalPad = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Run some extra dummy checks so the number of checks is always constant
|
||||
{
|
||||
byte[] dummyPad = m_randomData;
|
||||
while (dummyIndex < 256)
|
||||
{
|
||||
padDiff |= (byte)(dummyPad[dummyIndex++] ^ lastByte);
|
||||
}
|
||||
// Ensure the above loop is not eliminated
|
||||
dummyPad[0] ^= padDiff;
|
||||
}
|
||||
|
||||
return totalPad;
|
||||
}
|
||||
|
||||
protected virtual int ChooseExtraPadBlocks(int max)
|
||||
{
|
||||
byte[] random = m_cryptoParams.NonceGenerator.GenerateNonce(4);
|
||||
int x = (int)Pack.LE_To_UInt32(random, 0);
|
||||
int n = Integers.NumberOfTrailingZeros(x);
|
||||
return System.Math.Min(n, max);
|
||||
}
|
||||
|
||||
protected virtual int GetCiphertextLength(int blockSize, int macSize, int maxPadding, int plaintextLength)
|
||||
{
|
||||
int ciphertextLength = plaintextLength;
|
||||
|
||||
// An explicit IV consumes 1 block
|
||||
if (m_useExplicitIV)
|
||||
{
|
||||
ciphertextLength += blockSize;
|
||||
}
|
||||
|
||||
// Leave room for the MAC and (block-aligning) padding
|
||||
|
||||
ciphertextLength += maxPadding;
|
||||
|
||||
if (m_encryptThenMac)
|
||||
{
|
||||
ciphertextLength -= (ciphertextLength % blockSize);
|
||||
ciphertextLength += macSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
ciphertextLength += macSize;
|
||||
ciphertextLength -= (ciphertextLength % blockSize);
|
||||
}
|
||||
|
||||
return ciphertextLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 20ab76109ec6a50438d1f788603eeb49
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,68 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
#pragma warning disable
|
||||
using System;
|
||||
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Tls.Crypto.Impl;
|
||||
|
||||
namespace BestHTTP.Connections.TLS.Crypto.Impl
|
||||
{
|
||||
internal sealed class FastTlsBlockCipherImpl
|
||||
: TlsBlockCipherImpl
|
||||
{
|
||||
private readonly bool m_isEncrypting;
|
||||
private readonly IBlockCipher m_cipher;
|
||||
|
||||
private KeyParameter key;
|
||||
|
||||
internal FastTlsBlockCipherImpl(IBlockCipher cipher, bool isEncrypting)
|
||||
{
|
||||
this.m_cipher = cipher;
|
||||
this.m_isEncrypting = isEncrypting;
|
||||
}
|
||||
|
||||
public void SetKey(byte[] key, int keyOff, int keyLen)
|
||||
{
|
||||
this.key = new KeyParameter(key, keyOff, keyLen);
|
||||
}
|
||||
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
public void SetKey(ReadOnlySpan<byte> key)
|
||||
{
|
||||
this.key = new KeyParameter(key);
|
||||
}
|
||||
#endif
|
||||
|
||||
public void Init(byte[] iv, int ivOff, int ivLen)
|
||||
{
|
||||
m_cipher.Init(m_isEncrypting, new ParametersWithIV(key, iv, ivOff, ivLen));
|
||||
}
|
||||
|
||||
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || _UNITY_2021_2_OR_NEWER_
|
||||
public void Init(ReadOnlySpan<byte> iv)
|
||||
{
|
||||
m_cipher.Init(m_isEncrypting, new ParametersWithIV(key, iv));
|
||||
}
|
||||
#endif
|
||||
|
||||
public int DoFinal(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset)
|
||||
{
|
||||
int blockSize = m_cipher.GetBlockSize();
|
||||
|
||||
for (int i = 0; i < inputLength; i += blockSize)
|
||||
{
|
||||
m_cipher.ProcessBlock(input, inputOffset + i, output, outputOffset + i);
|
||||
}
|
||||
|
||||
return inputLength;
|
||||
}
|
||||
|
||||
public int GetBlockSize()
|
||||
{
|
||||
return m_cipher.GetBlockSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 942c0f75d0ed2dc4297fa1c7114d7f16
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,19 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using BestHTTP.Connections.TLS.Crypto;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Security;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Tls;
|
||||
|
||||
namespace BestHTTP.Connections.TLS
|
||||
{
|
||||
public class DefaultTls13Client : AbstractTls13Client
|
||||
{
|
||||
public DefaultTls13Client(HTTPRequest request, List<ServerName> sniServerNames, List<ProtocolName> protocols)
|
||||
: base(request, sniServerNames, protocols, new FastTlsCrypto(new SecureRandom()))
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c429a6a1f0a28754fb08d39cb6314c09
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,75 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Tls;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Tls.Crypto;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Tls.Crypto.Impl;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Encoders;
|
||||
|
||||
// https://www.m00nie.com/2015/05/decrypt-https-ssltls-with-wireshark/
|
||||
// https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format
|
||||
// https://github.com/bcgit/bc-csharp/issues/343
|
||||
|
||||
namespace BestHTTP.Connections.TLS
|
||||
{
|
||||
/// <summary>
|
||||
/// https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format
|
||||
/// </summary>
|
||||
internal enum Labels
|
||||
{
|
||||
CLIENT_RANDOM,
|
||||
CLIENT_EARLY_TRAFFIC_SECRET,
|
||||
CLIENT_HANDSHAKE_TRAFFIC_SECRET,
|
||||
SERVER_HANDSHAKE_TRAFFIC_SECRET,
|
||||
CLIENT_TRAFFIC_SECRET_0,
|
||||
SERVER_TRAFFIC_SECRET_0,
|
||||
EARLY_EXPORTER_SECRET,
|
||||
EXPORTER_SECRET
|
||||
}
|
||||
|
||||
internal static class KeyLogFileWriter
|
||||
{
|
||||
private static string GetKeylogFileName() => Environment.GetEnvironmentVariable("SSLKEYLOGFILE", EnvironmentVariableTarget.User);
|
||||
|
||||
[Conditional("UNITY_EDITOR")]
|
||||
public static void WriteLabel(Labels label, byte[] clientRandom, TlsSecret secret)
|
||||
{
|
||||
if (clientRandom != null && secret != null)
|
||||
{
|
||||
string SSLKEYLOGFILE = GetKeylogFileName();
|
||||
if (!string.IsNullOrEmpty(SSLKEYLOGFILE))
|
||||
using (var writer = new StreamWriter(System.IO.File.Open(SSLKEYLOGFILE, FileMode.Append, FileAccess.Write, FileShare.ReadWrite)))
|
||||
writer.WriteLine($"{label} {Hex.ToHexString(clientRandom)} {Hex.ToHexString((secret as AbstractTlsSecret).CopyData())}");
|
||||
}
|
||||
}
|
||||
|
||||
[Conditional("UNITY_EDITOR")]
|
||||
public static void WriteLabel(Labels label, SecurityParameters securityParameters)
|
||||
{
|
||||
try
|
||||
{
|
||||
TlsSecret secret = null;
|
||||
switch (label)
|
||||
{
|
||||
case Labels.CLIENT_RANDOM: secret = securityParameters.MasterSecret; break;
|
||||
case Labels.CLIENT_HANDSHAKE_TRAFFIC_SECRET: secret = securityParameters.TrafficSecretClient; break;
|
||||
case Labels.SERVER_HANDSHAKE_TRAFFIC_SECRET: secret = securityParameters.TrafficSecretServer; break;
|
||||
case Labels.CLIENT_TRAFFIC_SECRET_0: secret = securityParameters.TrafficSecretClient; break;
|
||||
case Labels.SERVER_TRAFFIC_SECRET_0: secret = securityParameters.TrafficSecretServer; break;
|
||||
case Labels.EXPORTER_SECRET: secret = securityParameters.ExporterMasterSecret; break;
|
||||
|
||||
case Labels.CLIENT_EARLY_TRAFFIC_SECRET: break;
|
||||
case Labels.EARLY_EXPORTER_SECRET: break;
|
||||
}
|
||||
|
||||
if (secret != null)
|
||||
WriteLabel(label, securityParameters.ClientRandom, secret);
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a73956af7ab6d8b46bdc431620279764
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user