c++ md5 hash not equal to vb.net code socket - c++

server -> AUTH GateKeeper S :GKSSP\0\0\0\0\0\0\0\0\0r!W\tvM
Client -> AUTH GateKeeper S :GKSSP\0\0\0\0\0\0\0\0\0"g#çójDî3(ƒP¡Á"
VB.NET tcp code:
Do While Stream.CanRead
responseData = Trim(System.Text.Encoding.UTF8.GetString(data2, 0, bytes))
Feed = responseData.Split(Chr(13), Chr(10))
Dim datax As [Byte]()
For Each line As String In Feed
If line <> "" Then
Select Case UCase(Split(line.Trim, " ")(0))
Case "AUTH"
If (Split(line, " ")(2) = "S") And (Split(line, " ")(3) <> ":OK") Then
ChOK = ":GKSSP\0\0\0" & Chr(2) & "\0\0\0" & Chr(3) & "\0\0\0" & Challenge_1(Mid(line, InStr(line, "\0\0\0\0\0\0") + Len("\0\0\0\0\0\0"))) & "Sm(" &
HexToAsc("e4") & "HS" & HexToAsc("c1") & "M" & HexToAsc("847f8293f98d") & "UC"
message = "AUTH GateKeeper S " & ChOK & vbCrLf
VB.NET challenge functions
Public Function Challenge_1(Challenge As String) As String
Dim c1 As String, a1 As String, c2 As String, a2 As String
c1 = "edp{}e|wxrdse}}u666666666666666666666666666666666666666666666666" + Challenge
a1 = HexToAsc(GetIRC7PWD(c1))
Dim Aaah As New String("\"c, 48)
c2 = HexToAsc("0f0e1a11170f161d12180e190f17171f") & Aaah & a1
a2 = HexToAsc(GetIRC7PWD(c2))
Challenge_1 = a2
End Function
Public Function GetIRC7PWD(source As String)
Dim md5Hash As MD5 = MD5.Create()
' Dim source As String = "edp{}e|wxrdse}}u666666666666666666666666666666666666666666666666"
Dim data As Byte() = md5Hash.ComputeHash(Encoding.Default.GetBytes(source))
Dim sBuilder As New StringBuilder()
Dim i As Integer
For i = 0 To data.Length - 1
sBuilder.Append(data(i).ToString("x2"))
Next i
Return sBuilder.ToString()
End Function
The above code authenticates successfully.
My C++ code works fine, until it reads data sent from the AUTH and MD5's it. However, it creates the wrong MD5 hash:
while (true)
{
ZeroMemory(buff, 1024);
int bytesReceived = NetDll_recv(XNCALLER_SYSAPP,Sock, buff, 1024, 0);
if (bytesReceived == SOCKET_ERROR)
{
}
if (bytesReceived > 0) {
std::string Recieved(buff, bytesReceived);
std::stringstream iss(Recieved);
while(iss.good())
{
std::string SingleLine;
getline(iss,SingleLine);
if (!SingleLine.empty()) {
stringstream ss(SingleLine);
string s;
int xUp = 1;
vector <string> tokens;
while (getline(ss, s, ' ')) {
tokens.push_back(s);
xUp++;
}
if (tokens[0] == "AUTH" && tokens[2] == "S") {
MD5 md5;
string ChOK3 = SingleLine.substr(46);
string ChallengeCode = "edp{}e|wxrdse}}u666666666666666666666666666666666666666666666666" + ChOK3;
char* cz = const_cast<char*>(ChallengeCode.c_str());
string g1 = md5.digestString( cz );
string a1 = hexToASCII(g1);
int n = 48;
char cx = '\\';
string c2 = hexToASCII("0f0e1a11170f161d12180e190f17171f") + std::string(n, cx) + a1;
char* c = const_cast<char*>(c2.c_str());
string a2 = md5.digestString( c );
string a3 = hexToASCII(a2);
//string ChOK4 = ChOK + a2 + "Sm(" + hexToASCII("e4") + "HS" + hexToASCII("c1") + "M" + hexToASCII("847f8293f98d") + "UC\r\n";
string ChOK4 = "AUTH GateKeeper S :GKSSP\\0\\0\\0\x02\\0\\0\\0\x03\\0\\0\\0" + a3 + "Sm(" + hexToASCII("e4") + "HS" + hexToASCII("c1") + "M" + hexToASCII("847f8293f98d") + "UC\r\n";
const char *cstr = ChOK4.c_str();
NetDll_send(XNCALLER_SYSAPP, Sock,ChOK4.c_str(),ChOK4.length(), 0);
}
It looks like my code is the same.
Is it because the characters are UTF-16? How can I read the buffer in UTF-16 so that I can properly MD5 the string?

The two codes are NOT doing the same thing, which is why they are producing different results. Differences are highlighted below:
While reading from the socket:
the VB code is:
reading arbitrary bytes (without regard to message boundaries)
converting the bytes from UTF-8 to a UTF-16 string (without regard to codeunit boundaries)
trimming the string
splitting the string on line breaks
parsing each line
the C++ code is:
reading arbitrary bytes (without regard to message boundaries)
converting the bytes as-is to a std::string (without regard to encoding)
splitting the string on line breaks (without trimming it first)
parsing each line
Up to this point, given the data shown, the results are logically the same for both codes (though technically different layouts in memory), even though they are being processed differently. But even up to this point, both codes are NOT handling the socket data adequately, and there IS potential for data corruption and data loss here.
Now, assuming no corruption/loss has actually occurred, then for the actual AUTH command:
the VB code is:
extracting the challenge from position 37, after the first "\0\0\0\0\0\0" substring (thus, the extracted challenge is "\0\0\0r!W\tvM")
AUTH GateKeeper S :GKSSP\0\0\0\0\0\0\0\0\0r!W\tvM
^ ^ ^
1 25 37
converting the challenge from UTF-16 to ANSI bytes
calculating an MD5 for those bytes
calculating a 2nd MD5 based on the 1st MD5
creating a response for the 2nd MD5
the C++ code is:
extracting the challenge from fixed index 46 (position 47) (thus, the extracted challenge is "tvM")
AUTH GateKeeper S :GKSSP\0\0\0\0\0\0\0\0\0r!W\tvM
^ ^ ^ ^
0 24 36 46
getting the challenge's raw UTF-8 bytes
calculating an MD5 for those bytes
calculating a 2nd MD5 based on the 1st MD5
creating a response for the 2nd MD5
So, the biggest issues I see that you need to fix are the following:
in both languages, fix the socket reading logic to avoid data loss. Right now, you are extracting all bytes from the socket and throwing away any incomplete lines that are waiting for more bytes to arrive. You need to save all raw bytes from the socket into an intermediate buffer first, and then extract only complete lines from that buffer, leaving behind incomplete lines in the buffer so they can be finished on subsequent reads.
on the VB side, for ASCII-only data (as you have shown), the conversion from UTF-8 to UTF-16 to ANSI is loss-less, but for non-ASCII data this will be lossy. Consider converting the UTF-16 data back to UTF-8 instead of ANSI, to match the C++ code.
on the C++ side, fix the code to extract the server's challenge from the correct string position to match the VB code.

Related

Why is string.find_first_of behaving this way?

I am trying to make a (assembly) parser which uses a string as a guide for how to cut the text to get the tokens I want.
string s = "$t4,";
string guide = "$!,$!,$!";
int i = 1;
string test =s.substr(0, s.find_first_of(" ,.\t"+to_string(guide[i+1]) ));
cout << test << "\n";
if s = "$t4" then test = "$t"
what I am expecting it to do is test to be "$t4", this works for every other $tX except for specifically the number 4 even though it's not in the (" ,.\t"+to_string(guide[i+1])) string
s.find_first_of(" ,.\t" + std::to_string(guide[i + 1]))
Assuming ASCII, that string will be:
,.\t44
44 is the ASCII value of the , in guide[i + 1].
The first character in "$t4," that it'll find is 4 at position 2, and you then create a substring from 0 and length 2, that is $t.

Amazon s3 Test suite REST API signature calculation

I am working on implementing the Amazon REST API in our application. The application is build with WinDev. In order too test my signature calculation i desided to try the test suite provided by amazon:
https://docs.aws.amazon.com/general/latest/gr/signature-v4-test-suite.html
This is how i derive the hex value of my canonical request:
sCanonicalRequestHash = :HashCanonicalRequest([
GET
/
Param1=value1&Param2=value2
host:example.amazonaws.com
x-amz-date:20150830T123600Z
host;x-amz-date
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
])
The method HashCanoncialRequest removes all the char 10 ( this is done in order to hash the string correctly) hashes the string in to binary using windev's hashstring function. This binary function is converted to a hex value, all the whitespace is removed and changed to lower case.
//Remove char 13 ,otherwise the hash fails( Windows enter )
sResult = Replace(sResult, Charact(13), "")
//Create hash
sResult = HashString(HA_SHA_256, sResult)
//Convert hash to lower case hex
sResult = Lower(BufferToHexa(sResult, 1, 32))
//Remove spaces
sResult = Replace(sResult," ", "")
This results the following value:
816cd5b414d056048ba4f7c5386d6e0533120fb1fcfa93762cf0fc39e2cf19e0
This is the value expected by the test suite. So far so good.
Next up is the string to sign, this looks as followed:
AWS4-HMAC-SHA256
20150830T123600Z
20150830/us-east-1/service/aws4_request
816cd5b414d056048ba4f7c5386d6e0533120fb1fcfa93762cf0fc39e2cf19e0
Now it's time to calculate the signingkey.
First up some values given by the test suite:
sSecret is string = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY"
sDate is string = "20150830"
sRegion is string = "us-east-1"
And now the calculation:
bufDateKey is Buffer = WL.HashString(HA_HMAC_SHA_256, sDate, "AWS4" + sSecret)
bufRegionKey is Buffer = WL.HashString(HA_HMAC_SHA_256, sRegion, bufDateKey)
bufServiceKey is Buffer = WL.HashString(HA_HMAC_SHA_256, "service", bufRegionKey)
bufSigningKey is Buffer = WL.HashString(HA_HMAC_SHA_256, "aws4_request", bufServiceKey)
Amazon provides a different test in order too check your calculations here and this calculation is tested and returns the value expected.
Now for the part that doesn't do what the test suite expects. The signature calculation.
//Hashing the ss with psSigningKey as the key
bufSignature = WL.HashString(HA_HMAC_SHA_256, ss, bufSigningKey)
//Converting the hash to hex
bufSignature = BufferToHexa(bufSignature, 1, 32)
//Converting the hex value to lower case and remove any whitespace
bufSignature = Replace(Lower(bufSignature), " ", "")
ss is the string value of the string to sign as shown in the third code snipped
bufSigningKey is the binary value of the result for the for last code snipped. This is converted to hex and all the white space is removed and the string is converted to lower case. This do's not return the signature as shown by the test suite.
If hope someone can help.

How can I use languages (like arabic or chinese) in a QString?

How can I use languages (like arabic or chinese) in a QString?
I am creating a QString:
QString m = "سلام علیکم";
and then I am saving it into a file using:
void stWrite(QString Filename,QString stringtext){
QFile mFile(Filename);
if(!mFile.open(QIODevice::WriteOnly | QIODevice::Append |QIODevice::Text))
{
QMessageBox message_file_Write;
message_file_Write.warning(0,"Open Error"
,"could not to open file for Writing");
return;
}
QTextStream out(&mFile);
out << stringtext<<endl;
out.setCodec("UTF-8");
mFile.flush();
mFile.close();
}
But, when I open the result file I see:
???? ????
What is going wrong? How can I get my characters to be saved correctly in the file?
QString has unicode support. So, there is nothing wrong with having*:
QString m = "سلام علیکم";
Most modern compilers use UTF-8 to encode this ordinary string literal (You can enforce this in C++11 by using u8"سلام عليكم", see here). The string literal has the type of an array of chars. When QString is initialized from a const char*, it expects data to be encoded in UTF-8. And everything works as expected.
All input controls and text drawing methods in Qt can take such a string and display it without any problems. See here for a list of supported languages.
As for the problem you are having writing this string to a file, You just need to set the encoding of data you are writing to a codec that can encode these international characters (such as UTF-8).
From the docs, When using QTextStream::operator<<(const QString& string), The string is encoded using the assigned codec before it is written to the stream.
The problem you have is that you are using the operator<< before assigning. You should setCodec before writing. your code should look something like this:
void stWrite(QString Filename,QString stringtext){
QFile mFile(Filename);
if(!mFile.open(QIODevice::WriteOnly | QIODevice::Append |QIODevice::Text))
{
QMessageBox message_file_Write;
message_file_Write.warning(0,"Open Error"
,"could not to open file for Writing");
return;
}
QTextStream out(&mFile);
out.setCodec("UTF-8");
out << stringtext << endl;
mFile.flush();
mFile.close();
}
* In translation phase 1, Any source file character not in the basic character set is replaced by the universal-character-name that designates the character,The basic character set is defined as follows:
N4140 §2.3 [lex.charset]/1
The basic source character set consists of 96 characters: the space
character, the control characters representing horizontal tab, vertical tab, form feed, and new-line, plus the following 91 graphical characters:
a b c d e f g h i j k l m n o p q r s t u v w x y z
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
0 1 2 3 4 5 6 7 8 9
_ { } [ ] # ( ) < > % : ; . ? * + - / ^ & | ~ ! = , \ " ’
This means that a string like:
QString m = "سلام عليكم";
Will be translated to something like:
QString m = "\u0633\u0644\u0627\u0645\u0020\u0639\u0644\u064a\u0643\u0645";
Assuming that the source file is encoded in an encoding that supports storing such characters such as UTF-8.

ofstream not translating "\r\n" to new line character

I have written a c++ code for changing file formats. Part of the functionality is to add a configured line end character. For one of file conversions, the line end character required is "\r\n" i.e. CR+NL .
My code basically reads the configured value from DB and appends it to the end of each record. Something on the lines of
//read DB and store line end char in a string lets say lineEnd.
//code snippet for file writting
string record = "this is a record";
ofstream outFileStream;
string outputFileName = "myfile.txt";
outFileStream.open (outputFileName.c_str());
outFileStream<<record;
outFileStream<<lineEnd; // here line end contains "\r\n"
But this prints record followed by \r\n as it is, no translation to CR+NL takes place.
this is a record\r\n
While the following works (prints CR+LF in output file)
outFileStream<<record;
outFileStream<<"\r\n";
this is a record
But I can not hard code it. I am facing similar issues with "\n" also.
Any suggestions on how to do it.
The translation of \r into the ASCII character CR and of \n into the ASCII character LF is done by the compiler when parsing your source code, and in literals only. That is, the string literal "A\n" will be a 3-character array with values 65 10 0.
The output streams do not interpret escape sequences in any way. If you ask an output stream to write the characters \ and r after each other, it will do so (write characters with ASCII value 92 and 114). If you ask it to write the character CR (ASCII code 13), it will do so.
The reason std::cout << "\r"; writes the CR character is that the string literal already contains the character 13. So if your database includes the string \r\n (4 characters: \, \r, \, n, ASCII 92 114 92 110), that is also the string you will get on output. If it contained the string with ASCII 13 10, that's what you'd get.
Of course, if it's impractical for you to store 13 10 in the database, nothing prevents you from storing 92 114 92 110 (the string "\r\n") in there, and translating it at runtime. Something like this:
void translate(std::string &str, const std::string &from, const std:string &to)
{
std::size_t at = 0;
for (;;) {
at = str.find(from, at);
if (at == str.npos)
break;
str.replace(at, from.size(), to);
}
}
std::string lineEnd = getFromDatabase();
translate(lineEnd, "\\r", "\r");
translate(lineEnd, "\\n", "\n");

Split a text file so that each line does not exceed 50 characters

I am reading a text file with mostly alpha characters. the content is not really relevant but the size of each line is very important. The process I will feed this text to will require each line be no more than 50 characters. So I will pre-process the text and add line feeds to make sure that happens.
I tried several VB.NET regex like ^.*$ but that doesn't really break up the lines by 50 characters. I would take the result and iterate through each match and then cut it up and write it to an object in memory. Can this be done with a single regex pass?
Otherwise I will use a streamreader and on each line check the length and if <=50 write it out with a streamwriter. if >50 cut it up in sections of 50 and then use streamwriter.
A brief example of my text:
119 SMITH KATY AAAA F ZZZ X NB SX ET
MILES,200/LM450
120 JONES THOMAS W QQQ 66-W NB OS SC LW EP
ET
L/G/B/MAY20-2010/JONES/THOMAS/KEITH 121 BUBBA BILLY HH4 S XQT 2PA-F 1 IP SC LH ET
DOCC
122 NEWTON IAASAC S FTY 240-U NB QC LF KD EE
Just looking for tips on how to efficiently do this.
Update: I ended up using the streamreader approach as suggested by SSS. However, I tried to avoid the old Mid function and stick with Substring. Thus i had to make some checks and use some code from another SO post but cant remember which one. anyway here it is:
Dim reader As New StringReader(aSource)
Dim line As String = Nothing
Dim writer As New StringWriter
Dim chunkSize As Integer = 50
Dim chunk As String
Do
line = reader.ReadLine()
If Not String.IsNullOrEmpty(line) Then
Debug.WriteLine(line.Length & "-->" & line)
'if line length is less than or equal to chunk size then write it out, otherwise cut it up and then write the chunks out
If line.Length <= chunkSize Then
writer.WriteLine(line)
Else
Debug.WriteLine("---------------------")
For i = 0 To line.Length Step chunkSize
Debug.WriteLine("i =" & i)
Debug.WriteLine("i+c=" & i + chunkSize)
Debug.WriteLine("L =" & line.Length)
If i + chunkSize > line.Length Then
chunk = line.Substring(i, line.Length - i)
Else
chunk = line.Substring(i, chunkSize)
End If
Debug.WriteLine(" " & chunk.Length & "-->" & chunk)
writer.WriteLine(chunk)
Next i
Debug.WriteLine("---------------------")
End If
End If
Loop While (line IsNot Nothing)
reader.Close()
reader.Dispose()
'this cut string now becomes our source
Debug.WriteLine("==>" & writer.ToString)
sourceText = writer.ToString
writer.Close()
writer.Dispose()
Hope that helps someone with the same problem.
Imports System.IO
Public Class Form1
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim strFilenameIn As String = "C:\Junk\Input.TXT"
Dim strFilenameOut As String = "C:\Junk\Output.TXT"
Try
Using sr As New StreamReader(strFilenameIn)
Using sw As New StreamWriter(strFilenameOut)
Do
Dim strLine As String = sr.ReadLine()
If strLine Is Nothing Then Exit Do 'EOF
For i As Integer = 1 To strLine.Length Step 50
sw.WriteLine(Mid(strLine, i, 50))
Next i
Loop
End Using
End Using
Catch ex As Exception
MsgBox(ex.Message)
Exit Sub
End Try
MsgBox(strfilenameout & " written")
End Sub
End Class