2

In the process of moving from Delphi2007 to XE2, we are thinking about switching encryption libraries from DCPCrypt to Turbopower Lockbox 3.

a) In DCPCrypt I have explicit control over the initialization vector. How would I set the IV in TPLB3?

b) DCPCrypt has no padding, we pad the plaintext with zeroes before encryption. How does TPLB pad? We could still do it ourselves of course.

Test Vector

  • Cipher = AES-256;
  • Chaining mode = CBC;
  • Termination = C# style all-zero-padding;
  • IV transmission = Full block prepended in the clear in the ciphertext stream.
  • Key = 33d46cffa158533194214a91e712fc2b45b587076675affd910edeca5f41ac64 little-endien
  • IV = 917fe226df8308f4d96c33304768354a
  • Ciphertext = +kdTGzdV5KZIw8tv466nhQ== (base64)
  • Plaintext = 'a_decent_text' (ansistring)

Thanks Jan

RRUZ
  • 134,889
  • 20
  • 356
  • 483
Jan Doggen
  • 8,799
  • 13
  • 70
  • 144

1 Answers1

3

a) IV

Let me preface by saying you may be trying to solve a problem that doesn't need solving. Lockbox 3 is auto-salted. That means that under most circumstances a 64 bit nonce is automatically generated and inserted into the IV. The nonce value is transported by means of insertion into the ciphertext stream. The upshot is that you can use the TCodec component without writing single line of code to manage IV's, and you still get all the cryptographic benefits of salting (basically meaning unpredictable IV's). This is in contrast to DCPCrypt, where you either have zeroised IV's or you self-manage the IV's.

I can't imagine a scenario, other than "Known Test Answer" testing that you would want or need to override this behaviour, but having said that, if you really want to insist on setting your own IV's, if you have a cvs client, you can download revision 231 (not yet "stable release" status), and implement the OnSetIV() event handler of the TCodec component to set the IV to your custom value. As the IV is transmitted with the message, this step is not necessary upon decrypt.

Let me know if you want some demo code.

b) Padding (corrected from first post. Sorry for the error.)

It depends on the chaining method. When the standard stream-to-block adapter is used, termination is handled in the following cases.

  • Zero length messages:

Zero length messages are encrypted as zero length ciphertext.

  • ECB mode:

For ECB mode Lockbox 3 uses ISO/IEC 9797-1 method 2 padding. ISO/IEC 9797-1 method 2 essentially is a pad of one byte with value $80 followed by as many zero bytes as required to reach the next block boundary.

  • Not ECB, but message is block aligned:

No padding. No special termination handling is required.

  • Key-streaming mode (such as OFB)

No padding. Termination is handled by truncation.

  • Other (such as CBC)

No padding. Termination is handled by ciphertext stealing, which is "too cool for school"! If the message is too short for ciphertext stealing (less than 2 blocks), then it automatically switches to CFB-8 bit and treat as key-streaming.


UPDATE

Caveat

LockBox 3 was never designed for interoperability with CSharp style user-managed IV and all-zero padding. (IMHO, this type of padding is problematic and should be eschewed).

The following code fragment demonstrates LockBox3 decrypting from the test vector given in the comments. It is assumed that the ciphertext stream is prepended with the full IV and that the encrypting codec uses (yucky) all-zero padding.

procedure TForm5.Button1Click(Sender: TObject);
const
  Key: ansistring = #$33#$d4#$6c#$ff#$a1#$58#$53#$31#$94#$21#$4a#$91#$e7#$12#$fc#$2b +
                       #$45#$b5#$87#$07#$66#$75#$af#$fd#$91#$0e#$de#$ca#$5f#$41#$ac#$64;
  Reference_Plaintext: ansistring = 'a_decent_text';
  IV: ansistring = #$91#$7f#$e2#$26#$df#$83#$08#$f4#$d9#$6c#$33#$30#$47#$68#$35#$4a;
var
  Stream, ReconStream: TStream;
  Cipherb64: ansistring;
  Recon_Plaintext: ansistring;
begin
Stream := TMemoryStream.Create;
Stream.WriteBuffer( Key[1], Length( Key));
Stream.Position := 0;
CryptographicLibrary1.RegisterStreamCipher( StreamToBlock_Adapter_CSharpVariant);
Codec1.StreamCipherId := 'CSharp.StreamToBlock';
Codec1.BlockCipherId  := Format( AES_ProgId, [256]);
Codec1.InitFromStream( Stream);
Stream.Size := 0;
Stream.WriteBuffer( IV[1], Length( IV));
Cipherb64 := '+kdTGzdV5KZIw8tv466nhQ==';
Base64_to_stream( Cipherb64, Stream);
ReconStream := TMemoryStream.Create;
Stream.Position := 0;
Codec1.DecryptStream( ReconStream, Stream);
ReconStream.Position := 0;
SetLength( Recon_Plaintext, ReconStream.Size);
ReconStream.ReadBuffer( Recon_Plaintext[1], Length( Recon_Plaintext));
SetLength( Recon_Plaintext, StrLen( PAnsiChar( Recon_Plaintext)));
ReconStream.Free;
Stream.Free;
if Recon_Plaintext = Reference_Plaintext  then
    ShowMessage( 'Test passed! LockBox3 decrypts from CSharp-style zero padding.')
  else
    ShowMessage( 'Test failed!')
end;

Some points to note:

  1. It is assumed that you have pre-created a TCodec and TCryptographicLibrary (suggestively named), probably on a form at design time.
  2. The TCodec's chaining mode and other properties have already been set at design time. In the case of our test vector, it should be set to CBC .
  3. The given stream-to-block adapter is a special one, not normally included in the cryptographic library. This is why there is a line of code to explicitly register it.
  4. You will need a CVS client to down-load the latest revision from the TurboPower LockBox 3 CVS repository. This adapter is not yet in the official stable release.
  5. Using this adapter, you can only decrypt. CSharp-compatible encryption is not yet available. (Let me know if you need it sooner rather than later).
  6. I tested it on Delphi 2007 and Delphi 2010. It works for me.

Correction

The maxiumum short message length, that is the say the maximum length of plaintext message that will be deemed as too short for classical ciphertext streaming, and so have it's chaining mode treated as a key-streaming one (CFB 8-bit), is one byte less than one block, (not 'less than 2 blocks' as stated earlier). A one and a half block message can still use ciphertext stealing for its block quantisation method. A half block message cannot.

Sean B. Durkin
  • 12,659
  • 1
  • 36
  • 65
  • My IV question comes from the fact that we have to encrypt in C# on the asp.Net platform and decrypt in a Delphi Win32 app. The params on both platforms need to match. – Jan Doggen Jun 04 '12 at 06:40
  • What do you mean that the nonce is inserted into the ciphertext? I need a 'bare' cypher to be able to decryot it on the 'other side' – Jan Doggen Jun 04 '12 at 06:43
  • Give me some more information and I will post some example code on how to control your IV on the Delphi side. Does the Delphi side encrypt or decrypt? What chaining mode do you use? What version of Delphi? Do you control the C# side, or is that written by a third party? – Sean B. Durkin Jun 05 '12 at 00:10
  • The Delphi side decrypts. AES-256 CBC chaining, zero-padding. The C# side concatenates IV+ciphertext, base64 it, and pass that as a parameter to a webservice in Delphi. The Delphi code knows the sizes of IV and cipher, so it can split them and decode. Then the padding ASCII 0s are removed to get the plaintext. The padding does *not* start with a $80 byte as you wrote in your original answer. – Jan Doggen Jun 05 '12 at 08:00
  • The C# encrypts, we have control over that: MemoryStream ms = new MemoryStream(); AesManaged aes = new AesManaged(); aes.Padding = PaddingMode.Zeros; aes.GenerateIV(); aes.Key = -something-; currentIV = aes.IV; CryptoStream cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write); cs.Write(input, 0, input.Length); cs.Close(); return ms.ToArray(); – Jan Doggen Jun 05 '12 at 08:01
  • And this is already production code, so we cannot change the encryption, only the decryption. And thanks. – Jan Doggen Jun 05 '12 at 08:02
  • @JanDoggen could you please supply me with a test vector? A test vector means the combination of a sample IV+ciphertext, the plaintext that it came from, and the key data (32 bytes). You can render your test vector either in base64 or hexadecimal notation, but if using hexadecimal, be sure to specify endieness. Also what version of Delphi are you using? – Sean B. Durkin Jun 05 '12 at 12:46
  • Hello Sean, here's a test sample: Key (little endian hex string): 33d46cffa158533194214a91e712fc2b45b587076675affd910edeca5f41ac64 IV (id): 917fe226df8308f4d96c33304768354a Plaintext: a_decent_text Cipher (raw bytes): 250 71 83 27 55 85 228 166 72 195 203 111 227 174 167 133 Cipher(Base64): +kdTGzdV5KZIw8tv466nhQ== Cipher (hex): fa47531b3755e4a648c3cb6fe3aea785 We currently use Delphi 2007 and are preparing for XE2 (that's why we want to move away from the no longer supported DCPCrypt). – Jan Doggen Jun 06 '12 at 06:56
  • We also use the Known Answers Test (KAT) vectors from NIST http://csrc.nist.gov/groups/STM/cavp/index.html, link to "AES Known Answer Test (KAT) Vectors" (http://csrc.nist.gov/groups/STM/cavp/documents/aes/KAT_AES.zip) These are also little endian hex strings – Jan Doggen Jun 06 '12 at 06:57
  • @JanDoggen Yes LockBox 3 has been using these KATs in the published unit tests, since the inception of LB3. – Sean B. Durkin Jun 06 '12 at 16:04
  • Thank you, Sean! I have tested this with production data on release 232 from the source repository. One remark: you now specify that the IV+CipherText have to be appended in the stream to be decrypted. Isn't that a too specific format, i.e. wouldn't it be better to specify the IV separately? – Jan Doggen Jun 07 '12 at 07:50
  • Perhaps I misunderstood but I thought that prepending was your preference, based on your comment "...The C# side concatenates IV+ciphertext..." . Thanks for the tick. – Sean B. Durkin Jun 07 '12 at 09:31
  • Yes, prepending was my preference, but my comment came from imagining your point of view (code quality) ;-) – Jan Doggen Jun 07 '12 at 13:08