1

I have a problem by converting the following piece of Delphi code to Java. The code I posted in here is just a small part of my full code that I took to describe my problem.

type
  TSecureArray = Array of AnsiChar;

const
  CodePosIdx = 10; 

function ReadLenFromArray(aArray:TSecureArray):integer;
var
  HS:integer;
begin
  Hs:=0;
  HS:=Hs+(ord(aArray[3]))*$1000000;
  HS:=Hs+(ord(aArray[4]))*$10000;
  HS:=Hs+(ord(aArray[5]))*$100;
  HS:=Hs+(ord(aArray[6]));
  result:=HS;
end;

function Decrypt(Source: Ansistring): Ansistring;    
var 
  srclen, aArrayLength: integer;
  aArray: TSecureArray;
  cryptedstring:AnsiString;
begin
  aArrayLength:=Length(Source); // length of source-String in my test case is 1046
for i:=1 to aArrayLength do                                      
    aArray[i]:=Source[i];

  srclen:=ReadLenFromArray(aArray);  // function returns 858862149 in my test case
  cryptedString:='';
  for i:=1 to srclen do
    cryptedstring:=cryptedstring+aArray[aArraylength-srclen-ord(aArray[CodePosIdx])+i];
end;

in Java I've implemented this piece of Delphi code as:

 protected static int readLenFromArray(char secureArray[])
    {
        int arrayLength = secureArray.length;
        int hs = 0;

        if(2<arrayLength)
        {
        hs = hs + (int) secureArray[2] * 0x1000000;
        }

        if(3<arrayLength)
        {
        hs = hs + (int) secureArray[3] * 0x10000;
        }

        if(4<arrayLength)
        {
        hs = hs + (int) secureArray[4] * 0x100;
        }

        if(5<arrayLength)
        {
        hs = hs + (int) secureArray[5];
        }

        return hs;


 }

    protected static String deCrypt(String code)
    {  
    int codePosIdx = 10;
    char [] secureArray;
    int srcLen;
    int arrayLength;

    arrayLength = source.length();   // 1046 in my test case

    for (i=0; i<arrayLength; i++)
    {
     secureArray[i] = source.charAt(i);
    }
    srcLen = readLenFromArray(secureArray); //  function returns 858862149 in my test case
    StringBuilder tmpStr = new StringBuilder();

    for(i=0; i<srcLen; i++)
    {
    tmpStr = tmpStr.append(secureArray[arrayLength - srcLen - 1 - (int) secureArray[codePosIdx - 1] + i]);
    }
    cryptCode = tmpStr.toString();

    return cryptCode;
    }

My problem is checking of condition in the for-Loop in Java. In my testcase aArray[i + (int) secureArray[codePosIdx - 1]] is equal to aArray[-858861172] and in contrast to Delphi it causes an IndexOutOfBound Exception. I avoided it in Java readLineFromArray function through if-statement. But how could I solve this problem in my decrypt function, so that the functionality of function is preserved for all values of parameters. Any advice truly approciated!

8
  • present the full declaration of your Delphi function including full declaration of TSecureArray type. Same u do for secureArray in Java. Also tell your Delphi version. I suspect you stumbled upon char/PChar/String ambiguity. In some Delphi versions they are mapped to MBCS types AnsiChar/PAnsiChar/AnsiString but in other Delphi versions they are mapped to UTF-16 types WideChar/PWideChar/UnicodeString like text data probably is in Java. Try to output and compare sizeof(aArray[1]) in both Delphi and Java when you deal with unsafe bits-jockeying like Ord/(int) Commented May 26, 2016 at 15:07
  • 1
    @Arioch'The I'm using Embarcadero® Delphi® XE Version 15.0.3953.35171. TSecureArray is just my self-defined type. In Java I'm using a char array for that. I'm totally new in Delphi and also not good familiar with Java. I have to convert a Delphi decryption algorithm to Java that I need to read the password from WindowsRegistry for my Java Client. There are another functions in code that I already implemented in Java and they're working well. The problem is the for-loop in Delphi from decrypt function that not work in Java. Commented May 26, 2016 at 15:22
  • It seems to me that the Delphi code was written for Delphi versions before Delphi 2009, when Char was AnsiChar and string was AnsiString. To use the Delphi code as a model for the Java code will lead to all kind of troubles along the way. Look for a specification of the functionality and code it in Java without even looking at the Delphi code. Commented May 26, 2016 at 16:08
  • @TomBrunberg The Delphi code was written 20 years ago. There is no more specification of it. Even my internship supervisor couldn't explain me how it work. Commented May 26, 2016 at 16:24
  • 1
    Ok, do you have e.g. D7 or D2007 to test with? How can you otherwise be sure that the Java code does what the Delphi code does? Is the return value from ReadLenFronArray() (858862149) reasonable? Anyway, as Johan wrote already, move away from string and char types. Cryptography does not operate on those types. BTW, of curiosity, is the Delphi code available on the net? Commented May 26, 2016 at 17:05

2 Answers 2

6

The problem is that the arrays in Delphi and Java you've declared are not the same.
You're using a AnsiChar in Delphi, which is one byte.
Java is always unicode and therefore the char type is two bytes.
You need to declare the Java array as byte aArray[].

The second routine fails because the Java String is made up of two byte chars so that will never work.
Again you need to pass byte aArray[] and process that.

Because you are no longer working with strings, you cannot rely on the .length() function to get the length of the input, you'll either have to do your own test to see where the terminating zero byte is, or pass the length as a parameter.

Sign up to request clarification or add additional context in comments.

4 Comments

since he uses Windows Registry to fetch the passwords ( I don't want to even talk how "secure" that is, my, my... ) I believe he does not have to worry about detecting string length - he gets it from registry via Win32 API, and that very API deals with MBCS/Unicode gap for applications...
I stumbled upon meaning and using of String and AnsiString, Array and AnsiArray in Delphi. For the Java implementation of Delphi code I just assumed it as String and Array without distinguishing whether they are one or two byte. If I use byte aArray[] in Java, does it mean that there will be no IndexOutOfBound Exception in the for-loop of readLineFromArray in my Java code?
In Delphi arrays are not objects(classes) but compiler intrinsics, low-level structures , unless you would explicitly ask compiler to check arrays boundaries - it would not do it saving few CPU commands on every array access. You need to explicitly ask for that extra safety if you need it - docwiki.embarcadero.com/RADStudio/Berlin/en/Range_checking and docwiki.embarcadero.com/RADStudio/Berlin/en/…
@Johan There is in Delphi for each character of AnsiString 1 byte available, so that only ANSI can be used. Unlike Delphi byte is signed in Java. Have I also take it into account in my Java source code? Which encoding have I to use in this case when I convert byte [] back to string in Java. Is it right if I use UTF-8 like String bytesAsString = new String(bytes, StandardCharsets.UTF_8)
1

Making this an answer for the sake of code formatting that I can not get in comments.

I guess I can se e a fundamental control flow problem in your functions, using the non-initialized ( that is: zero or random garbage ) data. Your function goes as that:

function Decrypt(Source: Ansistring): Ansistring;    
var 
  srclen, aArrayLength: integer;
  aArray: TSecureArray;
  cryptedstring:AnsiString;
begin
  aArrayLength:=Length(Source); // length of source-String in my test case is 1046

....and then suddenly...

  srclen:=ReadLenFromArray(aArray);  // function returns 858862149 in my test case
  for i:=1 to aArrayLength do                                      
    aArray[i]:=Source[i];

This is nuts!!! you did not initialized aArray nowhere, neither the container (length aka size aka volume) nor the data itself, yet you pass that nil=NULL pointer into ReadLenFromArray !!!

I think you wanted something like this:

function Decrypt(const Source: Ansistring): Ansistring;    
....
begin
  aArrayLength := Length(Source); 

  SetLength(aArray, aArrayLength); //  !!!!! init the container
  for i := 1 to aArrayLength do                                      
    aArray[i - 1] := Source[i];    //  !!!!! init the data in the container

  srclen := ReadLenFromArray(aArray); // now and only now can you use the array
   .....rest of function

You should note that for historical reasons ( which I do not think you are very interested in) strings in Delphi are indexed 1 to Length and dynamic arrays are indexed 0 to Length-1 - so i corrected indexes in the for-loop

As far as I can see you also forgot to initialize the secureArray in Java - you NEVER set its Length, so I can only wonder where the data falls to during assignments loop.


Basically your ReadLengthFromArray function just takes the slice of the array and (on Intel-endian machines) reverses byte order there.

More obvious and streamlined code would be like

function ReadLenFromArray(const aArray:TSecureArray): integer;
var
  HS : LongRec absolute Result;
begin
  if (High(aArray) < 6) or (Low(aArray) > 3) then 
     raise EAccessViolation.Create('The passed aArray does not have required data!');

  HS.Bytes[3] := ord(aArray[3]);
  HS.Bytes[2] := ord(aArray[4]);
  HS.Bytes[1] := ord(aArray[5]);
  HS.Bytes[0] := ord(aArray[6]);
end;

http://docwiki.embarcadero.com/Libraries/Berlin/en/System.SysUtils.LongRec

8 Comments

I've composed the code in my question from the entire code and unfortunately put the initialization of array into wrong position in my piece of code. I edited it now. In the Java readLenFromArray function I'm just working with the array that was handed over as formal parameter for the function.
The index for the array initialization I took from the original Delphi source code and there is ('aArray[i] = source [i]').
what does your "original code" does with array length? does it set the length? or maybe that is the STATIC (fixed-boundaries) array there not DYNAMIC(variable-length) array? either there is a bad bug in the original code or in your misrepresentation of it in the sample. we need FULL declaration of aArray from the original code to be able to reason about it
you still did not initialized your array before copying data form string into it - you just stuff the data into Win32 zero-page in Delphi which may result either in AV or into hidden memory corruption
So you posted fake code and want us to help! Do you think we enjoy doing that? Why are you so dismissive of our time? Stop being lazy. Think harder. Try harder. Spend more time. Check your work before posting. Make sure that the code you post is correct. Read this: minimal reproducible example.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.