obfuscate password in qt - qt

I want to store a password (no hash) on my disk. it's nothing sensitive but i just don't want it in plaintext on my disk.
what i tried till now is:
converting the string in binary and XOR it with the binary of a key.
bool ok = true;
QByteArray qbaPW("mypass");
long long intPW = qbaPW.toHex().toLongLong( &ok, 16 );
QString binPW = QString::number( intPW, 2);
but the thing is, that it only works with short passwords. if they are too long intPW gets too big for longlong. any ideas how can avoid that thing?
cheers

A QByteArray is like a char array[len] in C. You can access individual members and do whatever you please with them. For example:
QByteArray const key("mykey");
QByteArray password("password");
for (int ik = 0, ip = 0;
ip < password.length();
++ ip, ik = (ik+1 < key.length() ? ik+1 : 0)) {
password[ip] = password[ip] ^ key[ik];
}
Since this just XORs with the key, you repeat this procedure to decrypt the password. A good key will be generated randomly and will be longer than the longest password you envisage (say 64 characters).
Do note that this method is only reasonably safe if the users are explicitly informed not to reuse any other password in your application - otherwise you're essentially leaking passwords that are supposed to be secure.

Related

What's the Zip Strong Encryption Specification/SecureZip key derivation function?

In the (PK)ZIP specification at https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT, specifically in the Strong Encryption Specification (SES) section, there is a line on deriving a key from a password:
MasterSessionKey = DeriveKey(SHA1(Password))
What's DeriveKey?
(In WinZip's AES documentation at https://www.winzip.com/en/support/aes-encryption/, they use PBKDF2 with 1000 iterations. I don't see any similar explanation in APPNOTE)
PKWARE implemented a strong encryption in version 5, but did not provide the algorithm of encoding/decoding (Method For Strongly Encrypted .ZIP Files - Patent US 2020/0250329 A1). In this algorithm AES encryption was implemented as part of it. You can define this by strong encryption (bit 6) = yes in General Purpose Flag.
After that WinZip could not use this algo, so it invented another one. You can define this by strong encryption (bit 6) = no in General Purpose Flag and AesExtraFieldRecord with signature 0x990.
As you can see there're two ways to encrypt a zip file. All open source software use the second one. The first one is available only by PKWARE SecureZIP
You can find example of this alogirthm in (7zip) Strong.cpp:35. In java it should look like this:
public static byte[] getMasterKey(String password) {
byte[] data = password.getBytes(StandardCharsets.UTF_8);
byte[] sha1 = DigestUtils.sha1(data);
return DeriveKey(sha1);
}
private static byte[] DeriveKey(byte[] digest) {
byte[] buf = new byte[kDigestSize * 2]; // kDigestSize = 20
DeriveKey2(digest, (byte)0x36, buf, 0);
DeriveKey2(digest, (byte)0x5C, buf, kDigestSize);
return Arrays.copyOfRange(buf, 0, 32);
}
private static void DeriveKey2(byte[] digest, byte c, byte[] dest, int offs) {
byte[] buf = new byte[64];
Arrays.fill(buf, c);
for (int i = 0; i < kDigestSize; i++)
buf[i] ^= digest[i];
byte[] sha1 = DigestUtils.sha1(buf);
System.arraycopy(sha1, 0, dest, offs, sha1.length);
}
Demo:
String password = "JohnDoe";
byte[] masterKey = getMasterKey(password);
The next paragraph 'defines' it
7.2.5.3 The function names and parameter requirements will depend on
the choice of the cryptographic toolkit selected. Almost any
toolkit supporting the reference implementations for each
algorithm can be used. The RSA BSAFE(r), OpenSSL, and Microsoft
CryptoAPI libraries are all known to work well.
I guess it's up to you to decide which of the encryption algorithms you want to use and go from there

Having trouble decrypting a well-formed cipher text using Crypto++

Background
I've been struggling with decrypting an apparently well-formed cipher text for about a day. Assume we've got the following hex-encoded cipher text which contains exactly 160 characters thereby having 80 bytes.
QString c = "1BFAC407AF0D440A2D6176C0B5D125AA96088490299AC18C74623C0EF1BB1372E554FC4150A8066220E943697BE2491D8AE13AA036B298425AC510A8A917D59EBB69708B9040AB3A84C63043EAD4AB07";
QString k = CryptoUtils::hexEncode("abc");
QString p = CryptoUtils::decrypt(c, k);
qDebug() << p;
Provided we're using AES 256, AFAIK, the key must be of length 32 bytes and cipher text of a length of multiple of 16 bytes, which all these consditions are met regarding my snippet code.
Please note that I'm using SHA256 feeding with a pass phrase to generate a 32 bytes key. So, this ensures that all keys are of length 32 bytes.
Full source codes of those function can be found on my repo on GitHub (at branch Part1).
My Question
When I want to run this code, my app crashes. Here's the exception:
terminate called after throwing an instance of 'CryptoPP::InvalidCiphertext'
what(): StreamTransformationFilter: invalid PKCS #7 block padding found
The program has unexpectedly finished.
I searched around about this problem and figured out it could be because of the trailing \0 once you encrypted the plain text. However, I couldn't just solve the problem. Please help me out, it's just driving me crazy.
Full source codes of those function can be found on my repo on GitHub
I'd make these changes at minimum:
QString CryptoUtils::encrypt(QString text, QString keyhex)
{
...
// name the variable, kill the memory leak
SHA256 sha256;
StringSource ss1(decodedKey, size, true, new HashFilter(sha256, new ArraySink(key, AES::MAX_KEYLENGTH)));
...
// name the variable
StringSource ss2(plain, true, new StreamTransformationFilter(Encryptor, new HexEncoder(new StringSink(encrypted))));
// verify embedded NULLs don't affect results
QString qs = QString::fromStdString(encrypted);
assert(qs.length() == encrypted.length());
}
And:
QString CryptoUtils::decrypt(QString text, QString keyhex)
{
// bad karma here...
string encrypted = text.toStdString();
assert(encrypted.length() == text.length());
...
// name the variable, kill the memory leak
SHA256 sha256;
StringSource ss1(decodedKey, size, true, new HashFilter(sha256, new ArraySink(key, AES::MAX_KEYLENGTH)));
...
// name the variable,
StringSource ss2(encrypted, true, new HexDecoder(new StreamTransformationFilter(Decryptor, new StringSink(plain))));
// verify embedded NULLs don't affect results
QString qs = QString::fromStdString(plain);
assert(qs.length() == plain.length());
}
The hexEncode function seems to misbehave:
QString CryptoUtils::hexEncode(QString text)
{
byte *bytearray = (byte *) text.toLatin1().data();
int length = text.toLatin1().length();
return hexEncode(bytearray, length);
}
Should be replaced with:
QString CryptoUtils::hexEncode(QString text)
{
byte *bytearray = (byte *) text.toStdString().data();
int length = text.length();
return hexEncode(bytearray, length);
}

Implementation to protect a 2048-bit RSA private key using a secret sharing scheme

I'm trying to protect a 2048-bit RSA private key (confidentiality & availability).
I have been looking around for more information on how to do that and I'm thinking of using a secret sharing scheme (Shamir's Secret Sharing would be fine).
Is it the best option ?
Does anyone know a GNU/GPL software implementation in order to accomplish this ?
I look at "ssss" (http://point-at-infinity.org/ssss/), but secret need to be at most 128 ASCII characters and it's too short for a 2048-bit RSA private key.
Thanks for your help.
Just as with public-key cryptography, you occasionally need to use a hybrid scheme when the data exceeds a certain size - you can encrypt the private key using a normal symmetric algorithm with a random key, and then split the symmetric key using a secret-splitting algorithm of choice.
I believe that the implementation here: https://github.com/moserware/SecretSplitter uses this method to split data that exceeds the size limit of the underlying splitting algorithm.
Is it the best option ? Does anyone know a GNU/GPL software implementation in order to accomplish this ?
Crypto++ offers the functionality. But the license is Public Domain (individual source files) or Boost Software 1.0 (library as a whole). Its not GNU/GPL.
Here's the code to do it from Crypto++. It was taken from test.cpp:
Splitting
void SecretShareFile(int threshold, int nShares, const char *filename, const char *seed)
{
RandomPool rng;
rng.IncorporateEntropy((byte *)seed, strlen(seed));
ChannelSwitch *channelSwitch;
FileSource source(filename, false, new SecretSharing(rng,
threshold, nShares, channelSwitch = new ChannelSwitch));
vector_member_ptrs<FileSink> fileSinks(nShares);
string channel;
for (int i=0; i<nShares; i++)
{
char extension[5] = ".000";
extension[1]='0'+byte(i/100);
extension[2]='0'+byte((i/10)%10);
extension[3]='0'+byte(i%10);
fileSinks[i].reset(new FileSink((string(filename)+extension).c_str()));
channel = WordToString<word32>(i);
fileSinks[i]->Put((byte *)channel.data(), 4);
channelSwitch->AddRoute(channel, *fileSinks[i], DEFAULT_CHANNEL);
}
source.PumpAll();
}
Combining
void SecretRecoverFile(int threshold, const char *outFilename, char *const *inFilenames)
{
SecretRecovery recovery(threshold, new FileSink(outFilename));
vector_member_ptrs<FileSource> fileSources(threshold);
SecByteBlock channel(4);
int i;
for (i=0; i<threshold; i++)
{
fileSources[i].reset(new FileSource(inFilenames[i], false));
fileSources[i]->Pump(4);
fileSources[i]->Get(channel, 4);
fileSources[i]->Attach(new ChannelSwitch(recovery, string((char *)channel.begin(), 4)));
}
while (fileSources[0]->Pump(256))
for (i=1; i<threshold; i++)
fileSources[i]->Pump(256);
for (i=0; i<threshold; i++)
fileSources[i]->PumpAll();
}

Is there a way to receive data as unsigned char over UDP on Qt?

I need to send floating point numbers using a UDP connection to a Qt application. Now in Qt the only function available is
qint64 readDatagram ( char * data, qint64 maxSize, QHostAddress * address = 0, quint16 * port = 0 )
which accepts data in the form of signed character buffer. I can convert my float into a string and send it but it will obviously not be very efficient converting a 4 byte float into a much longer sized character buffer.
I got hold of these 2 functions to convert a 4 byte float into an unsinged 32 bit integer to transfer over network which works fine for a simple C++ UDP program but for Qt I need to receive the data as unsigned char.
Is it possible to avoid converting the floatinf point data into a string and then sending it?
uint32_t htonf(float f)
{
uint32_t p;
uint32_t sign;
if (f < 0) { sign = 1; f = -f; }
else { sign = 0; }
p = ((((uint32_t)f)&0x7fff)<<16) | (sign<<31); // Whole part and sign.
p |= (uint32_t)(((f - (int)f) * 65536.0f))&0xffff; // Fraction.
return p;
}
float ntohf(uint32_t p)
{
float f = ((p>>16)&0x7fff); // Whole part.
f += (p&0xffff) / 65536.0f; // Fraction.
if (((p>>31)&0x1) == 0x1) { f = -f; } // Sign bit set.
return f;
}
Have you tried using readDatagram? Or converting the data to a QByteArray after reading? In many cases a char* is really just a byte array. This is one of those cases. Note that the writeDatagram can take a QByteArray.
Generally every thing sent across sockets is in bytes not strings, layers on either end do the conversions. Take a look here, especially the Broadcaster examples. They show how to create a QByteArray for broadcast and receive.
Not sure why the downvote, since the question is vague in requirements.
A 4-byte float is simply a 4 character buffer, if cast as one. If the systems are homogenous, the float can be sent as a signed char *, and bit for bit it'll be the same read into the signed char * on the receiver directly, no conversion needed. If the systems are heterogenous, then this won't work and you need to convert it to a portable format, anyway. IEEE format is often used, but my question is still, what are the requirements, is the float format the same between systems?
If I read it correctly, your primary question seems to be how to receive data of type unsigned char with QT's readDatagram function which uses a pointer to a buffer of type char.
The short answer is use a cast along these lines:
const size_t MAXSIZE = 1024;
unsigned char* data = malloc(MAXSIZE);
readDatagram ( (unsigned char *)data, MAXSIZE, address, port )
I'm going to assume you have multiple machines which use the same IEEE floating point format but some of which are big endian and some of which are little endian. See this SO post for a good discussion of this issue.
In that case you could do something a bit simpler like this:
const size_t FCOUNT = 256;
float* data = malloc(FCOUNT * sizeof(*data));
readDatagram ( (char *)data, FCOUNT * sizeof(*data), address, port )
for (int i = 0; i != FCOUNT; ++i)
data[i] = ntohf(*((uint32_t*)&data[i]));
The thing to remember is that as far as networking functions like readDatagram are concerned, the data is just a bunch of bits and it doesn't care what type those bits are interpreted as.
If both ends of your UDP connection use Qt, I would suggest looking at QDataStream. You can create this from a QByteArray each time you read a datagram, and then read whatever values you require - floats, maps, lists, QVariants, and of course string.
Similarly, on the sending side, you'd create a data stream, push data into it, then send the resulting QByteArray over writeDatagram.
Obviously this only works if both ends use Qt - the data encoding is well-defined, but non-trivial to generate by hand.
(If you want stream orientated behaviour, you could use the fact that QUDPSocket is a QIODevice with a data-stream, but it sounds as if you want per-datagram behaviour)

Using Python to authenticate against raw username, hash, salt in DB created by ASP.NET roles/membership

We have a current application where user login credentials are stored in a SQL Server DB. These are, basically, stored as a plain text username, a password hash, and an associated salt for this hash.
These were all created by built in functions in ASP.NET's membership/role system. Here's a row for a user named 'joe' and a password of 'password':
joe,kDP0Py2QwEdJYtUX9cJABg==,OJF6H4KdxFLgLu+oTDNFodCEfMA=
I've dumped this stuff into a CSV file and I'm attempting to get it into a usable format for Django which stores its passwords in this format:
[algo]$[salt]$[hash]
Where the salt is a plain string and the hash is the hex digest of an SHA1 hash.
So far I've been able to ascertain that ASP is storing these hashes and salts in a base64 format. Those values above decode into binary strings.
We've used reflector to glean how ASP authenticates against these values:
internal string EncodePassword(string pass, int passwordFormat, string salt)
{
if (passwordFormat == 0)
{
return pass;
}
byte[] bytes = Encoding.Unicode.GetBytes(pass);
byte[] src = Convert.FromBase64String(salt);
byte[] dst = new byte[src.Length + bytes.Length];
byte[] inArray = null;
Buffer.BlockCopy(src, 0, dst, 0, src.Length);
Buffer.BlockCopy(bytes, 0, dst, src.Length, bytes.Length);
if (passwordFormat == 1)
{
HashAlgorithm algorithm = HashAlgorithm.Create(Membership.HashAlgorithmType);
if ((algorithm == null) && Membership.IsHashAlgorithmFromMembershipConfig)
{
RuntimeConfig.GetAppConfig().Membership.ThrowHashAlgorithmException();
}
inArray = algorithm.ComputeHash(dst);
}
else
{
inArray = this.EncryptPassword(dst);
}
return Convert.ToBase64String(inArray);
}
Eseentially, pulls in the salt from the DB and b64 decodes it into a binary representation. It does a "GetBytes" on the raw password and then it concatinates them, salt first.
It then runs the SHA1 algorithm on this new string, base64 encodes it, and compares it against the value stored in the database.
I've attempted to write some code to try and reproduce these hashes in Python and I'm failing. I won't be able to use them in Django until I can figure out how this translates over. Here's how I'm testing:
import hashlib
from base64 import b64decode, b64encode
b64salt = "kDP0Py2QwEdJYtUX9cJABg=="
b64hash = "OJF6H4KdxFLgLu+oTDNFodCEfMA="
binsalt = b64decode(b64salt)
password_string = 'password'
m1 = hashlib.sha1()
# Pass in salt
m1.update(binsalt)
# Pass in password
m1.update(password_string)
# B64 encode the binary digest
if b64encode(m1.digest()) == b64hash:
print "Logged in!"
else:
print "Didn't match"
print b64hash
print b64encode(m1.digest())
I'm wondering if anyone can see any flaws in my approach or can suggest an alternate method. Perhaps you can take the algorithms above and the known password and salt above and produce the hash on your system?
It appears python is inserting a byte order marker when you convert a UTF16 string to binary. The .NET byte array contains no BOM, so I did some ghetto python that turns the UTF16 into hex, removes the first 4 characters, then decodes it to binary.
There may be a better way to rip out the BOM, but this works for me!
Here's one that passes:
import hashlib
from base64 import b64decode, b64encode
def utf16tobin(s):
return s.encode('hex')[4:].decode('hex')
b64salt = "kDP0Py2QwEdJYtUX9cJABg=="
b64hash = "OJF6H4KdxFLgLu+oTDNFodCEfMA="
binsalt = b64decode(b64salt)
password_string = 'password'.encode("utf16")
password_string = utf16tobin(password_string)
m1 = hashlib.sha1()
# Pass in salt
m1.update(binsalt + password_string)
# Pass in password
# B64 encode the binary digest
if b64encode(m1.digest()) == b64hash:
print "Logged in!"
else:
print "Didn't match"
print b64hash
print b64encode(m1.digest())
Two thoughts as to what could be going wrong.
First the code from the reflection has three paths:
If passwordFormat is 0 it returns the password as is.
If passwordFormat is 1 it creates the hash as your python code does.
If passwordFormat is anything other than 0 or 1 it calls this.EncryptPassword()
How do you know you are hashing the password, and not encrypting the password with this.EncryptPassword()? You may need to reverse the EncryptPassword() member function and replicate that. That is unless you have some information which ensures that you are hashing the password and not encrypting it.
Second if it is indeed hashing the password you may want to see what the Encoding.Unicode.GetBytes() function returns for the string "password", as you may be getting something back like:
0x00 0x70 0x00 0x61 0x00 0x73 0x00 0x73 0x00 0x77 0x00 0x6F 0x00 0x72 0x00 0x64
instead of:
0x70 0x61 0x73 0x73 0x77 0x6F 0x72 0x64
I hope this helps.

Resources