I am implementing something in C#, for which I have a separate spec and fairly clear understanding of what I need to do, but at the same time as a reference I have a Java implementation and would like to follow the Java implementation in this case as close as I can.
The code involves an encrypted stream and the Java source is here
The relevant lines are here:
private final StreamCipher enc;
...
BlockCipher cipher;
enc = new SICBlockCipher(cipher = new AESEngine());
enc.init(true, new ParametersWithIV(new KeyParameter(secrets.aes), new byte[cipher.getBlockSize()]));
...
...
byte[] ptype = RLP.encodeInt((int) frame.type); //Result can be a single byte long
...
...
enc.processBytes(ptype, 0, ptype.length, buff, 0);
out.write(buff, 0, ptype.length); //encrypt and write a single byte from the SICBlockCipher stream
The above Java BouncyCastle SicBlockCipher
is a StreamCipher
and allows processing a single or small number of bytes less than the Aes block size.
In c# BouncyCastle the SicBlockCipher
only offers ProcessBlock and BufferedBlockCipher does not seem to offer a way of guaranteeing an output using ProcessBytes.
What do I need to do with C# BouncyCastle library to achieve the equivalent functionality?
Unfortunately the SicBlockCipher
itself is not implemented as a stream cipher, so this functionality is (indeed) not available directly.
BufferedBlockCipher
has been created with many different modes of operation in mind. It buffers the input, while for counter (CTR) mode which SicBlockCipher
implements, you would need to buffer the encrypted counter blocks instead.
The encrypted counter blocks make up the key stream, which can then be XOR'ed with the plaintext to create the cipherstream (or indeed, with the ciphertext to retrieve the plaintext again, encryption is decryption for counter mode).
The only way I see how to do this is to create your own implementation of IBlockCipher
and implement said functionality.
Here is the counter mode as stream cipher...
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Modes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SicStream
{
public class SicStreamCipher : IStreamCipher
{
private SicBlockCipher parent;
private int blockSize;
private byte[] zeroBlock;
private byte[] blockBuffer;
private int processed;
public SicStreamCipher(SicBlockCipher parent)
{
this.parent = parent;
this.blockSize = parent.GetBlockSize();
this.zeroBlock = new byte[blockSize];
this.blockBuffer = new byte[blockSize];
// indicates that no bytes are available: lazy generation of counter blocks (they may not be needed)
this.processed = blockSize;
}
public string AlgorithmName
{
get
{
return parent.AlgorithmName;
}
}
public void Init(bool forEncryption, ICipherParameters parameters)
{
parent.Init(forEncryption, parameters);
Array.Clear(blockBuffer, 0, blockBuffer.Length);
processed = blockSize;
}
public void ProcessBytes(byte[] input, int inOff, int length, byte[] output, int outOff)
{
int inputProcessed = 0;
while (inputProcessed < length)
{
// NOTE can be optimized further
// the number of available bytes can be pre-calculated; too much branching
if (processed == blockSize)
{
// lazilly create a new block of key stream
parent.ProcessBlock(zeroBlock, 0, blockBuffer, 0);
processed = 0;
}
output[outOff + inputProcessed] = (byte)(input[inOff + inputProcessed] ^ blockBuffer[processed]);
processed++;
inputProcessed++;
}
}
public void Reset()
{
parent.Reset();
Array.Clear(blockBuffer, 0, blockBuffer.Length);
this.processed = blockSize;
}
public byte ReturnByte(byte input)
{
if (processed == blockSize)
{
// lazily create a new block of key stream
parent.ProcessBlock(zeroBlock, 0, blockBuffer, 0);
processed = 0;
}
return (byte)(input ^ blockBuffer[processed++]);
}
}
}
... and here it is wrapped so that it can be retrofitted in code where a block cipher mode of operation is used ...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Modes;
namespace SicStream
{
/**
* A class that implements an online Sic (segmented integer counter mode, or just counter (CTR) mode for short).
* This class buffers one encrypted counter (representing the key stream) at a time.
* The encryption of the counter is only performed when required, so that no key stream blocks are generated while they are not required.
*/
public class StreamingSicBlockCipher : BufferedCipherBase
{
private SicStreamCipher parent;
private int blockSize;
public StreamingSicBlockCipher(SicBlockCipher parent)
{
this.parent = new SicStreamCipher(parent);
this.blockSize = parent.GetBlockSize();
}
public override string AlgorithmName
{
get
{
return parent.AlgorithmName;
}
}
public override byte[] DoFinal()
{
// returns no bytes at all, as there is no input
return new byte[0];
}
public override byte[] DoFinal(byte[] input, int inOff, int length)
{
byte[] result = ProcessBytes(input, inOff, length);
Reset();
return result;
}
public override int GetBlockSize()
{
return blockSize;
}
public override int GetOutputSize(int inputLen)
{
return inputLen;
}
public override int GetUpdateOutputSize(int inputLen)
{
return inputLen;
}
public override void Init(bool forEncryption, ICipherParameters parameters)
{
parent.Init(forEncryption, parameters);
}
public override byte[] ProcessByte(byte input)
{
return new byte[] { parent.ReturnByte(input) };
}
public override byte[] ProcessBytes(byte[] input, int inOff, int length)
{
byte[] result = new byte[length];
parent.ProcessBytes(input, inOff, length, result, 0);
return result;
}
public override void Reset()
{
parent.Reset();
}
}
}
Note that the last code is less efficient because of the additional arrays that need to be created.
Having had something of an epiphany about streaming and block ciphers based on Maarten Bodewes helpful and informative answers (many thanks!) I also came up with the below approach.
The .NET BC library has a StreamBlockCipher class, as in Java, but has a guard in its ctor or initializer that the underlying cipher should have a block size of 1.
To use the StreamBlockCipher then, I created a subclass of SicBlockCipher that buffers the block of keystream internally. I named it StreamableSicBlockCipher. It is not yet tested but if there are issues, it at least points the direction to another way.
public class StreamableSicBlockCipher : SicBlockCipher
{
private int blockSize;
private int position = 0;
private byte[] zeroBlock;
private byte[] keyStreamBlock;
public StreamableSicBlockCipher(IBlockCipher cipher) : base(cipher)
{
blockSize=cipher.GetBlockSize();
zeroBlock = new byte[blockSize];
keyStreamBlock = new byte[blockSize];
}
public override int GetBlockSize()
{
return 1;
}
public override int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
{
int keyStreamBlockOffset = position % blockSize;
if (0==keyStreamBlockOffset)
{
var cipher = GetUnderlyingCipher();
cipher.ProcessBlock(zeroBlock, 0, keyStreamBlock, 0);
// Increment the counter
int j = zeroBlock.Length;
while (--j >= 0 && ++zeroBlock[j] == 0)
{
}
}
output[outOff] = (byte)(input[inOff] ^ keyStreamBlock[keyStreamBlockOffset]);
position++;
return 1;
}
public override void Reset()
{
base.Reset();
this.position = 0;
}
It can then be called using the appropriate wrappers as follows:
StreamBlockCipher EncCipher = new StreamBlockCipher(new StreamableSicBlockCipher(new AesEngine()));
Initialisation can be done using an instance of IBlockCipher the get the block size. Example below inits with empty IV, using 'Cipher' which is an instance of AESEngine.:
EncCipher.Init(true, new ParametersWithIV(new KeyParameter(cryptoSecret), new byte[Cipher.GetBlockSize()]));