add all
This commit is contained in:
267
Packages/com.tivadar.best.http/Runtime/3rdParty/BouncyCastle/crypto/encodings/ISO9796d1Encoding.cs
vendored
Normal file
267
Packages/com.tivadar.best.http/Runtime/3rdParty/BouncyCastle/crypto/encodings/ISO9796d1Encoding.cs
vendored
Normal file
@@ -0,0 +1,267 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
#pragma warning disable
|
||||
using System;
|
||||
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto;
|
||||
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
|
||||
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Math;
|
||||
|
||||
namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Encodings
|
||||
{
|
||||
/**
|
||||
* ISO 9796-1 padding. Note in the light of recent results you should
|
||||
* only use this with RSA (rather than the "simpler" Rabin keys) and you
|
||||
* should never use it with anything other than a hash (ie. even if the
|
||||
* message is small don't sign the message, sign it's hash) or some "random"
|
||||
* value. See your favorite search engine for details.
|
||||
*/
|
||||
public class ISO9796d1Encoding
|
||||
: IAsymmetricBlockCipher
|
||||
{
|
||||
private static readonly BigInteger Sixteen = BigInteger.ValueOf(16);
|
||||
private static readonly BigInteger Six = BigInteger.ValueOf(6);
|
||||
|
||||
private static readonly byte[] shadows = { 0xe, 0x3, 0x5, 0x8, 0x9, 0x4, 0x2, 0xf,
|
||||
0x0, 0xd, 0xb, 0x6, 0x7, 0xa, 0xc, 0x1 };
|
||||
private static readonly byte[] inverse = { 0x8, 0xf, 0x6, 0x1, 0x5, 0x2, 0xb, 0xc,
|
||||
0x3, 0x4, 0xd, 0xa, 0xe, 0x9, 0x0, 0x7 };
|
||||
|
||||
private readonly IAsymmetricBlockCipher engine;
|
||||
private bool forEncryption;
|
||||
private int bitSize;
|
||||
private int padBits = 0;
|
||||
private BigInteger modulus;
|
||||
|
||||
public ISO9796d1Encoding(IAsymmetricBlockCipher cipher)
|
||||
{
|
||||
this.engine = cipher;
|
||||
}
|
||||
|
||||
public string AlgorithmName => engine.AlgorithmName + "/ISO9796-1Padding";
|
||||
|
||||
public IAsymmetricBlockCipher UnderlyingCipher => engine;
|
||||
|
||||
public void Init(bool forEncryption, ICipherParameters parameters)
|
||||
{
|
||||
RsaKeyParameters kParam;
|
||||
if (parameters is ParametersWithRandom withRandom)
|
||||
{
|
||||
kParam = (RsaKeyParameters)withRandom.Parameters;
|
||||
}
|
||||
else
|
||||
{
|
||||
kParam = (RsaKeyParameters)parameters;
|
||||
}
|
||||
|
||||
engine.Init(forEncryption, parameters);
|
||||
|
||||
modulus = kParam.Modulus;
|
||||
bitSize = modulus.BitLength;
|
||||
|
||||
this.forEncryption = forEncryption;
|
||||
}
|
||||
|
||||
/**
|
||||
* return the input block size. The largest message we can process
|
||||
* is (key_size_in_bits + 3)/16, which in our world comes to
|
||||
* key_size_in_bytes / 2.
|
||||
*/
|
||||
public int GetInputBlockSize()
|
||||
{
|
||||
int baseBlockSize = engine.GetInputBlockSize();
|
||||
|
||||
if (forEncryption)
|
||||
{
|
||||
return (baseBlockSize + 1) / 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
return baseBlockSize;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* return the maximum possible size for the output.
|
||||
*/
|
||||
public int GetOutputBlockSize()
|
||||
{
|
||||
int baseBlockSize = engine.GetOutputBlockSize();
|
||||
|
||||
if (forEncryption)
|
||||
{
|
||||
return baseBlockSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (baseBlockSize + 1) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* set the number of bits in the next message to be treated as
|
||||
* pad bits.
|
||||
*/
|
||||
public void SetPadBits(
|
||||
int padBits)
|
||||
{
|
||||
if (padBits > 7)
|
||||
{
|
||||
throw new ArgumentException("padBits > 7");
|
||||
}
|
||||
|
||||
this.padBits = padBits;
|
||||
}
|
||||
|
||||
/**
|
||||
* retrieve the number of pad bits in the last decoded message.
|
||||
*/
|
||||
public int GetPadBits()
|
||||
{
|
||||
return padBits;
|
||||
}
|
||||
|
||||
public byte[] ProcessBlock(
|
||||
byte[] input,
|
||||
int inOff,
|
||||
int length)
|
||||
{
|
||||
if (forEncryption)
|
||||
{
|
||||
return EncodeBlock(input, inOff, length);
|
||||
}
|
||||
else
|
||||
{
|
||||
return DecodeBlock(input, inOff, length);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] EncodeBlock(
|
||||
byte[] input,
|
||||
int inOff,
|
||||
int inLen)
|
||||
{
|
||||
byte[] block = new byte[(bitSize + 7) / 8];
|
||||
int r = padBits + 1;
|
||||
int z = inLen;
|
||||
int t = (bitSize + 13) / 16;
|
||||
|
||||
for (int i = 0; i < t; i += z)
|
||||
{
|
||||
if (i > t - z)
|
||||
{
|
||||
Array.Copy(input, inOff + inLen - (t - i),
|
||||
block, block.Length - t, t - i);
|
||||
}
|
||||
else
|
||||
{
|
||||
Array.Copy(input, inOff, block, block.Length - (i + z), z);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = block.Length - 2 * t; i != block.Length; i += 2)
|
||||
{
|
||||
byte val = block[block.Length - t + i / 2];
|
||||
|
||||
block[i] = (byte)((shadows[(uint) (val & 0xff) >> 4] << 4)
|
||||
| shadows[val & 0x0f]);
|
||||
block[i + 1] = val;
|
||||
}
|
||||
|
||||
block[block.Length - 2 * z] ^= (byte) r;
|
||||
block[block.Length - 1] = (byte)((block[block.Length - 1] << 4) | 0x06);
|
||||
|
||||
int maxBit = (8 - (bitSize - 1) % 8);
|
||||
int offSet = 0;
|
||||
|
||||
if (maxBit != 8)
|
||||
{
|
||||
block[0] &= (byte) ((ushort) 0xff >> maxBit);
|
||||
block[0] |= (byte) ((ushort) 0x80 >> maxBit);
|
||||
}
|
||||
else
|
||||
{
|
||||
block[0] = 0x00;
|
||||
block[1] |= 0x80;
|
||||
offSet = 1;
|
||||
}
|
||||
|
||||
return engine.ProcessBlock(block, offSet, block.Length - offSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* @exception InvalidCipherTextException if the decrypted block is not a valid ISO 9796 bit string
|
||||
*/
|
||||
private byte[] DecodeBlock(
|
||||
byte[] input,
|
||||
int inOff,
|
||||
int inLen)
|
||||
{
|
||||
byte[] block = engine.ProcessBlock(input, inOff, inLen);
|
||||
int r = 1;
|
||||
int t = (bitSize + 13) / 16;
|
||||
|
||||
BigInteger iS = new BigInteger(1, block);
|
||||
BigInteger iR;
|
||||
if (iS.Mod(Sixteen).Equals(Six))
|
||||
{
|
||||
iR = iS;
|
||||
}
|
||||
else
|
||||
{
|
||||
iR = modulus.Subtract(iS);
|
||||
|
||||
if (!iR.Mod(Sixteen).Equals(Six))
|
||||
throw new InvalidCipherTextException("resulting integer iS or (modulus - iS) is not congruent to 6 mod 16");
|
||||
}
|
||||
|
||||
block = iR.ToByteArrayUnsigned();
|
||||
|
||||
if ((block[block.Length - 1] & 0x0f) != 0x6)
|
||||
throw new InvalidCipherTextException("invalid forcing byte in block");
|
||||
|
||||
block[block.Length - 1] =
|
||||
(byte)(((ushort)(block[block.Length - 1] & 0xff) >> 4)
|
||||
| ((inverse[(block[block.Length - 2] & 0xff) >> 4]) << 4));
|
||||
|
||||
block[0] = (byte)((shadows[(uint) (block[1] & 0xff) >> 4] << 4)
|
||||
| shadows[block[1] & 0x0f]);
|
||||
|
||||
bool boundaryFound = false;
|
||||
int boundary = 0;
|
||||
|
||||
for (int i = block.Length - 1; i >= block.Length - 2 * t; i -= 2)
|
||||
{
|
||||
int val = ((shadows[(uint) (block[i] & 0xff) >> 4] << 4)
|
||||
| shadows[block[i] & 0x0f]);
|
||||
|
||||
if (((block[i - 1] ^ val) & 0xff) != 0)
|
||||
{
|
||||
if (!boundaryFound)
|
||||
{
|
||||
boundaryFound = true;
|
||||
r = (block[i - 1] ^ val) & 0xff;
|
||||
boundary = i - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidCipherTextException("invalid tsums in block");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
block[boundary] = 0;
|
||||
|
||||
byte[] nblock = new byte[(block.Length - boundary) / 2];
|
||||
|
||||
for (int i = 0; i < nblock.Length; i++)
|
||||
{
|
||||
nblock[i] = block[2 * i + boundary + 1];
|
||||
}
|
||||
|
||||
padBits = r - 1;
|
||||
|
||||
return nblock;
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b494ca7cbaa199f48a7c39c7516f49cd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
356
Packages/com.tivadar.best.http/Runtime/3rdParty/BouncyCastle/crypto/encodings/OaepEncoding.cs
vendored
Normal file
356
Packages/com.tivadar.best.http/Runtime/3rdParty/BouncyCastle/crypto/encodings/OaepEncoding.cs
vendored
Normal file
@@ -0,0 +1,356 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
#pragma warning disable
|
||||
using System;
|
||||
|
||||
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Digests;
|
||||
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
|
||||
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Utilities;
|
||||
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Security;
|
||||
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Utilities;
|
||||
|
||||
namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Encodings
|
||||
{
|
||||
/**
|
||||
* Optimal Asymmetric Encryption Padding (OAEP) - see PKCS 1 V 2.
|
||||
*/
|
||||
public class OaepEncoding
|
||||
: IAsymmetricBlockCipher
|
||||
{
|
||||
private byte[] defHash;
|
||||
private IDigest mgf1Hash;
|
||||
|
||||
private IAsymmetricBlockCipher engine;
|
||||
private SecureRandom random;
|
||||
private bool forEncryption;
|
||||
|
||||
public OaepEncoding(
|
||||
IAsymmetricBlockCipher cipher)
|
||||
: this(cipher, new Sha1Digest(), null)
|
||||
{
|
||||
}
|
||||
|
||||
public OaepEncoding(
|
||||
IAsymmetricBlockCipher cipher,
|
||||
IDigest hash)
|
||||
: this(cipher, hash, null)
|
||||
{
|
||||
}
|
||||
|
||||
public OaepEncoding(
|
||||
IAsymmetricBlockCipher cipher,
|
||||
IDigest hash,
|
||||
byte[] encodingParams)
|
||||
: this(cipher, hash, hash, encodingParams)
|
||||
{
|
||||
}
|
||||
|
||||
public OaepEncoding(
|
||||
IAsymmetricBlockCipher cipher,
|
||||
IDigest hash,
|
||||
IDigest mgf1Hash,
|
||||
byte[] encodingParams)
|
||||
{
|
||||
this.engine = cipher;
|
||||
this.mgf1Hash = mgf1Hash;
|
||||
this.defHash = new byte[hash.GetDigestSize()];
|
||||
|
||||
hash.Reset();
|
||||
|
||||
if (encodingParams != null)
|
||||
{
|
||||
hash.BlockUpdate(encodingParams, 0, encodingParams.Length);
|
||||
}
|
||||
|
||||
hash.DoFinal(defHash, 0);
|
||||
}
|
||||
|
||||
public string AlgorithmName => engine.AlgorithmName + "/OAEPPadding";
|
||||
|
||||
public IAsymmetricBlockCipher UnderlyingCipher => engine;
|
||||
|
||||
public void Init(bool forEncryption, ICipherParameters parameters)
|
||||
{
|
||||
if (parameters is ParametersWithRandom withRandom)
|
||||
{
|
||||
this.random = withRandom.Random;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.random = CryptoServicesRegistrar.GetSecureRandom();
|
||||
}
|
||||
|
||||
engine.Init(forEncryption, parameters);
|
||||
|
||||
this.forEncryption = forEncryption;
|
||||
}
|
||||
|
||||
public int GetInputBlockSize()
|
||||
{
|
||||
int baseBlockSize = engine.GetInputBlockSize();
|
||||
|
||||
if (forEncryption)
|
||||
{
|
||||
return baseBlockSize - 1 - 2 * defHash.Length;
|
||||
}
|
||||
else
|
||||
{
|
||||
return baseBlockSize;
|
||||
}
|
||||
}
|
||||
|
||||
public int GetOutputBlockSize()
|
||||
{
|
||||
int baseBlockSize = engine.GetOutputBlockSize();
|
||||
|
||||
if (forEncryption)
|
||||
{
|
||||
return baseBlockSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
return baseBlockSize - 1 - 2 * defHash.Length;
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] ProcessBlock(
|
||||
byte[] inBytes,
|
||||
int inOff,
|
||||
int inLen)
|
||||
{
|
||||
if (forEncryption)
|
||||
{
|
||||
return EncodeBlock(inBytes, inOff, inLen);
|
||||
}
|
||||
else
|
||||
{
|
||||
return DecodeBlock(inBytes, inOff, inLen);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] EncodeBlock(
|
||||
byte[] inBytes,
|
||||
int inOff,
|
||||
int inLen)
|
||||
{
|
||||
Check.DataLength(inLen > GetInputBlockSize(), "input data too long");
|
||||
|
||||
byte[] block = new byte[GetInputBlockSize() + 1 + 2 * defHash.Length];
|
||||
|
||||
//
|
||||
// copy in the message
|
||||
//
|
||||
Array.Copy(inBytes, inOff, block, block.Length - inLen, inLen);
|
||||
|
||||
//
|
||||
// add sentinel
|
||||
//
|
||||
block[block.Length - inLen - 1] = 0x01;
|
||||
|
||||
//
|
||||
// as the block is already zeroed - there's no need to add PS (the >= 0 pad of 0)
|
||||
//
|
||||
|
||||
//
|
||||
// add the hash of the encoding params.
|
||||
//
|
||||
Array.Copy(defHash, 0, block, defHash.Length, defHash.Length);
|
||||
|
||||
//
|
||||
// generate the seed.
|
||||
//
|
||||
byte[] seed = SecureRandom.GetNextBytes(random, defHash.Length);
|
||||
|
||||
//
|
||||
// mask the message block.
|
||||
//
|
||||
byte[] mask = MaskGeneratorFunction(seed, 0, seed.Length, block.Length - defHash.Length);
|
||||
|
||||
for (int i = defHash.Length; i != block.Length; i++)
|
||||
{
|
||||
block[i] ^= mask[i - defHash.Length];
|
||||
}
|
||||
|
||||
//
|
||||
// add in the seed
|
||||
//
|
||||
Array.Copy(seed, 0, block, 0, defHash.Length);
|
||||
|
||||
//
|
||||
// mask the seed.
|
||||
//
|
||||
mask = MaskGeneratorFunction(
|
||||
block, defHash.Length, block.Length - defHash.Length, defHash.Length);
|
||||
|
||||
for (int i = 0; i != defHash.Length; i++)
|
||||
{
|
||||
block[i] ^= mask[i];
|
||||
}
|
||||
|
||||
return engine.ProcessBlock(block, 0, block.Length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @exception InvalidCipherTextException if the decrypted block turns out to
|
||||
* be badly formatted.
|
||||
*/
|
||||
private byte[] DecodeBlock(
|
||||
byte[] inBytes,
|
||||
int inOff,
|
||||
int inLen)
|
||||
{
|
||||
byte[] data = engine.ProcessBlock(inBytes, inOff, inLen);
|
||||
byte[] block = new byte[engine.GetOutputBlockSize()];
|
||||
|
||||
//
|
||||
// as we may have zeros in our leading bytes for the block we produced
|
||||
// on encryption, we need to make sure our decrypted block comes back
|
||||
// the same size.
|
||||
//
|
||||
// i.e. wrong when block.length < (2 * defHash.length) + 1
|
||||
int wrongMask = (block.Length - ((2 * defHash.Length) + 1)) >> 31;
|
||||
|
||||
if (data.Length <= block.Length)
|
||||
{
|
||||
Array.Copy(data, 0, block, block.Length - data.Length, data.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
Array.Copy(data, 0, block, 0, block.Length);
|
||||
wrongMask |= 1;
|
||||
}
|
||||
|
||||
//
|
||||
// unmask the seed.
|
||||
//
|
||||
byte[] mask = MaskGeneratorFunction(
|
||||
block, defHash.Length, block.Length - defHash.Length, defHash.Length);
|
||||
|
||||
for (int i = 0; i != defHash.Length; i++)
|
||||
{
|
||||
block[i] ^= mask[i];
|
||||
}
|
||||
|
||||
//
|
||||
// unmask the message block.
|
||||
//
|
||||
mask = MaskGeneratorFunction(block, 0, defHash.Length, block.Length - defHash.Length);
|
||||
|
||||
for (int i = defHash.Length; i != block.Length; i++)
|
||||
{
|
||||
block[i] ^= mask[i - defHash.Length];
|
||||
}
|
||||
|
||||
//
|
||||
// check the hash of the encoding params.
|
||||
// long check to try to avoid this been a source of a timing attack.
|
||||
//
|
||||
for (int i = 0; i != defHash.Length; i++)
|
||||
{
|
||||
wrongMask |= defHash[i] ^ block[defHash.Length + i];
|
||||
}
|
||||
|
||||
//
|
||||
// find the data block
|
||||
//
|
||||
int start = -1;
|
||||
|
||||
for (int index = 2 * defHash.Length; index != block.Length; index++)
|
||||
{
|
||||
int octet = block[index];
|
||||
|
||||
// i.e. mask will be 0xFFFFFFFF if octet is non-zero and start is (still) negative, else 0.
|
||||
int shouldSetMask = (-octet & start) >> 31;
|
||||
|
||||
start += index & shouldSetMask;
|
||||
}
|
||||
|
||||
wrongMask |= start >> 31;
|
||||
++start;
|
||||
wrongMask |= block[start] ^ 1;
|
||||
|
||||
if (wrongMask != 0)
|
||||
{
|
||||
Arrays.Fill(block, 0);
|
||||
throw new InvalidCipherTextException("data wrong");
|
||||
}
|
||||
|
||||
++start;
|
||||
|
||||
//
|
||||
// extract the data block
|
||||
//
|
||||
byte[] output = new byte[block.Length - start];
|
||||
|
||||
Array.Copy(block, start, output, 0, output.Length);
|
||||
Array.Clear(block, 0, block.Length);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
private byte[] MaskGeneratorFunction(
|
||||
byte[] Z,
|
||||
int zOff,
|
||||
int zLen,
|
||||
int length)
|
||||
{
|
||||
if (mgf1Hash is IXof)
|
||||
{
|
||||
byte[] mask = new byte[length];
|
||||
mgf1Hash.BlockUpdate(Z, zOff, zLen);
|
||||
((IXof)mgf1Hash).OutputFinal(mask, 0, mask.Length);
|
||||
|
||||
return mask;
|
||||
}
|
||||
else
|
||||
{
|
||||
return MaskGeneratorFunction1(Z, zOff, zLen, length);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* mask generator function, as described in PKCS1v2.
|
||||
*/
|
||||
private byte[] MaskGeneratorFunction1(
|
||||
byte[] Z,
|
||||
int zOff,
|
||||
int zLen,
|
||||
int length)
|
||||
{
|
||||
byte[] mask = new byte[length];
|
||||
byte[] hashBuf = new byte[mgf1Hash.GetDigestSize()];
|
||||
byte[] C = new byte[4];
|
||||
int counter = 0;
|
||||
|
||||
mgf1Hash.Reset();
|
||||
|
||||
while (counter < (length / hashBuf.Length))
|
||||
{
|
||||
Pack.UInt32_To_BE((uint)counter, C);
|
||||
|
||||
mgf1Hash.BlockUpdate(Z, zOff, zLen);
|
||||
mgf1Hash.BlockUpdate(C, 0, C.Length);
|
||||
mgf1Hash.DoFinal(hashBuf, 0);
|
||||
|
||||
Array.Copy(hashBuf, 0, mask, counter * hashBuf.Length, hashBuf.Length);
|
||||
|
||||
counter++;
|
||||
}
|
||||
|
||||
if ((counter * hashBuf.Length) < length)
|
||||
{
|
||||
Pack.UInt32_To_BE((uint)counter, C);
|
||||
|
||||
mgf1Hash.BlockUpdate(Z, zOff, zLen);
|
||||
mgf1Hash.BlockUpdate(C, 0, C.Length);
|
||||
mgf1Hash.DoFinal(hashBuf, 0);
|
||||
|
||||
Array.Copy(hashBuf, 0, mask, counter * hashBuf.Length, mask.Length - (counter * hashBuf.Length));
|
||||
}
|
||||
|
||||
return mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning restore
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 873572ed0f1356b488df46595d586784
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
380
Packages/com.tivadar.best.http/Runtime/3rdParty/BouncyCastle/crypto/encodings/Pkcs1Encoding.cs
vendored
Normal file
380
Packages/com.tivadar.best.http/Runtime/3rdParty/BouncyCastle/crypto/encodings/Pkcs1Encoding.cs
vendored
Normal file
@@ -0,0 +1,380 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
#pragma warning disable
|
||||
using System;
|
||||
|
||||
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
|
||||
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Digests;
|
||||
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Security;
|
||||
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Utilities;
|
||||
|
||||
namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Encodings
|
||||
{
|
||||
/**
|
||||
* this does your basic Pkcs 1 v1.5 padding - whether or not you should be using this
|
||||
* depends on your application - see Pkcs1 Version 2 for details.
|
||||
*/
|
||||
public class Pkcs1Encoding
|
||||
: IAsymmetricBlockCipher
|
||||
{
|
||||
/**
|
||||
* some providers fail to include the leading zero in PKCS1 encoded blocks. If you need to
|
||||
* work with one of these set the system property Best.HTTP.SecureProtocol.Org.BouncyCastle.Pkcs1.Strict to false.
|
||||
*/
|
||||
public const string StrictLengthEnabledProperty = "Best.HTTP.SecureProtocol.Org.BouncyCastle.Pkcs1.Strict";
|
||||
|
||||
private const int HeaderLength = 10;
|
||||
|
||||
/**
|
||||
* The same effect can be achieved by setting the static property directly
|
||||
* <p>
|
||||
* The static property is checked during construction of the encoding object, it is set to
|
||||
* true by default.
|
||||
* </p>
|
||||
*/
|
||||
public static bool StrictLengthEnabled
|
||||
{
|
||||
get { return strictLengthEnabled[0]; }
|
||||
set { strictLengthEnabled[0] = value; }
|
||||
}
|
||||
|
||||
private static readonly bool[] strictLengthEnabled;
|
||||
|
||||
static Pkcs1Encoding()
|
||||
{
|
||||
string strictProperty = Org.BouncyCastle.Utilities.Platform.GetEnvironmentVariable(StrictLengthEnabledProperty);
|
||||
|
||||
strictLengthEnabled = new bool[]{ strictProperty == null || Org.BouncyCastle.Utilities.Platform.EqualsIgnoreCase("true", strictProperty) };
|
||||
}
|
||||
|
||||
|
||||
private SecureRandom random;
|
||||
private IAsymmetricBlockCipher engine;
|
||||
private bool forEncryption;
|
||||
private bool forPrivateKey;
|
||||
private bool useStrictLength;
|
||||
private int pLen = -1;
|
||||
private byte[] fallback = null;
|
||||
private byte[] blockBuffer = null;
|
||||
|
||||
/**
|
||||
* Basic constructor.
|
||||
*
|
||||
* @param cipher
|
||||
*/
|
||||
public Pkcs1Encoding(
|
||||
IAsymmetricBlockCipher cipher)
|
||||
{
|
||||
this.engine = cipher;
|
||||
this.useStrictLength = StrictLengthEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for decryption with a fixed plaintext length.
|
||||
*
|
||||
* @param cipher The cipher to use for cryptographic operation.
|
||||
* @param pLen Length of the expected plaintext.
|
||||
*/
|
||||
public Pkcs1Encoding(IAsymmetricBlockCipher cipher, int pLen)
|
||||
{
|
||||
this.engine = cipher;
|
||||
this.useStrictLength = StrictLengthEnabled;
|
||||
this.pLen = pLen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for decryption with a fixed plaintext length and a fallback
|
||||
* value that is returned, if the padding is incorrect.
|
||||
*
|
||||
* @param cipher
|
||||
* The cipher to use for cryptographic operation.
|
||||
* @param fallback
|
||||
* The fallback value, we don't to a arraycopy here.
|
||||
*/
|
||||
public Pkcs1Encoding(IAsymmetricBlockCipher cipher, byte[] fallback)
|
||||
{
|
||||
this.engine = cipher;
|
||||
this.useStrictLength = StrictLengthEnabled;
|
||||
this.fallback = fallback;
|
||||
this.pLen = fallback.Length;
|
||||
}
|
||||
|
||||
public string AlgorithmName => engine.AlgorithmName + "/PKCS1Padding";
|
||||
|
||||
public IAsymmetricBlockCipher UnderlyingCipher => engine;
|
||||
|
||||
public void Init(bool forEncryption, ICipherParameters parameters)
|
||||
{
|
||||
AsymmetricKeyParameter kParam;
|
||||
if (parameters is ParametersWithRandom withRandom)
|
||||
{
|
||||
this.random = withRandom.Random;
|
||||
kParam = (AsymmetricKeyParameter)withRandom.Parameters;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.random = CryptoServicesRegistrar.GetSecureRandom();
|
||||
kParam = (AsymmetricKeyParameter)parameters;
|
||||
}
|
||||
|
||||
engine.Init(forEncryption, parameters);
|
||||
|
||||
this.forPrivateKey = kParam.IsPrivate;
|
||||
this.forEncryption = forEncryption;
|
||||
this.blockBuffer = new byte[engine.GetOutputBlockSize()];
|
||||
|
||||
if (pLen > 0 && fallback == null && random == null)
|
||||
throw new ArgumentException("encoder requires random");
|
||||
}
|
||||
|
||||
public int GetInputBlockSize()
|
||||
{
|
||||
int baseBlockSize = engine.GetInputBlockSize();
|
||||
|
||||
return forEncryption
|
||||
? baseBlockSize - HeaderLength
|
||||
: baseBlockSize;
|
||||
}
|
||||
|
||||
public int GetOutputBlockSize()
|
||||
{
|
||||
int baseBlockSize = engine.GetOutputBlockSize();
|
||||
|
||||
return forEncryption
|
||||
? baseBlockSize
|
||||
: baseBlockSize - HeaderLength;
|
||||
}
|
||||
|
||||
public byte[] ProcessBlock(
|
||||
byte[] input,
|
||||
int inOff,
|
||||
int length)
|
||||
{
|
||||
return forEncryption
|
||||
? EncodeBlock(input, inOff, length)
|
||||
: DecodeBlock(input, inOff, length);
|
||||
}
|
||||
|
||||
private byte[] EncodeBlock(
|
||||
byte[] input,
|
||||
int inOff,
|
||||
int inLen)
|
||||
{
|
||||
if (inLen > GetInputBlockSize())
|
||||
throw new ArgumentException("input data too large", "inLen");
|
||||
|
||||
byte[] block = new byte[engine.GetInputBlockSize()];
|
||||
|
||||
if (forPrivateKey)
|
||||
{
|
||||
block[0] = 0x01; // type code 1
|
||||
|
||||
for (int i = 1; i != block.Length - inLen - 1; i++)
|
||||
{
|
||||
block[i] = (byte)0xFF;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
random.NextBytes(block); // random fill
|
||||
|
||||
block[0] = 0x02; // type code 2
|
||||
|
||||
//
|
||||
// a zero byte marks the end of the padding, so all
|
||||
// the pad bytes must be non-zero.
|
||||
//
|
||||
for (int i = 1; i != block.Length - inLen - 1; i++)
|
||||
{
|
||||
while (block[i] == 0)
|
||||
{
|
||||
block[i] = (byte)random.NextInt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
block[block.Length - inLen - 1] = 0x00; // mark the end of the padding
|
||||
Array.Copy(input, inOff, block, block.Length - inLen, inLen);
|
||||
|
||||
return engine.ProcessBlock(block, 0, block.Length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the argument is a correctly PKCS#1.5 encoded Plaintext
|
||||
* for encryption.
|
||||
*
|
||||
* @param encoded The Plaintext.
|
||||
* @param pLen Expected length of the plaintext.
|
||||
* @return Either 0, if the encoding is correct, or -1, if it is incorrect.
|
||||
*/
|
||||
private static int CheckPkcs1Encoding(byte[] encoded, int pLen)
|
||||
{
|
||||
int correct = 0;
|
||||
/*
|
||||
* Check if the first two bytes are 0 2
|
||||
*/
|
||||
correct |= (encoded[0] ^ 2);
|
||||
|
||||
/*
|
||||
* Now the padding check, check for no 0 byte in the padding
|
||||
*/
|
||||
int plen = encoded.Length - (
|
||||
pLen /* Length of the PMS */
|
||||
+ 1 /* Final 0-byte before PMS */
|
||||
);
|
||||
|
||||
for (int i = 1; i < plen; i++)
|
||||
{
|
||||
int tmp = encoded[i];
|
||||
tmp |= tmp >> 1;
|
||||
tmp |= tmp >> 2;
|
||||
tmp |= tmp >> 4;
|
||||
correct |= (tmp & 1) - 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure the padding ends with a 0 byte.
|
||||
*/
|
||||
correct |= encoded[encoded.Length - (pLen + 1)];
|
||||
|
||||
/*
|
||||
* Return 0 or 1, depending on the result.
|
||||
*/
|
||||
correct |= correct >> 1;
|
||||
correct |= correct >> 2;
|
||||
correct |= correct >> 4;
|
||||
return ~((correct & 1) - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode PKCS#1.5 encoding, and return a random value if the padding is not correct.
|
||||
*
|
||||
* @param in The encrypted block.
|
||||
* @param inOff Offset in the encrypted block.
|
||||
* @param inLen Length of the encrypted block.
|
||||
* @param pLen Length of the desired output.
|
||||
* @return The plaintext without padding, or a random value if the padding was incorrect.
|
||||
* @throws InvalidCipherTextException
|
||||
*/
|
||||
private byte[] DecodeBlockOrRandom(byte[] input, int inOff, int inLen)
|
||||
{
|
||||
if (!forPrivateKey)
|
||||
throw new InvalidCipherTextException("sorry, this method is only for decryption, not for signing");
|
||||
|
||||
byte[] block = engine.ProcessBlock(input, inOff, inLen);
|
||||
byte[] random;
|
||||
if (this.fallback == null)
|
||||
{
|
||||
random = new byte[this.pLen];
|
||||
this.random.NextBytes(random);
|
||||
}
|
||||
else
|
||||
{
|
||||
random = fallback;
|
||||
}
|
||||
|
||||
byte[] data = (useStrictLength & (block.Length != engine.GetOutputBlockSize())) ? blockBuffer : block;
|
||||
|
||||
/*
|
||||
* Check the padding.
|
||||
*/
|
||||
int correct = CheckPkcs1Encoding(data, this.pLen);
|
||||
|
||||
/*
|
||||
* Now, to a constant time constant memory copy of the decrypted value
|
||||
* or the random value, depending on the validity of the padding.
|
||||
*/
|
||||
byte[] result = new byte[this.pLen];
|
||||
for (int i = 0; i < this.pLen; i++)
|
||||
{
|
||||
result[i] = (byte)((data[i + (data.Length - pLen)] & (~correct)) | (random[i] & correct));
|
||||
}
|
||||
|
||||
Arrays.Fill(data, 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @exception InvalidCipherTextException if the decrypted block is not in Pkcs1 format.
|
||||
*/
|
||||
private byte[] DecodeBlock(
|
||||
byte[] input,
|
||||
int inOff,
|
||||
int inLen)
|
||||
{
|
||||
/*
|
||||
* If the length of the expected plaintext is known, we use a constant-time decryption.
|
||||
* If the decryption fails, we return a random value.
|
||||
*/
|
||||
if (this.pLen != -1)
|
||||
{
|
||||
return this.DecodeBlockOrRandom(input, inOff, inLen);
|
||||
}
|
||||
|
||||
byte[] block = engine.ProcessBlock(input, inOff, inLen);
|
||||
bool incorrectLength = (useStrictLength & (block.Length != engine.GetOutputBlockSize()));
|
||||
|
||||
byte[] data;
|
||||
if (block.Length < GetOutputBlockSize())
|
||||
{
|
||||
data = blockBuffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
data = block;
|
||||
}
|
||||
|
||||
byte expectedType = (byte)(forPrivateKey ? 2 : 1);
|
||||
byte type = data[0];
|
||||
|
||||
bool badType = (type != expectedType);
|
||||
|
||||
//
|
||||
// find and extract the message block.
|
||||
//
|
||||
int start = FindStart(type, data);
|
||||
|
||||
start++; // data should start at the next byte
|
||||
|
||||
if (badType | (start < HeaderLength))
|
||||
{
|
||||
Arrays.Fill(data, 0);
|
||||
throw new InvalidCipherTextException("block incorrect");
|
||||
}
|
||||
|
||||
// if we get this far, it's likely to be a genuine encoding error
|
||||
if (incorrectLength)
|
||||
{
|
||||
Arrays.Fill(data, 0);
|
||||
throw new InvalidCipherTextException("block incorrect size");
|
||||
}
|
||||
|
||||
byte[] result = new byte[data.Length - start];
|
||||
|
||||
Array.Copy(data, start, result, 0, result.Length);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private int FindStart(byte type, byte[] block)
|
||||
{
|
||||
int start = -1;
|
||||
bool padErr = false;
|
||||
|
||||
for (int i = 1; i != block.Length; i++)
|
||||
{
|
||||
byte pad = block[i];
|
||||
|
||||
if (pad == 0 & start < 0)
|
||||
{
|
||||
start = i;
|
||||
}
|
||||
padErr |= ((type == 1) & (start < 0) & (pad != (byte)0xff));
|
||||
}
|
||||
|
||||
return padErr ? -1 : start;
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 849a220cd89229e4d985db5a20dd7598
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user