Convert char to bytearray with hex-data 0x31 - c++

I work with QT Creator (MinGW 5.3.0 C++, Windows) and want to write some serial-connection hex-data.
I found a way but there is something wrong. Only one hex-data he convert wrong.
I hope my code will help to understand.
static const unsigned char mytestUC[] = {0x04,0x31,0xD2,0x01, 0xA1};
enum { NBYTES = sizeof(mytestUC) };
const char *mytest = reinterpret_cast<const char*>(mytestUC);
QByteArray testdata = QByteArray::fromRawData(mytest, sizeof(mytest)+1);
qDebug()<<"testdata "<<testdata;
I would think the output should be:
testdata "\x04\31\xD2\x01\xA1"
But in real it looks like this:
testdata "\x04""1\xD2\x01\xA1"
I tried some other way to write my hex-data directly in the QBytearray with append.
testdata .append((char) 0x04);
testdata .append((char) 0x31);
testdata .append((char) 0xD2);
testdata .append((char) 0x01);
testdata .append((char) 0xA1);
Why does the Programm convert only the the 0x31 in the wrong way and how can I make it better?
Is there some easier way to write hex-data in QBytearray?

Whe you print the QByteArray it tries to convert all characters to ASCII, but the numerical value is the 0x31. In serial port data is send in binary, so character '1' will be send as 0x31. To see data in hexa from a QByteArray you can use the function toHex(), example:
QByteArray ba;
ba.append((char) 0x31);
ba.append((char) 0x32);
ba.append((char) 0x33);
ba.append((char) 0x34);
qDebug() << "char: " << ba;
qDebug() << "hexa: " << ba.toHex();
The output will be:
char: "1234"
hexa: "31323334"
There is a function qint64 QIODevice::write(const QByteArray &byteArray) in QSerialPort class.

Related

Loss of non ascii characters from QString to char *

I have a QString with non ascii characters . I am passing this QString to another process . However when I debug the receiving process the argv arguments do not receive a correct string that was intended from the source . My pseudo code would be
process.start( proc, QStringList() << "-a" <<param );
process.waitForFinished(m_timeout);
Here param is the QString that contains the non ascii text
I solved the problem with the following code
LPWSTR *szArglist;
int argC;
szArglist = CommandLineToArgvW(GetCommandLineW(),&argC);
if(NULL==szArglist) {
throw;
}
QString qstrConvertFile = QString::fromUtf16((const ushort*)(szArglist[4]));

Serialize encrypted password to XML

I have an application that contains userbase which is stored in xml file and loaded at the start of the program. I use QXmlStreamWriter/Reader for this purpose. The problem occurs when I try to serialize encrypted (hashed?) form of password (using QCryptographicHash and Sha256 for this).
QCryptographicHash return QByteArray, which can be converted to QString (necessary for use of QXmlStreamWriter/Reader). Relevant code below. Everything works fine before the serialization (I can log in), but when I read data from xml, after finding hashed password the function behaves like it found the EOF, and only about 2 chars are loaded to the QString by QXmlStreamReader.
In the code please ignore reservations etc (it's a cinema panel), relevant fragments are passwords, I supply full function just in case.
I hope I explained what the issue is, here are fragments of my code (note: before adding the hashing everything worked fine)
Register function (the hashing, pass is a QString):
QString hash = QCryptographicHash::hash(pass.toUtf8(), QCryptographicHash::Sha256);
User* user_pointer;
user_pointer = new User(name, hash, admin);
Writing function:
QFile file("users/users.xml");
if(!file.open(QIODevice::WriteOnly))
throw "Error podczas otwierania bazy użytkowników!";
QXmlStreamWriter writer (&file);
writer.setAutoFormatting(true);
writer.writeStartDocument();
writer.writeStartElement("USERS");
int list_size = userList.size();
for(int i = 0; i < list_size; i++)
{
writer.writeStartElement("USER");
writer.writeTextElement("name", userList.at(i)->name);
writer.writeTextElement("pass", userList.at(i)->password);
writer.writeTextElement("admin", QString::number(userList.at(i)->is_admin));
writer.writeStartElement("RESERVATIONS");
for(int m = 0; m < userList.at(i)->reservList.size(); m++)
{
writer.writeStartElement("reservation");
writer.writeTextElement("moviename", userList.at(i)->reservList.at(m)->movie_name);
writer.writeTextElement("date", userList.at(i)->reservList.at(m)->date.toString("dd.MM.yyyy"));
writer.writeTextElement("hour", (userList.at(i)->reservList.at(m)->hour).toString("hhmm"));
writer.writeTextElement("paid", QString::number(userList.at(i)->reservList.at(m)->paid));
for(int n = 0; n < userList.at(i)->reservList.at(m)->placeList.size(); n++)
writer.writeTextElement("place", QString::number(userList.at(i)->reservList.at(m)->placeList.at(n)));
writer.writeEndElement();
}
writer.writeEndElement();
writer.writeEndElement();
}
writer.writeEndDocument();
file.close();
}
Reading function:
QFile file("users/users.xml");
if(!file.open(QIODevice::ReadOnly))
throw "Brak bazy danych użytkowników lub błąd jej otworzenia!";
QXmlStreamReader reader;
reader.setDevice(&file);
reader.readNext();
QString user_name;
QString user_pass;
bool admin;
QString movie_name;
QTime hour;
QDate date;
bool paid;
User* user_pointer = NULL;
int user_counter = -1;
Reservation* reserv_pointer = NULL;
int reserv_counter = -1;
while(!reader.atEnd())
{
if(reader.isStartElement())
{
if(reader.name() == "USER")
{
reserv_counter = -1;
}
if(reader.name() == "name")
user_name = reader.readElementText();
if(reader.name() == "pass")
user_pass = reader.readElementText();
if(reader.name() == "admin")
{
admin = reader.readElementText().toInt();
user_pointer = new User(user_name, user_pass, admin);
userList.append(user_pointer);
user_counter++;
}
if(reader.name() == "reservation")
{
reserv_counter++;
}
if(reader.name() == "moviename")
movie_name = reader.readElementText();
if(reader.name() == "hour")
hour = QTime::fromString(reader.readElementText(), "hhmm");
if(reader.name() == "date")
date = QDate::fromString(reader.readElementText(), "dd.MM.yyyy");
if(reader.name() == "paid")
{
paid = reader.readElementText().toInt();
reserv_pointer = new Reservation(movie_name, date, hour, paid);
userList.at(user_counter)->reservList.append(reserv_pointer);
}
if(reader.name() == "place")
{
userList.at(user_counter)->reservList.at(reserv_counter)->placeList.append(reader.readElementText().toInt());
}
reader.readNextStartElement();
}
else
reader.readNext();
}
file.close();
}
The hash value is not a string, it is a sequence of arbitrary byte values, some of those might happen to be problematic when interpreting it as a string.
You have an implicit conversion from QByteArray to QString, for which the documentation says that:
The byte array is converted to Unicode using the fromUtf8() function.
This function stops conversion at the first NUL character found, or
the end of the ba byte array.
You could for example use explicit conversion which specifies the length:
QString::fromUtf8(byteArray.data(), length);
As Frank Osterfeld noted in the comments, using UTF8 is not a good idea, I have tested from and to Latin1 extensively for a project I was working on, and the binary data is identical, however in looks "funky" in textual form, which may not play well with the XML reading and writing, toHex() will fix that by limiting the character set to 0-F:
QByteArray b; // hash
QString ss = QString::fromLatin1(b.toHex()); // to QString
b = QByteArray::fromHex(ss.toLatin1()); // back to QByteArray
Your problem is that XML is a text format but encrypted passwords are a binary format. The two are not compatible. You need to have some way to encode the binary data in a text-friendly format.
As #ddriver has mentioned, one way to do this is to use QByteArray::toHex() since it will convert all bytes to human readable text characters. The cost, however, is a 100% increase in size (2 characters returned for each byte of the password hash)
Another, more ubiquitous, and efficient, method for transmitting binary data in textual form is to use the QByteArray::toBase64(). While it also returns binary data in a textual form, there's only a 33 1/3% increase in size (4 bytes returned for every 3 bytes of password hash).
(You might recognise this encoding since its the nonsensical text that usually ends with one or two = characters, and is the usual encoding used to transmit binary data in emails)

Embarcadero: How to use TBase64Encoding's EncodeBytesToString method

I am attempting to convert an array of bytes to a base64 encoded String using the EncodeBytesToString method of the TBase64Encoding class. The documentation for EncodeBytesToString states:
"Returns a string with the input array of bytes encoded up to the specified number of bytes."
Therefore, I attempted to encode my byte array like so:
TFile * File = new TFile();
TBytes Bytes = File->ReadAllBytes("D:\\Sample.pdf");
TBase64Encoding * Encoder = new TBase64Encoding();
String EncodedBytes = Encoder->EncodeBytesToString(Bytes, Bytes.Length);
However, I get the following error:
E2285 Could not find a match for 'TNetEncoding::EncodeBytesToString(TByteDynArray,int)'
I am confused, as the documentation seems to say that I should pass a TBytes object and an int into this function. What am I missing here?
Try this:
//------------------------------------------------------------------------------
String __fastcall BytesToBase64( TByteDynArray _ArrayIn )
{
TBase64Encoding * Encoding = new TBase64Encoding( 64, '\n' );
String Result = Encoding->EncodeBytesToString( &_ArrayIn[0], _ArrayIn.High );
delete Encoding;
return Result;
}
//------------------------------------------------------------------------------
TByteDynArray __fastcall Base64ToBytes( String _64String )
{
TByteDynArray My64Bytes = _64String.BytesOf();
return TNetEncoding::Base64->Decode(&My64Bytes[0], My64Bytes.High);
}
//------------------------------------------------------------------------------
System.NetEncoding.TNetEncoding provides the static property Base64 to retrieve an instance of TNetEncoding for base64 encoding.
So this will also work:
String __fastcall BytesToBase64(TByteDynArray _ArrayIn)
{
return TNetEncoding::Base64->EncodeBytesToString(&_ArrayIn[0], _ArrayIn.High);
}

HMAC on Mountain lion OSX 10.8.3 EXC_CRASH

Looking for a bit of help using OpenSSL's HMAC function. Currently this function is failing on the HMAC call. ONLY for OSX. Both linux and windows os's are working okay.
QString tradingDialog::HMAC_SHA512_SIGNER(QString UrlToSign, QString Secret){
QString retval = "";
QByteArray byteArray = UrlToSign.toUtf8();
const char* URL = byteArray.constData();
QByteArray byteArrayB = Secret.toUtf8();
const char* Secretkey = byteArrayB.constData();
const EVP_MD *md = EVP_sha512();
unsigned char* digest = NULL;
// Be careful of the length of string with the choosen hash engine. SHA1 produces a 20-byte hash value which rendered as 40 characters.
// Change the length accordingly with your choosen hash engine
char mdString[129] = { 0 };
// Using sha512 hash engine here.
digest = HMAC(md, Secretkey, strlen( Secretkey), (unsigned char*) URL, strlen( URL), NULL, NULL);
for(int i = 0; i < 64; i++){
sprintf(&mdString[i*2], "%02x", (unsigned int)digest[i]);
}
retval = mdString;
return retval;
}
You don't say what the problem is on osx, but it looks like you're not nul terminating mdString, so try changing it to
char mdString[129] = { 0 };
The crashlog you linked to shows that your app is aborting because the stack has been corrupted (I assume this happens on exit).
I would say the final sprintf is causing this, as it is adding a nul byte after the end of your mdString array. Try the above modification and see if that helps.
This ought to crash on all platforms, but I guess you got "lucky".

unpredictable runtime behavior while converting QString to const char*

I have these following codes set up-
class ID3{
const char* fileName;
TagLib::FileRef *file;
public:
ID3(const char *);
QImage* artwork();
}
ID3::ID3(const char* fileNameStr){
this->fileName = fileNameStr;
this->file = new TagLib::FileRef(fileNameStr);
qDebug()<<fileNameStr; //OUTPUT 2
}
QImage* ID3::artwork(){
QString str = QString::fromLocal8Bit(this->fileName);
qDebug()<<str; //OUTPUT 3
//MORE CODES-------
}
const char * QstrTocChar(QString str){
QByteArray ba = str.toLocal8Bit();
qDebug()<<ba.constData(); //OUTPUT 1
return ba.constData();
}
int main(int argc, char *argv[]){
.
.
.
QString fileName = "C:/Qt/Qt5.0.2/Projects/taglib_test/music files/Muse_-_Madness.mp3";
file = new ID3(QstrTocChar(fileName));
QImage *image = file->artwork();
}
Now when I run the program, I get these strange outputs
OUTPUT 1
C:/Qt/Qt5.0.2/Projects/taglib_test/music files/Muse_-_Madness.mp3
OUTPUT 2
????p???e'2/
OUTPUT 3
"°í³àpµ˜Æe'2/"
Not sure about OUTPUT 2 but I expect OUTPUT 3 to be same as OUTPUT 1. I am a Qt newbie. Would really appreciate advice/help in understanding, these strange character encoding issues and how to get OUTPUT 3 fixed.
Thanks!
ba.constantData() is returning a pointer to data which will be invalid when QstrToChar finishes executing (the 8-bit converted QByteArray), when QstrToChar completes, all you have left is free'd junk.
What if you just did:
file = new ID3(fileName.toLocal8Bit().constData());
in your main routine?
Actually, you still probably need to keep your own copy of this data in your private ID3 char *, since it can go away with the destruction of these temporaries.
Your code should be this, instead:
class ID3{
std::string fileName;
std::smart_ptr<TagLib::FileRef> file;
public:
ID3(std::string);
QImage* artwork();
}
ID3::ID3(std::string fileNameStr) {
this->fileName = fileNameStr;
this->file.reset(new TagLib::FileRef(fileNameStr));
qDebug()<<fileNameStr; //OUTPUT 2
}
QImage* ID3::artwork(){
QString str = QString::fromLocal8Bit(this->fileName);
qDebug()<<str; //OUTPUT 3
//MORE CODES-------
}
std::string QstrToCppString(QString str){
QByteArray ba = str.toLocal8Bit();
qDebug()<<ba.constData(); //OUTPUT 1
return std::string(ba.constData());
}
int main(int argc, char *argv[]){
.
.
.
QString fileName = "C:/Qt/Qt5.0.2/Projects/taglib_test/music files/Muse_-_Madness.mp3";
file = new ID3(QstrToCppString(fileName));
QImage *image = file->artwork();
}
Notice that I've wrapped your TagLib::FileRef in a smart_ptr as well, since you are new-ing it, you'll need to manage the memory. An alternative would be to write a proper destructor for your ID3 class. You're definitely leaking these currently (unless you just didn't share your destructor code).