You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
184 lines
4.4 KiB
184 lines
4.4 KiB
11 months ago
|
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||
|
#pragma warning disable
|
||
|
using System;
|
||
|
|
||
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes;
|
||
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
|
||
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Security;
|
||
|
|
||
|
namespace BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Engines
|
||
|
{
|
||
|
/**
|
||
|
* an implementation of the RFC 3211 Key Wrap
|
||
|
* Specification.
|
||
|
*/
|
||
|
public class Rfc3211WrapEngine
|
||
|
: IWrapper
|
||
|
{
|
||
|
private CbcBlockCipher engine;
|
||
|
private ParametersWithIV param;
|
||
|
private bool forWrapping;
|
||
|
private SecureRandom rand;
|
||
|
|
||
|
public Rfc3211WrapEngine(
|
||
|
IBlockCipher engine)
|
||
|
{
|
||
|
this.engine = new CbcBlockCipher(engine);
|
||
|
}
|
||
|
|
||
|
public virtual void Init(
|
||
|
bool forWrapping,
|
||
|
ICipherParameters param)
|
||
|
{
|
||
|
this.forWrapping = forWrapping;
|
||
|
|
||
|
if (param is ParametersWithRandom)
|
||
|
{
|
||
|
ParametersWithRandom p = (ParametersWithRandom)param;
|
||
|
|
||
|
this.rand = p.Random;
|
||
|
this.param = p.Parameters as ParametersWithIV;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (forWrapping)
|
||
|
{
|
||
|
rand = new SecureRandom();
|
||
|
}
|
||
|
|
||
|
this.param = param as ParametersWithIV;
|
||
|
}
|
||
|
|
||
|
if (null == this.param)
|
||
|
throw new ArgumentException("RFC3211Wrap requires an IV", "param");
|
||
|
}
|
||
|
|
||
|
public virtual string AlgorithmName
|
||
|
{
|
||
|
get { return engine.GetUnderlyingCipher().AlgorithmName + "/RFC3211Wrap"; }
|
||
|
}
|
||
|
|
||
|
public virtual byte[] Wrap(
|
||
|
byte[] inBytes,
|
||
|
int inOff,
|
||
|
int inLen)
|
||
|
{
|
||
|
if (!forWrapping)
|
||
|
throw new InvalidOperationException("not set for wrapping");
|
||
|
if (inLen > 255 || inLen < 0)
|
||
|
throw new ArgumentException("input must be from 0 to 255 bytes", "inLen");
|
||
|
|
||
|
engine.Init(true, param);
|
||
|
|
||
|
int blockSize = engine.GetBlockSize();
|
||
|
byte[] cekBlock;
|
||
|
|
||
|
if (inLen + 4 < blockSize * 2)
|
||
|
{
|
||
|
cekBlock = new byte[blockSize * 2];
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
cekBlock = new byte[(inLen + 4) % blockSize == 0 ? inLen + 4 : ((inLen + 4) / blockSize + 1) * blockSize];
|
||
|
}
|
||
|
|
||
|
cekBlock[0] = (byte)inLen;
|
||
|
|
||
|
Array.Copy(inBytes, inOff, cekBlock, 4, inLen);
|
||
|
|
||
|
rand.NextBytes(cekBlock, inLen + 4, cekBlock.Length - inLen - 4);
|
||
|
|
||
|
cekBlock[1] = (byte)~cekBlock[4];
|
||
|
cekBlock[2] = (byte)~cekBlock[4 + 1];
|
||
|
cekBlock[3] = (byte)~cekBlock[4 + 2];
|
||
|
|
||
|
for (int i = 0; i < cekBlock.Length; i += blockSize)
|
||
|
{
|
||
|
engine.ProcessBlock(cekBlock, i, cekBlock, i);
|
||
|
}
|
||
|
|
||
|
for (int i = 0; i < cekBlock.Length; i += blockSize)
|
||
|
{
|
||
|
engine.ProcessBlock(cekBlock, i, cekBlock, i);
|
||
|
}
|
||
|
|
||
|
return cekBlock;
|
||
|
}
|
||
|
|
||
|
public virtual byte[] Unwrap(
|
||
|
byte[] inBytes,
|
||
|
int inOff,
|
||
|
int inLen)
|
||
|
{
|
||
|
if (forWrapping)
|
||
|
{
|
||
|
throw new InvalidOperationException("not set for unwrapping");
|
||
|
}
|
||
|
|
||
|
int blockSize = engine.GetBlockSize();
|
||
|
|
||
|
if (inLen < 2 * blockSize)
|
||
|
{
|
||
|
throw new InvalidCipherTextException("input too short");
|
||
|
}
|
||
|
|
||
|
byte[] cekBlock = new byte[inLen];
|
||
|
byte[] iv = new byte[blockSize];
|
||
|
|
||
|
Array.Copy(inBytes, inOff, cekBlock, 0, inLen);
|
||
|
Array.Copy(inBytes, inOff, iv, 0, iv.Length);
|
||
|
|
||
|
engine.Init(false, new ParametersWithIV(param.Parameters, iv));
|
||
|
|
||
|
for (int i = blockSize; i < cekBlock.Length; i += blockSize)
|
||
|
{
|
||
|
engine.ProcessBlock(cekBlock, i, cekBlock, i);
|
||
|
}
|
||
|
|
||
|
Array.Copy(cekBlock, cekBlock.Length - iv.Length, iv, 0, iv.Length);
|
||
|
|
||
|
engine.Init(false, new ParametersWithIV(param.Parameters, iv));
|
||
|
|
||
|
engine.ProcessBlock(cekBlock, 0, cekBlock, 0);
|
||
|
|
||
|
engine.Init(false, param);
|
||
|
|
||
|
for (int i = 0; i < cekBlock.Length; i += blockSize)
|
||
|
{
|
||
|
engine.ProcessBlock(cekBlock, i, cekBlock, i);
|
||
|
}
|
||
|
|
||
|
bool invalidLength = (int)cekBlock[0] > (cekBlock.Length - 4);
|
||
|
|
||
|
byte[] key;
|
||
|
if (invalidLength)
|
||
|
{
|
||
|
key = new byte[cekBlock.Length - 4];
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
key = new byte[cekBlock[0]];
|
||
|
}
|
||
|
|
||
|
Array.Copy(cekBlock, 4, key, 0, key.Length);
|
||
|
|
||
|
// Note: Using constant time comparison
|
||
|
int nonEqual = 0;
|
||
|
for (int i = 0; i != 3; i++)
|
||
|
{
|
||
|
byte check = (byte)~cekBlock[1 + i];
|
||
|
nonEqual |= (check ^ cekBlock[4 + i]);
|
||
|
}
|
||
|
|
||
|
Array.Clear(cekBlock, 0, cekBlock.Length);
|
||
|
|
||
|
if (nonEqual != 0 | invalidLength)
|
||
|
throw new InvalidCipherTextException("wrapped key corrupted");
|
||
|
|
||
|
return key;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#pragma warning restore
|
||
|
#endif
|