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.
183 lines
4.4 KiB
183 lines
4.4 KiB
#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
|
|
|