Turbopower Lockbox3 - Can I control initialization vector and padding for AES-256 encryption? Request for encryption equivalent

1.6k Views Asked by At

Is there an equivalent encryption procedure for the given code below. The current implementation does not support encryption and throws a 'Feature not supported.' exception.

The routine below successfully decrypted this cipher 'G6DR0607ZXRu24envBxPRA==' to its plain text value '1'. I need to reverse the process and get the same given cipher.

I hope someone can help me provide a working equivalent code to encrypt a plain text value, using the same procedure found in the DecryptAES256 routine.

const
  Key: array of byte = [$61,$A6,$02,$D1,$E6,$89,$87,$A3,$7C,$0B,$54,$D2,$64,$7D,
                        $B9,$41,$D0,$E6,$56,$DE,$CF,$A2,$5B,$6C,$76,$4A,$BB,$FA,
                        $DB,$CD,$41,$2D];
  IV: array of byte  = [$86,$78,$1C,$D2,$66,$91,$F7,$91,$3B,$2A,$44,$10,$DF,$38,
                        $E4,$47];

function DecryptAES256(const Value: String): String;
var
  Codec: TCodec;
  CryptographicLibrary: TCryptographicLibrary;
  CipherStream, PlainTextStream: TStream;
  buffer: array of Byte;
begin
  Codec                          := TCodec.Create( nil);
  CryptographicLibrary           := TCryptographicLibrary.Create( nil);

  CipherStream                   := TMemoryStream.Create;
  PlainTextStream                := TMemoryStream.Create;

  try
    Codec.CryptoLibrary          := CryptographicLibrary;
    CryptographicLibrary.RegisterStreamCipher(StreamToBlock_Adapter_CSharpVariant);
    Codec.StreamCipherId         := 'CSharp.StreamToBlock';
    Codec.BlockCipherId          := Format( AES_ProgId, [256]);
    Codec.AsymetricKeySizeInBits := 256;
    Codec.Cipher                 := '[AES-256*]';
    Codec.ChainModeId            := CBC_ProgId;

    CipherStream.WriteBuffer( Key[0], Length(Key));
    CipherStream.Position        := 0;
    Codec.InitFromStream(CipherStream);
    CipherStream.Size            := 0;
    CipherStream.WriteBuffer( IV[0], Length(IV));
    Base64_to_stream(Value, CipherStream);
    CipherStream.Position        := 0;
    Codec.DecryptStream(PlainTextStream, CipherStream);
    PlainTextStream.Position     := 0;
    SetLength(buffer, PlainTextStream.Size);
    PlainTextStream.ReadBuffer(buffer[0], Length(buffer));
    Result                       := StripNonAscii(ByteToString(buffer));
  finally
    CipherStream.Free;
    PlainTextStream.Free;
    Codec.Free;
    CryptographicLibrary.Free;
  end;
end;
1

There are 1 best solutions below

1
On

Here is a sample program to encrypt/decrypt UTF-16LE strings in AES-256, modifying the padding to be interoperable with C# stype of padding. Lockbox 3 wasn't made for unorthodox padding techniques, such as C# uses, but with a bit of work, we can adapt.

  • Compiler = Delphi XE6
  • TPLB3 version = 3.6.0
  • Target platform = Win32
  • Config = DEBUG
  • Application genre = VCL forms

Listing 1: program CSharp2.dpr

program CSharp2;

uses
  Vcl.Forms,
  umfmCSharp2Demo in 'umfmCSharp2Demo.pas' {mfmCSharp2Demo};

{$R *.res}

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TmfmCSharp2Demo, mfmCSharp2Demo);
  Application.Run;
end.

Listing 2: umfmCSharp2Demo.pas

unit umfmCSharp2Demo;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, System.Actions,
  Vcl.ActnList, Vcl.PlatformDefaultStyleActnCtrls, Vcl.ActnMan, Vcl.ExtCtrls,
  TPLB3.Codec, TPLB3.CryptographicLibrary;

type
  TmfmCSharp2Demo = class(TForm)
    edtKey: TLabeledEdit;
    edtIV: TLabeledEdit;
    rdChain: TRadioGroup;
    memoPlaintext: TMemo;
    lblPlaintext: TLabel;
    edtCiphertext: TLabeledEdit;
    actmngrMainActions: TActionManager;
    Button1: TButton;
    Button2: TButton;
    memoResults: TMemo;
    actEncrypt: TAction;
    actDecrypt: TAction;
    procedure actEncryptExecute(Sender: TObject);
    procedure actDecryptExecute(Sender: TObject);

  private
    procedure Put( const Line: string; const Args: array of const);
    procedure Setup( var Codec: TCodec; var CryptographicLibrary: TCryptographicLibrary);
    procedure SetIV( var Codec: TCodec; var Padder: TObject);
  end;

var
  mfmCSharp2Demo: TmfmCSharp2Demo;

implementation














uses TPLB3.Constants,
     TPLB3.StreamUtils, TPLB3.StreamToBlock;

{$R *.dfm}


type
  TCSharpPadder = class( TObject)
    private
      FIV: TBytes;
    public
      constructor Create( const AIV: TBytes);
      procedure SetIV_For_Decrypt( Value: TMemoryStream);
    end;



function StripSpace( const Spacey: string): string;
var
  Builder: TStringBuilder;
  i: integer;
begin
Builder := TStringBuilder.Create;
Builder.Append( Spacey);
for i := Builder.Length - 1 downto 0 do
  if CharInSet( Builder.Chars[ i], [' ', #9, #$A, #$D]) then
    Builder.Remove( i, 1);
result := Builder.ToString;
Builder.Free
end;

procedure TmfmCSharp2Demo.Setup(
  var Codec: TCodec;
  var CryptographicLibrary: TCryptographicLibrary);
begin
Codec := TCodec.Create( nil);
CryptographicLibrary := TCryptographicLibrary.Create( nil);
Codec.CryptoLibrary  := CryptographicLibrary;
Codec.StreamCipherId := BlockCipher_ProgId;
Codec.BlockCipherId  := Format( AES_ProgId, [256]);
case rdChain.ItemIndex of
  0:  Codec.ChainModeId := CBC_ProgId;
  1:  Codec.ChainModeId := CFB_ProgId;
  2:  Codec.ChainModeId := CFB8bit_ProgId;
  3:  Codec.ChainModeId := CTR_ProgId;
  4:  Codec.ChainModeId := OFB_ProgId;
  end
end;


procedure TmfmCSharp2Demo.SetIV( var Codec: TCodec; var Padder: TObject);
var
  IV_AsStream: TStream;
  IV: TBytes;
  CSharpPadder: TCSharpPadder;
begin
Padder := nil;
IV_AsStream := TMemoryStream.Create;
try
  Base64_to_stream( StripSpace( edtIV.Text), IV_AsStream);
  IV_AsStream.Position := 0;
  SetLength( IV, IV_AsStream.Size);
  IV_AsStream.ReadData( IV, Length( IV))
finally
  IV_AsStream.Free
  end;
if Length( IV) <> 16 then
  raise Exception.Create( 'To encrypt C# style, you must supply a 16 byte IV.');
CSharpPadder  := TCSharpPadder.Create( IV);
Padder        := CSharpPadder;
Codec.OnSetIV := CSharpPadder.SetIV_For_Decrypt
end;

procedure TmfmCSharp2Demo.actEncryptExecute( Sender: TObject);
var
  Key: TStream;
  Codec: TCodec;
  CryptographicLibrary: TCryptographicLibrary;
  s: string;
  Builder: TStringBuilder;
  BlockLenInUnits, PadIdx: integer;
  Ciphertext: TStream;
  Padder: TObject;
  Temp: TBytes;
  Len: integer;
begin
try
  // 1. Create the components dynamically.
  //  An alternative would be to use design-time properties.
  Setup( Codec, CryptographicLibrary);
  Ciphertext := TMemoryStream.Create;
  try

    // 2. Pad the plaintext, C# style.
    //  This is an inferior style of padding. Only do it if you need
    //  interoperability with C# libraries.
    Builder := TStringBuilder.Create;
    Builder.Append( memoPlaintext.Lines.Text);
    BlockLenInUnits := 16 div SizeOf( Char);
    for PadIdx := 1 to (BlockLenInUnits - (Builder.Length mod BlockLenInUnits)) mod BlockLenInUnits do
      // C# just pads out with zeros.
      Builder.Append( Char( #0));
    s := Builder.ToString;
    Builder.Free;

    // 3. Read in the IV.
    //  Normally IV is set automatically by TP Lockbox 3, but C# library is pretty stupid
    //  and requires manual management.
    SetIV( Codec, Padder);

    try
      // 4. Read in the binary key (instead of setting a string password),
      //  and initialize the Codec.
      Key := TMemoryStream.Create;
      try
        Base64_to_stream( StripSpace( edtKey.Text), Key);
        Key.Position := 0;
        Codec.InitFromStream( Key);
      finally
        Key.Free
        end;

      // 5. Encrypt using a UTF-16LE format.
      Codec.Begin_EncryptMemory( Ciphertext);
      if s.Length > 0 then
        Codec.EncryptMemory( s[ Low( s)], s.Length * SizeOf( Char));
      Codec.End_EncryptMemory;
    finally
      Padder.Free
      end;

    // 8. TP LockBox 3 automatically seeds (prepend) the cipher stream with
    //  the low 8 bytes of the IV. But we don't need this for C# interoperability,
    //  so remove these bytes from the stream.
    if CipherText.Size <> 0 then
      begin
      Ciphertext.Position := 8;
      Len := CipherText.Size - CipherText.Position;
      SetLength( Temp, Len);
      if Len > 0 then
        CipherText.ReadData( Temp, Len);
      CipherText.Size := Len;
      CipherText.Position := 0;
      CipherText.WriteData( Temp, Len)
      end;
    edtCiphertext.Text := Stream_to_Base64( Ciphertext);

  // 9. Tidy up
  finally
    Ciphertext.Free;
    Codec.Free;
    CryptographicLibrary.Free;
    end;
  Put( 'Encryption done.', [])
except on E: Exception do
    begin
    Put( 'Encryption failed: %s: %s', [E.ClassName, E.Message]);
    end
  end;
end;

procedure TmfmCSharp2Demo.actDecryptExecute( Sender: TObject);
var
  Key: TStream;
  Codec: TCodec;
  CryptographicLibrary: TCryptographicLibrary;
  Plaintext: TStream;
  Padder: TObject;
  Temp: TBytes;
  Len: integer;
  CipherStream: TMemoryStream;
  idx, Count: Integer;
begin
try
  // 1. Setup
  Setup( Codec, CryptographicLibrary);
  Plaintext := TMemoryStream.Create;
  try

    // 2. Read in the IV.
    //  Normally IV is set automatically by TP Lockbox 3, but C# library is pretty stupid
    //  and requires manual management.
    SetIV( Codec, Padder);
    CipherStream := TMemoryStream.Create;
    try
      // 3. Read in the binary key (instead of setting a string password),
      //  and initialize the Codec.
      Key := TMemoryStream.Create;
      try
        Base64_to_stream( StripSpace( edtKey.Text), Key);
        Key.Position := 0;
        Codec.InitFromStream( Key);
      finally
        Key.Free
        end;

      // 3. Fake a prepended IV. This value doesn't matter any way.
      //     It gets overwritten by the Padder object.
      Temp := TBytes.Create( 0, 0, 0, 0, 0, 0, 0, 0);
      CipherStream.WriteData( Temp, 8);

      // 4. Decrypt using a UTF-16LE format.
      Codec.Begin_DecryptMemory( Plaintext);
      Base64_to_stream( StripSpace( edtCiphertext.Text), CipherStream);
      Codec.DecryptMemory( CipherStream.Memory^, CipherStream.Size);
      Codec.End_DecryptMemory;
    finally
      Padder.Free;
      CipherStream.Free
      end;

    // 5. Strip C# style padding.
    Plaintext.Position := 0;
    SetLength( Temp, Plaintext.Size);
    Plaintext.ReadData( Temp, Length( Temp));
    Count := 0;
    Len := Length( Temp);
    if Len >= 2 then
      for idx := (Len div 2) - 1 downto 0 do
        begin // Detect how many padding zeroes.
        if (Temp[idx*2] = 0) and (Temp[idx*2 + 1] = 0) then continue;
        Count := (idx + 1) * 2;
        break
        end;
    memoPlaintext.Text := TEncoding.Unicode.GetString( Temp, 0, Count);

  // 9. Tidy up
  finally
    Plaintext.Free;
    Codec.Free;
    CryptographicLibrary.Free;
    end;
  Put( 'Decryption done.', [])
except on E: Exception do
    begin
    Put( 'Decryption failed: %s: %s', [E.ClassName, E.Message]);
    end
  end;
end;


procedure TmfmCSharp2Demo.Put( const Line: string; const Args: array of const);
var
  sLine: string;
begin
sLine := Line;
if Length( Args) > 0 then
  sLine := Format( sLine, Args);
memoResults.Lines.Add( sLine)
end;

constructor TCSharpPadder.Create( const AIV: TBytes);
begin
FIV := AIV
end;

procedure TCSharpPadder.SetIV_For_Decrypt( Value: TMemoryStream);
begin
Value.Size := Length( FIV);
Value.Position := 0;
Value.WriteData( FIV, Length( FIV))
end;

end.

Listing 3: umfmCSharp2Demo.dfm

object mfmCSharp2Demo: TmfmCSharp2Demo
  Left = 0
  Top = 0
  Caption = 'C# Demo 2'
  ClientHeight = 424
  ClientWidth = 554
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  DesignSize = (
    554
    424)
  PixelsPerInch = 96
  TextHeight = 13
  object lblPlaintext: TLabel
    Left = 210
    Top = 96
    Width = 289
    Height = 13
    Caption = 'Plaintext datum (encoded as UTF-16LE; CR.LF line endings):'
    FocusControl = memoPlaintext
  end
  object edtKey: TLabeledEdit
    Left = 8
    Top = 24
    Width = 538
    Height = 24
    Anchors = [akLeft, akTop, akRight]
    EditLabel.Width = 171
    EditLabel.Height = 13
    EditLabel.Caption = 'Key (32 bytes encoded as base64):'
    Font.Charset = DEFAULT_CHARSET
    Font.Color = clWindowText
    Font.Height = -13
    Font.Name = 'Courier New'
    Font.Style = []
    ParentFont = False
    TabOrder = 0
    Text = '0f2EYYpNRTE16ve7dYxbvGuk+BHR/YRhik1FMTXq97s='
  end
  object edtIV: TLabeledEdit
    Left = 8
    Top = 72
    Width = 538
    Height = 24
    Anchors = [akLeft, akTop, akRight]
    EditLabel.Width = 261
    EditLabel.Height = 13
    EditLabel.Caption = 'Initialization Vector (IV; 16 bytes encoded as base64):'
    Font.Charset = DEFAULT_CHARSET
    Font.Color = clWindowText
    Font.Height = -13
    Font.Name = 'Courier New'
    Font.Style = []
    ParentFont = False
    TabOrder = 1
    Text = 'kHLzYvYBxooAAAAAAAAAAA=='
  end
  object rdChain: TRadioGroup
    Left = 8
    Top = 99
    Width = 185
    Height = 102
    Caption = 'Chaining mode'
    Columns = 2
    ItemIndex = 0
    Items.Strings = (
      'CBC'
      'CFB'
      'CFB - 8 bit'
      'CTR'
      'OFB')
    TabOrder = 2
  end
  object memoPlaintext: TMemo
    Left = 208
    Top = 112
    Width = 338
    Height = 89
    Anchors = [akLeft, akTop, akRight]
    ScrollBars = ssVertical
    TabOrder = 3
  end
  object edtCiphertext: TLabeledEdit
    Left = 8
    Top = 224
    Width = 538
    Height = 21
    Anchors = [akLeft, akTop, akRight]
    EditLabel.Width = 399
    EditLabel.Height = 13
    EditLabel.Caption = 
      'Ciphertext datum (encoded as base64; byte count must be a whole ' +
      'multiple of 16):'
    TabOrder = 4
  end
  object Button1: TButton
    Left = 8
    Top = 251
    Width = 273
    Height = 25
    Action = actEncrypt
    TabOrder = 5
  end
  object Button2: TButton
    Left = 287
    Top = 251
    Width = 259
    Height = 25
    Action = actDecrypt
    TabOrder = 6
  end
  object memoResults: TMemo
    Left = 8
    Top = 282
    Width = 538
    Height = 134
    Anchors = [akLeft, akTop, akRight, akBottom]
    Color = clInfoBk
    ReadOnly = True
    ScrollBars = ssVertical
    TabOrder = 7
  end
  object actmngrMainActions: TActionManager
    Left = 256
    Top = 144
    StyleName = 'Platform Default'
    object actEncrypt: TAction
      Caption = 'Encrypt by AES-256 using C# style padding'
      OnExecute = actEncryptExecute
    end
    object actDecrypt: TAction
      Caption = 'Decrypt by AES-256 using C# style padding'
      OnExecute = actDecryptExecute
    end
  end
end