167 lines
3.5 KiB
C#
167 lines
3.5 KiB
C#
using System;
|
|
|
|
using Org.BouncyCastle.Crypto.Modes;
|
|
using Org.BouncyCastle.Crypto.Parameters;
|
|
using Org.BouncyCastle.Security;
|
|
|
|
namespace 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 void Init(
|
|
bool forWrapping,
|
|
ICipherParameters param)
|
|
{
|
|
this.forWrapping = forWrapping;
|
|
|
|
if (param is ParametersWithRandom)
|
|
{
|
|
ParametersWithRandom p = (ParametersWithRandom) param;
|
|
|
|
this.rand = p.Random;
|
|
this.param = (ParametersWithIV) p.Parameters;
|
|
}
|
|
else
|
|
{
|
|
if (forWrapping)
|
|
{
|
|
rand = new SecureRandom();
|
|
}
|
|
|
|
this.param = (ParametersWithIV) param;
|
|
}
|
|
}
|
|
|
|
public string AlgorithmName
|
|
{
|
|
get { return engine.GetUnderlyingCipher().AlgorithmName + "/RFC3211Wrap"; }
|
|
}
|
|
|
|
public byte[] Wrap(
|
|
byte[] inBytes,
|
|
int inOff,
|
|
int inLen)
|
|
{
|
|
if (!forWrapping)
|
|
{
|
|
throw new InvalidOperationException("not set for wrapping");
|
|
}
|
|
|
|
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;
|
|
cekBlock[1] = (byte)~inBytes[inOff];
|
|
cekBlock[2] = (byte)~inBytes[inOff + 1];
|
|
cekBlock[3] = (byte)~inBytes[inOff + 2];
|
|
|
|
Array.Copy(inBytes, inOff, cekBlock, 4, inLen);
|
|
|
|
rand.NextBytes(cekBlock, inLen + 4, cekBlock.Length - inLen - 4);
|
|
|
|
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 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);
|
|
}
|
|
|
|
if ((cekBlock[0] & 0xff) > cekBlock.Length - 4)
|
|
{
|
|
throw new InvalidCipherTextException("wrapped key corrupted");
|
|
}
|
|
|
|
byte[] key = new byte[cekBlock[0] & 0xff];
|
|
|
|
Array.Copy(cekBlock, 4, key, 0, cekBlock[0]);
|
|
|
|
for (int i = 0; i != 3; i++)
|
|
{
|
|
byte check = (byte)~cekBlock[1 + i];
|
|
if (check != key[i])
|
|
{
|
|
throw new InvalidCipherTextException("wrapped key fails checksum");
|
|
}
|
|
}
|
|
|
|
return key;
|
|
}
|
|
}
|
|
}
|