Workarounds for aes_gcm_siv 32-byte key requirement? - rust

Using any password that isn't 32 bytes makes the program panic with:
thread 'main' panicked at 'assertion failed: `(left == right)`
left: `4`,
right: `32`'
The easy solution seems to be either filling it out with zeros or trimming the password from 0 to 31, but why? I doubt it has any security implications since it won't let you use more than 32 bytes. Is there a workaround other than the aforementioned easy solution to allow users to have longer passwords other than rewriting the crate?
The following is a snippet of my code for context:
use aes_gcm_siv::Aes256GcmSiv;
use aes_gcm_siv::aead::{Aead, NewAead, generic_array::GenericArray};
fn encrypt_file(pass: &str, file: &str) {
println!("{}", pass);
let key = GenericArray::from_slice(pass.as_bytes());
let cipher = Aes256GcmSiv::new(&key);
//...
}
I dislike the given usage examples from https://docs.rs/aes-gcm-siv/0.9.0/aes_gcm_siv/. Using a 32 character long hard-coded key seems pretty deceiving.

Part of your problem is that you're using a password directly as a key. You don't want to do that. AES requires a 128-bit, 192-bit, or 256-bit key, and ideally you want it to be indistinguishable from random, which a password typically is not.
If you have a low-entropy secret, you'll probably want to use something like Argon2id to take that passphrase and a random salt of sufficient length to derive a key. If you have a strong secret with lots of entropy, then you can use something like HKDF to generate a key. If you do either of those things, you can generate a key that's exactly 32 bytes long, and, if you want to, a random nonce for the encryption as well. Since you're using AES-GCM-SIV, you can also use a random nonce and just derive the key this way.

Related

How to create API key and Secure Key?

Usually if I consume third party api's, they all give us two keys and they are:
API Key: kind of random number or GUID
Pin/Secure Key/etc: Kind of password or OTP
Now, assuming that I am a third party and I want my API's to be consumed by retailers,
I would also like to create and give these credentials to API consumers.
I work in .net core. Is there any way to create these and also we have to apply security
or token based security.
I am confused because I have no idea how this can be accomlished.
As I researched a few questions on stack-overflow, they suggest to use this, or this, or some use HMAC security but in HMAC, we have to mandate client also to use HMAC so that same signatures can be matched.
I am in confused state of mind. Can you please suggest some ways by which I can do this in .net core
Generating API Keys can basically be broken down to just generating cryptographically random strings.
The following C# code snippet I had lying around generates a random hex string:
using System.Security.Cryptography;
public static string RandomString()
{
byte[] randomBytes = new Byte[64];
using (RandomNumberGenerator rng = new RNGCryptoServiceProvider())
{
rng.GetBytes(randomBytes);
}
SHA256Cng ShaHashFunction = new SHA256Cng();
byte[] hashedBytes = ShaHashFunction.ComputeHash(randomBytes);
string randomString = string.Empty;
foreach (byte b in hashedBytes)
{
randomString += string.Format("{0:x2}", b);
}
return randomString;
}
You can easily change the length of the resulting key by using a different hash function or you can also switch the hex encoding to Base64 (Convert.ToBase64String(hashedBytes) which would replace the foreach loop) encoding which is more common when using API keys.
Edit 2022
Since when I wrote this answer both my understanding of cryptography and .NET Core itself have evolved.
Therefore nowadays I would recommend something like this
public static string GetSecureRandomString(int byteLength = 64)
{
Span<byte> buffer = byteLength > 4096
? new byte[byteLength]
: stackalloc byte[byteLength];
RandomNumberGenerator.Fill(buffer);
return Convert.ToHexString(buffer);
}
The following changes have been implemented:
using stackalloc if possible to reduce managed allocations and GC (garbage collector) pressure, thus increasing performance.
RNGCryptoServiceProvider has been deprecated and replaced with RandomNumberGenerator.Fill() or RandomNumberGenerator.GetBytes(), which also provide cryptographically sufficiently secure random bytes.
(Oversight on my part) There is actually no need for hashing in this context. The randomly generated bytes are secure as they are, so applying a hash function to them not only limits the output length (in case of SHA-256) but is also superfluous.
.NET 5 and later provide the Convert.ToHexString() method to convert bytes to hex.
I added a parameter to specify the length in bytes for the output string. More bytes = better security against brute-force attacks, but it comes with the drawback of a longer output string which may not be as handy as a shorter one. Tweak this value to fit your needs. The default is set to 512 bits (64 bytes) which is sufficiently secure for most applications.
In this example, I have chosen hex-encoding for the final string, but you may use any information-preserving encoding (hex, base64, base32, ASCII, ...) without compromising security.

Which of these encryption methods is more secure? Why?

I am writing a program that takes a passphrase from the user and then writes some encrypted data to file. The method that I have come up with so far is as follows:
Generate a 128-bit IV from hashing the filename and the system time, and write this to the beginning of the file.
Generate a 256-bit key from the passphrase using SHA256.
Encrypt the data (beginning with a 32-bit static signature) with this key using AES in CBC mode, and write it to file.
When decrypting, the IV is read, and then the passphrase used to generate the key in the same way, and the first 32-bits are compared against what the signature should be in order to tell if the key is valid.
However I was looking at the AES example provided in PolarSSL (the library I am using to do the hashing and encryption), and they use a much more complex method:
Generate a 128-bit IV from hashing the filename and file size, and write this to the beginning of the file.
Generate a 256-bit key from hashing (SHA256) the passphrase and the IV together 8192 times.
Initialize the HMAC with this key.
Encrypt the data with this key using AES in CBC mode, and write it to file, while updating the HMAC with each encrypted block.
Write the HMAC to the end of the file.
I get the impression that the second method is more secure, but I don't have enough knowledge to back that up, other than that it looks more complicated.
If it is more secure, what are the reasons for this?
Is appending an HMAC to the end of the file more secure than having a signature at the beginning of the encrypted data?
Does hashing 8192 times increase the security?
Note: This is an open source project so whatever method I use, it will be freely available to anyone.
The second option is more secure.
Your method, does not provide any message integrity. This means that an attacker can modify parts of the ciphertext and alter what the plain text decrypts to. So long as they don't modify anything that will alter your 32-bit static signature then you'll trust it. The HMAC on the second method provides message integrity.
By hashing the key 8192 times it adds extra computational steps for someone to try and bruteforce the key. Assume a user will pick a dictionary based password. With your method an attacker must perform SHA256(someguess) and then try and decrypt. However, with the PolarSSL version, they will have to calculate SHA256(SHA256(SHA256...(SHA256(someguess))) for 8192 times. This will only slow an attacker down, but it might be enough (for now).
For what it's worth, please use an existing library. Cryptography is hard and is prone to subtle mistakes.

AES-256 encryption workflow in scala with bouncy castle: salt and IV usage and transfer/storage

I'm trying to implement secure encryption of the files to be sent over insecure channel or stored in an insecure place. I use bouncy castle framework, my code is written in scala. I decided to use aes-256 (to be more specific - Rinjael with 256 bit block size, here is why). And it seems like I can use Rinjael with any (128|160|192|256) block length.
I cannot understand the whole process overview correctly. Here is one of good answers, in this question there is some useful code specific to bouncy castle. But both leaving some questions unanswered for me (questions below).
So this is how I understand the workflow:
For creating a block cipher instance I have to get an instance of padded block cipher with some output feedback:
// create an instance of the engine
val engine = new RijndaelEngine(bitLength)
// wrap engine with some feedback-blocking cipher mode engine
val ofb = new OFBBlockCipher(engine , bitLength)
// wrap this with some padded-blocking cipher mode
val cipher = new PaddedBufferedBlockCipher(ofb, new PKCS7Padding())
Now I have to run init() on the cipher engine
2.1. first generate a key, to do this the best solution suggested here was to use Scrypt to derive a secret from password instead of using PBKDF2-HMAC-xxx. In russian wikipedia article on Scrypt it is said that the recommended parameters for Scrypt are as follows: N = 16384, r = 8, p = 1
So I'we wrirtten this code to generate the password:
SCrypt.generate(password.getBytes(encoding), salt, 16384, 8, 1, bitLength / 8)
2.2. This leads to that I need a salt. Salt should be an array of random bytes. Most answers here use 8 bytes. So I do
// helper method to get a bunch of random bytes
def getRandomBytes(size: Int) = {
val bytes = Array.ofDim[Byte](size)
val rnd = new SecureRandom()
rnd.nextBytes(bytes)
bytes
}
// generate salt
val salt = getRandomBytes(8)
2.3. For cipher to initialize we need an initialization vector (please take a look at my question (2) below).
val iv = getRandomBytes(bitLength / 8)
2.4. Now we are ready to initialize the cipher.
cipher.init(mode, params(password, salt, iv, bitLength))
Questions:
What should be the size of salt? Why do most respondents here use 8 bytes, not more?
What should be the size of IV? Is it correct that it should be the same size as cipher block size? Is it preferred to be fetched from cipher like here: cipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV(); or to be just random as i did?
Is it correct that I need both the salt and IV or I can use just one of these? For example use random IV as a salt.
And the main question: I have to pass salt and IV to the other side or else it would be not possible to decrypt the message. I need to somehow pass both over unencrypted channel. Is it secure to just add both before an encrypted message (as a header)?
Thanks in advance!
I would go for 16 bytes salt length as suggested
IV should be size of block size of cipher and should be random
Yes you need both salt and IV because salt is used to generate key from password and IV is used to initialize block cipher
Salt and IV are designed to be public. You can send them or store unencrypted, but you do not use any authentication mechanism so anyone can change IV or Salt during transport and you would not be able to detect it and decryption will get you something different. To prevent that you should use some AEAD mode and include IV and salt in authentication.
Is it secure? Sure, they will still need to guess the passphrase. Is it as secure? No, because you're giving the attacker information that they need to simplify the decryption process. If the only way that you can get the salt/password to the other side is via an unencrypted channel then something is better than nothing I suppose, but why can't you exchange this information using PKI/SSL?

Known message and output

If the user knows the output from multiple digestions with a different message. Is that able to be so that the user can get the secret?
Example: Can the user find the unknown secret by knowing the following data?
Edit: updated to make a bit more sense.
unknown = ??;
(unknown+"A") = "";
SHA512(unknown+"B") = "";
SHA512(unknown+"C") = "";
.. etc ... ( can continue indefinitely )
can the unknown variable be discovered?
No secure hash function allows recovering a valid unknown input faster than brute force, even given a set of known plaintexts and hashes. That includes SHA512.
Also, note that HMAC-SHA512 is not implemented as you're suggesting. A system of SHA512(key+data) is vulnerable to a length extension attack: Given the hash for SHA512(key+"Hi"), an attacker can compute the hash for any string with that prefix, such as SHA512(key+"Hi, I'm a hacker") without knowing the key. The HMAC construction avoids this issue.

Password security

Currently I am using a particular scheme for securing passwords, and I think I have some points for improvement. The implementation is in Java, so I prefer to use SHA-2 512 as encryption form.
Currently I have a client-server model, so these things can happen:
Client wants to login, he sends his password with one time normal SHA-2 512 encryption over the network.
The server has the passwords stored in the database as for example SHA-2_512(SHA-2_512(password) + salt), with the inner SHA-2_512(password) being the 'encrypted' password it receives over the network.
Password checks are done server side and there is no way anything can leak out from the server, the only possible vulnerability would be if someone could read out the RAM I think.
I have these questions:
An attacker usually creates collision attacks when wanting to hack a password. However how are collision attacks sufficient? If the password needs to be used for other applications like Outlook.com, Facebook or whatever (which likely use another salt as they have nothing to do with my applications), how is a collision attack enough then? Don't you need the real password?
Does SHA-2 512 already use iteration? And even if so, should I change my encryption methods to automatically use a number of iterations plus how many iterations is preferred? I have also read about using a random number of iterations (in a range), how do I store the random factor determenistically?
Should I store system secrets for every iteration in the server code? See http://blog.mozilla.org/webappsec/2011/05/10/sha-512-w-per-user-salts-is-not-enough/ . I could store an array which would hold a static secret for every iteration, with the nth secret being for the nth iteration. Nobody can know the secrets, they are computed once (I guess as encrypting some random string), and then basically stored in the Server's RAM.
Currently I send the typed password from the client to the server as just SHA-2_512(password), should this process be improved, and if so, how? I cannot use salts, because the client does not have the salt available.
Regards.
TLDR: You need to send the password using an encrypted channel, such as TLS. Consider using bcrypt for password hashing.
SHA-2 512 is not an encryption algortihm, it is a message digest algorithm. An encryption algorithm requires a key and a message to encrypt. It produces ciphertext. The important thing is that an encryption algorithm has a decryption algorithm.
ciphertext = E(key, plaintext);
plaintext = D(key, ciphertext);
A message digest takes a piece of plaintext and produces a message digest. There is no corresponding reverse mechanism to take a message digest and retrieve the original message. There is also no secret key.
digest = hash(plaintext);
If an attacker is able to access a database with hashes, the attacker can retrieve the original password by brute forcing, trying lots of guesses with the hash algorithm.
digest1 = hash(guess1);
digest2 = hash(guess2); //repeat with lots of guesses
Firstly, sending a hash over a network is not secure. It needs to be sent through some secure communications mechanism such as SSL. If an attacker can intercept the hash over the communications they may be able to work out the orignal password.
A hash collision is not the same as brute forcing the password. A hash collision is caused when two different messages produce the same message digest.
digest1 = hash(plaintext1);
digest2 = hash(plaintext2);
if ( ( plaintext1 != plaintext2 ) && ( digest1 == digest2 ) )
// hash collision
SHA-512 does not have iterations designed to prevent brute-forcing. The SHA set of algorithms are designed to be efficient. The reason for adding iterations when hashing passwords is to increase the time it takes to brute force a password. The idea being the cost to perform a legitimate login attempt and perform 100 iterations is tiny compared to an attacker who has millions of passwords, each of which requires 100 iterations. Adding more iterations helps reduce the impact of improved processor speeds (which would help an attacker try more iterations quicker).
You should make the number of iterations a configurable limit that is stored against each user. So you store the password hash, salt and iteration count for each user. This means that in the future you can increase the number of iterations to take into account increased hardware power.
Sending the SHA-2 512 in plaintext is not secure. You should send it within an encrypted channel, such as SSL.
Having said all that, SHA-2 is not designed to be a password hashing algorithm. It is designed for message validation and is to be efficient. Consider using a purpose built password hashing algorithm. One example is bcrypt. It is designed to be computationally difficult and has salt and iterations built in.

Resources