How to generate a Tink key from a user-provided password - tink

I want to store a keyset, and would like the file to be encrypted with key produced from a user-provided "master password". And of course, at a later point I'd like to, given the same master password, be able to load that keyset by decrypting the file.
It seems that I need an Aead, which I can generate from a KeysetHandle with AeadFactory.getPrimitive(keysetHandle). But how can I produce a KeysetHandle from a "master password"?
(And for the context of this question, getting that key from a Key Management Systems, instead of producing it "out of thin air" from a master password, isn't an option.)

An Aead can be created as follows (here done from Scala):
val password: String = "..."
val aead = {
val messageDigest = MessageDigest.getInstance("SHA-256")
messageDigest.update(password.getBytes(CharsetNames.Utf8))
val key256Bit = messageDigest.digest()
val key128Bit = key256Bit.take(16)
new AesGcmJce(key128Bit)
}
A few comments:
I'd prefer the key to be based on a 32-bit digest, but the cipher picked by Tink in this case throws an exception when provided with a 32-bit key, hence the shortening to a 16-bit key.
It seems shortening the key this way is ok from a hash weakness perspective.

Related

How to create a Parameter Store entry without a value in AWS CDK?

In AWS CDK, you can create a parameter store entry for storing secrets like passwords.
However you cannot leave the value blank, and you shouldn't put the secret in the CDK git repository, so how do you get the entry created?
I am currently doing something like this:
const paramKey = new cdkSSM.StringParameter(this, 'example-key', {
description: 'Example SSH private key parameter',
parameterName: 'example-key',
stringValue: `???`, /// What goes here?
allowedPattern: '^-----BEGIN RSA PRIVATE KEY-----[^-]*-----END RSA PRIVATE KEY-----$',
});
In this case, I can't leave the stringValue blank or I get an error, as Parameter Store does not allow blank values. The only value it will accept is an RSA private key, due to the allowedPattern requirement (which is a safety measure to stop someone from accidentally putting in an invalid value through the AWS CLI). But I don't want to put my private key in as it should not be part of the CDK git repository. I don't want to use a dummy key as then someone might think the correct key has been entered already.
How can I deploy a blank Parameter Store value while having the allowedPattern present?
The only workaround I have come up with is to hack the value to allow another token as well, like this:
const paramKey = new cdkSSM.StringParameter(this, 'example-key', {
description: 'Example SSH private key parameter',
parameterName: 'example-key',
stringValue: `TODO`,
allowedPattern: '^TODO$|^-----BEGIN RSA PRIVATE KEY-----[^-]*-----END RSA PRIVATE KEY-----$',
});
This means the Parameter Store entry will accept either an RSA key or the value TODO. But this seems very hacky so I am wondering whether there is a proper solution for this?

How to store encrypted data?

I'm new to ruby on rails, and I'm developing an application that will have very sensitive data (api keys from other websites) and I need to store it encrypted in a db but without knowing them at any time.
Let me explain myself:
The form asks the user for his api keys
Encrypt them
Store it in the db
The main question is, how do I encrypt them in such a way that I can use them later (still without knowing them)?
Sorry if the question is silly, but I can't find a way to do it, and thanks.
I've used attr_encrypted for this. Works great.
class User
attr_encrypted :ssn, key: 'This is a key that is 256 bits!!'
end
You then work with ssn as if it were a plain field
user = User.find(1)
puts user.ssn
but it's encrypted at rest (in the database) and can't be retrieved without the key.
def encrypt text
text = text.to_s unless text.is_a? String
len = ActiveSupport::MessageEncryptor.key_len
salt = SecureRandom.hex len
key = ActiveSupport::KeyGenerator.new(Rails.application.secrets.secret_key_base).generate_key salt, len
crypt = ActiveSupport::MessageEncryptor.new key
encrypted_data = crypt.encrypt_and_sign text
"#{salt}$$#{encrypted_data}"
end
def decrypt text
salt, data = text.split "$$"
len = ActiveSupport::MessageEncryptor.key_len
key = ActiveSupport::KeyGenerator.new(Rails.application.secrets.secret_key_base).generate_key salt, len
crypt = ActiveSupport::MessageEncryptor.new key
crypt.decrypt_and_verify data
end
Pass the key to encrypt method and store the returned encrypted value in DB.
Then to decrypt pass the encrypted key to the decrypt method.
This is assuming your Secret Key Base is in Rails.application.secrets.secret_key_base
The original source for the answer is here

Flutter / Dart : Avoid storing password in plain text

I am using Dart mailer in Flutter and there is a comment that says:
How you use and store passwords is up to you. Beware of storing passwords in plain.
Is there any way to hash the password? How can I avoid storing it in plain text?
It is generally not a good idea to store passwords in plain text anywhere. The way you handle passwords, though, depends on the platform.
Flutter
The flutter_secure_storage package uses Keychain on iOS and KeyStore on Android to store passwords (or tokens).
// Create storage
final storage = FlutterSecureStorage();
// Read secret
String value = await storage.read(key: key);
// Write secret
await storage.write(key: key, value: value);
Note that for Android the min API is 18.
Dart Server
If you are making a server, it is even more important not to store the user passwords in plain text. If the server is compromised, the attacker would have access to all of the passwords, and many users use the same password on multiple accounts.
It would be best to hand the authentication over to Google or Facebook or some other trusted third party by using OAuth2. However, if you are doing your own authorization, you should hash the passwords with a salt and save the hash, not the password itself. This makes it more difficult for an attacker to get the user passwords in case the server is compromised.
A basic implementation (but see comment below) could use the crypto package by the Dart Team.
// import 'package:crypto/crypto.dart';
// import 'dart:convert';
var password = 'password123';
var salt = 'UVocjgjgXg8P7zIsC93kKlRU8sPbTBhsAMFLnLUPDRYFIWAk';
var saltedPassword = salt + password;
var bytes = utf8.encode(saltedPassword);
var hash = sha256.convert(bytes);
Save the salt and the hash. Discard the password. Use a different salt for every user.
To make brute forcing the hashes more difficult, you can also check out the dbcrypt package.
If you want to hash
Use the password_hash package. Their example code is very easy to use:
var generator = new PBKDF2();
var salt = Salt.generateAsBase64String();
var hash = generator.generateKey("mytopsecretpassword", salt, 1000, 32);
Store both the hash and the salt, and you can verify someone else's password attempt by running the generator.generateKey function using their password and the saved salt.
What you actually want
If you're trying to automatically login, you of course need the original password, not a hash. You have a couple options
If the device that will have your app installed is safe, as in it is some company-owned device that has to be logged into by an employee, then have it in plaintext. It doesn't matter. As any company's security policy should be, you must make sure that hard drives are wiped before disposing of electronics (And make sure that no one can stroll in and take the iPad or whatever it is).
If unknown people outside of your organization will be installing your app, you will have to have them login and use their email, or have an API open that will send emails on their behalf (To prevent spamming from your email). The app would sent a POST to that API to send an email. If you had the plaintext password in the application, they could find it on their device, and abuse it.
This response comes late, but here is my approach to storing and using a password for sending emails to recipients using mailer in Flutter. I hope it helps anyone facing this issue.
First I downloaded the crypton package. Then I created a separate dart file where I handle everything related to sending mails, I called it mailer. In this file is where I specify the password, encrypts it using crypton, and use send the email using the decrypted password.
Below is the code of my mailer.dart file:
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:mailer/mailer.dart';
import 'package:mailer/smtp_server.dart';
import 'package:intl/intl.dart';
import 'package:crypton/crypton.dart';
class Mailer {
//the variable below we use to encrypt and decrypt the password
RSAKeypair _rsaKeypair = RSAKeypair.fromRandom();
//Below we set the password as a private variable
String _password = 'mySecurePassword';
//We set an encrypted variable that will store the encrypted password;
String _encrypted;
//The function below sets the encrypted variable by assigning it the encrypted value of the password
void setEncrypt () {
_encrypted = _rsaKeypair.publicKey.encrypt(_password);
}
//The function below is responsible for sending the email to the recipients and it is what we call when we want to send an email
emailSender({#required String emailRecipient, #required List<String> paths}) async {
//We call the setEncrypt() function to assign the correct value to the encrypted variable
setEncrypt();
String username = 'email#email.com';
//We asign the decrypted value of encrypted to the password we provide to the smtpServer
String password = _rsaKeypair.privateKey.decrypt(_encrypted);
//The rest of sending an email is the same.
final smtpServer = gmail(username, password);
// Use the SmtpServer class to configure an SMTP server:
// final smtpServer = SmtpServer('smtp.domain.com');
// See the named arguments of SmtpServer for further configuration
// options.
// Create our message.
Message message = Message()
..from = Address(username, 'Your name')
..recipients.add(emailRecipient)
..ccRecipients.addAll(['secondEmail#email.com'])
..subject = 'Date: ${DateFormat('dd/MM/yyyy').format(DateTime.now())}'
..text = 'This is the plain text.\nThis is line 2 of the text part.'
..html = "<h1>Hi:</h1>\n<p>This is some html</p>\n<p>Greetings, mailer.dart</p>";
for (String path in paths) {
message
..attachments.add(
FileAttachment(
File(
path,
),
),
);
}
var connection = PersistentConnection(smtpServer);
// Send the first message
await connection.send(message);
// send the equivalent message
//await connection.send(equivalentMessage);
// close the connection
await connection.close();
}
}
This was my approach to solving the issue of storing passwords as plain text for sending emails using the mailer package or any package with a similar purpose.

Decrypt string with AES Cipher Block Chaining in Rails

I am having to implement a payment gateway in Rails that I've not worked with or seen before (Westpac's Payway in Australia if anyone is interested).
Their documentation isn't bad and the system is fairly logical, so much so that it's been quite painless so far (a miracle for payment integration).
Where there is an issue is that after the payment is POSTed directly to Westpac and the payment processed they redirect back to our site with a large encrypted parameter. This is then meant to be decrypted by us to get access to the actual parameters.
Here is Westpac's guidance:
The parameters are encrypted using AES with Cipher Block Chaining, using PCKS-5
Padding. The decryption algorithm should be initialised with a 16 byte, zero-filled
initialization vector, and should use your encryption key (which can be found on the Security page of PayWay Net Shopping Cart setup).
Before decryption, the parameters passed with the redirect will appear as follows:
EncryptedParameters=QzFtdn0%2B66KJV5L8ihbr6ofdmrkEQwqMXI3ayF7UpVlRheR7r5fA6
IqBszeKFoGSyR7c7J4YsXgaOergu5SWD%2FvL%2FzPSrZER9BS7mZGckriBrhYt%2FKMAbTSS8F
XR72gWJZsul9aGyGbFripp7XxE9NQHVMWCko0NlpWe7oZ0RBIgNpIZ3JojAfX7b1j%2F5ACJ79S
VeOIK80layBwCmIPOpB%2B%2BNI6krE0wekvkkLKF7CXilj5qITvmv%2FpMqwVDchv%2FUNMfCi
4uUA4igHGhaZDQcV8U%2BcYRO8dv%2FnqVbAjkNwBqxqN3UPNFz0Tt76%2BP7H48PDpU23c61eM
7mx%2FZh%2Few5Pd0WkiCwZVkSZoov97BWdnMIw5tOAiqHvAR3%2BnfmGsx
Westpac has no Rails demos but they do have PHP. Here is the PHP demo:
function decrypt_parameters( $base64Key, $encryptedParametersText, $signatureText )
{
$key = base64_decode( $base64Key );
$iv = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
$td = mcrypt_module_open('rijndael-128', '', 'cbc', '');
// Decrypt the parameter text
mcrypt_generic_init($td, $key, $iv);
$parametersText = mdecrypt_generic($td, base64_decode( $encryptedParametersText ) );
$parametersText = pkcs5_unpad( $parametersText );
mcrypt_generic_deinit($td);
}
Here is what I've tried in Rails:
def Crypto.decrypt(encrypted_data, key, iv, cipher_type)
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
aes.decrypt
aes.key = key
aes.iv = iv if iv != nil
aes.update(encrypted_data) + aes.final
end
iv = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
key = Base64.decode64("mysecretkey")
data = Base64.decode64("QzFtdn0%2B66KJV5L8ihbr6ofdmrkEQwqMXI3ayF7UpVlRheR7r5fA6
IqBszeKFoGSyR7c7J4YsXgaOergu5SWD%2FvL%2FzPSrZER9BS7mZGckriBrhYt%2FKMAbTSS8F
XR72gWJZsul9aGyGbFripp7XxE9NQHVMWCko0NlpWe7oZ0RBIgNpIZ3JojAfX7b1j%2F5ACJ79S
VeOIK80layBwCmIPOpB%2B%2BNI6krE0wekvkkLKF7CXilj5qITvmv%2FpMqwVDchv%2FUNMfCi
4uUA4igHGhaZDQcV8U%2BcYRO8dv%2FnqVbAjkNwBqxqN3UPNFz0Tt76%2BP7H48PDpU23c61eM
7mx%2FZh%2Few5Pd0WkiCwZVkSZoov97BWdnMIw5tOAiqHvAR3%2BnfmGsx")
cleartext = Crypto.decrypt(data, key, iv, "AES-128-CBC")
And I simply pass in the same initialization vector as noted in the PHP, though I'm not sure this is correct for Rails.
In any event, the key is provided and easy to Base64 decode, as are the Encrypted Parameters. At the end of the day, I'm getting this error:
cipher.rb:21:in `final': wrong final block length (OpenSSL::Cipher::CipherError)
from cipher.rb:21:in `decrypt'
from cipher.rb:29:in `<main>'
Admittedly, I'm out of my depth on this Crypto stuff but am up against a wall and do not have the time (despite the interest) to learn more.
The problem was, that the input data was additionally "URI-escaped" and ruby's base64-decoder did not "care" about the invalid base64-input (% is no base64-digit), so no error was raised.
The solution was to "unescape" the URI-encoding with URI.unescape:
require 'uri'
data = Base64.decode64(
URI.unescape("QzFtdn0%2B66 ... Iw5tOAiqHvAR3%2BnfmGsx"))
Of course, if the input data is received from a GET/POST parameter, the input data is most probably already "unescaped" by your web-stack - just as a note of caution (double unescape may cause problems if a percent-sign % appears in the input data).

Codesigning SWF?

AIR allows to inject code using Loader.LoadBytes()
this allows to download remote plugins as swf files which will have full access to everything that the AIR application has access to. This imposes a security risk, so it would be desirable to digitally sign the swf's.
What's the best way to do this and to verify the code signature?
I know the as3corelib has some encryption functionality and also for X.509 certificate - but I didn't find a ressource explaining how to use it. Also, maybe there's some 'official' way to codesign SWF's?
One robust method is using public key encryption, which goes something like this:
You will need an asymmetric encryption algorithm (eg, RSA), and a hash algorithm (eg, SHA, MD5).
Generate a public-private key pair.
Generate and checksum of the data using the hash algorithm.
Encrypt the checksum with the private key using the encryption algorithm. This becomes the "signature".
Send the data to the client along with the signature.
Decrypt the signature on the client with the public key to obtain the original checksum.
Generate a checksum from the data on the client.
Compare the checksums. If they match, then you know that the data came from you without alterations. If they do not match then you know the data was altered after it was sent from you, or it came from someone else.
See http://en.wikipedia.org/wiki/Public-key_cryptography
An attacker can bypass this security if they are able to intercept the connection and modify the original client SWF file and either change the public key, or remove the security mechanism entirely. Use TLS or SSL to prevent attackers intercepting the data.
An x.509 certificate is little more than a public key bundled with some meta-data. The standard also specifies a mechanism for validating the certificate, by relying on a certificate authority (CA) (see http://en.wikipedia.org/wiki/X.509).
The AS3Crypto library provides (amongst other things), an implementation of RSA, MD5, and an x.509 parser (see http://code.google.com/p/as3crypto/).
Here is some code. The signing process entails computing the hash of the data, then signing it with the private key to produce a signature, eg:
var rsa:RSAKey;
var md5:MD5;
var data:ByteArray = getSWFBytes();
var signature:ByteArray = new ByteArray();
var originalHash:ByteArray;
// read private key
rsa = PEM.readRSAPrivateKey(private_key);
// create the checksum of the original data
md5 = new MD5();
originalHash = md5.hash(original);
// encrypt the data using the private key
rsa.sign(data, signature, original.length);
The data and signature are sent to the client. The client decrypts the signature using the public key stored in the cert and compare it to the computed hash of the data, eg:
var rsa:RSAKey;
var md5:MD5;
var data:ByteArray = getSWFBytes();
var signature:ByteArray = new ByteArray();
var decryptedHash:ByteArray = new ByteArray();
var clientHash:ByteArray;
// load the certificate
var cert:X509Certificate = new X509Certificate(public_cert);
// get the public key from the cert
rsa = cert.getPublicKey();
// decrypt the signature with the public key
rsa.verify(signature, decryptedHash, encrypted.length);
// create a hash of the data
md5 = new MD5();
clientHash = md5.hash(data);
// compare the hashes
// isEqual compares the bytes in the input byte arrays, it returns true only of all bytes in both arrays match
if (isEqual(clientHash, decryptedHash))
trace("signature valid");
else
trace("signature invalid")
You can check if the certificate is signed like this:
var store:X509CertificateCollection = new MozillaRootCertificates();
var cert:X509Certificate = new X509Certificate(public_cert);
var isValid:Boolean = cert.isSigned(store, store);
You can load the raw SWF bytes like this:
var loader:URLLoader = new URLLoader();
loader.dataFormat = URLLoaderDataFormat.BINARY;
loader.addEventListener(Event.COMPLETE, completeHandler);
loader.load(new URLRequest(url_of_swf_to_load));
Example x.509 private key (usually created when you apply for a certificate):
-----BEGIN RSA PRIVATE KEY-----
MIICWwIBAAKBgQDoKlLzpJeLcoPYQQYPa0diM4zpZ+0rKeRxhx9ssq91DzwAeSmM
7wT03WLiLZkqPt2MS3uNo75zK5RtmjHqF6Ojfs2tbSdlCK5tpisvOAssuq0o5vIz
g/MhS2PIijnBtVB9XFSTXxhveKeIq1VgdB2wHW95+zhBF+Z1hsYcNRRFFwIDAQAB
AoGAI8wK2EhjmXvBuoFkJtJ6wjiCnKaKmiIueBbGkKMIjLsZnFUSRAnCsOLF0WwI
dswUqwIkfdVmkymADFo/IgIdF9hLGNLRskIPKGZWEUC8d5ZJnRg+nuzi2c2msN5u
/BvCCgL5/shBhO5KvrPbU/Fbs/k4saCDQZ2EO4HpueRZWGkCQQD6hC0pTfyW4yQT
Qr/dY7FhOwdOh/8ewGyXBa9ruOuZqTR23Ya20O8NuF22+NqW9AZl7uioiTZyZkOV
jqAckelrAkEA7T9QVdK+QcaQSznrZPJpXlSIDLSBRWjaPKBoypnNTF3y3JkUQE0L
iA0c2oUc8D+LCgx9vA0Ai0IzwzrIec+iBQJAJb5YV4rKbalXPBeodKCajv2nwis3
QtjXA4H1xhMcXBBkOSxzKYQdIEIQzIp91JR7ikwOfaX+sAm8UQImGWfadQJAMAb4
KVePQluDDGd+OqJEKF9uZzwHS1jNjSZf8FuwTrxaFMQ8cEPoiLM22xnFYPFMIU2k
CnSLXqWZOvVkbhxVTQJAL3xIc5AUbhsEp7ZeeJrkPRv5rCObmLw0+wIaERtMX83b
PNM0TpzY6EXk+geTCqudAipYF/A7qn38wpOh+PuuVg==
-----END RSA PRIVATE KEY-----
Example cert:
-----BEGIN CERTIFICATE-----
MIID4zCCA0ygAwIBAgIJAL7k5X3sCvniMA0GCSqGSIb3DQEBBQUAMIGoMQswCQYD
VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTERMA8GA1UEBxMIU2FuIEpvc2Ux
FDASBgNVBAoTC2h1cmxhbnQuY29tMRcwFQYDVQQLEw5hczMgY3J5cHRvIGxpYjEY
MBYGA1UEAxMPSGVucmkgVG9yZ2VtYW5lMSgwJgYJKoZIhvcNAQkBFhloZW5yaV90
b3JnZW1hbmVAeWFob28uY29tMB4XDTA3MTEwNTA1MjUyOVoXDTA4MTEwNDA1MjUy
OVowgagxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMREwDwYDVQQH
EwhTYW4gSm9zZTEUMBIGA1UEChMLaHVybGFudC5jb20xFzAVBgNVBAsTDmFzMyBj
cnlwdG8gbGliMRgwFgYDVQQDEw9IZW5yaSBUb3JnZW1hbmUxKDAmBgkqhkiG9w0B
CQEWGWhlbnJpX3RvcmdlbWFuZUB5YWhvby5jb20wgZ8wDQYJKoZIhvcNAQEBBQAD
gY0AMIGJAoGBAOgqUvOkl4tyg9hBBg9rR2IzjOln7Ssp5HGHH2yyr3UPPAB5KYzv
BPTdYuItmSo+3YxLe42jvnMrlG2aMeoXo6N+za1tJ2UIrm2mKy84Cyy6rSjm8jOD
8yFLY8iKOcG1UH1cVJNfGG94p4irVWB0HbAdb3n7OEEX5nWGxhw1FEUXAgMBAAGj
ggERMIIBDTAdBgNVHQ4EFgQU/XyNp2QghYm3MWOU5YoUoFWcTKMwgd0GA1UdIwSB
1TCB0oAU/XyNp2QghYm3MWOU5YoUoFWcTKOhga6kgaswgagxCzAJBgNVBAYTAlVT
MRMwEQYDVQQIEwpDYWxpZm9ybmlhMREwDwYDVQQHEwhTYW4gSm9zZTEUMBIGA1UE
ChMLaHVybGFudC5jb20xFzAVBgNVBAsTDmFzMyBjcnlwdG8gbGliMRgwFgYDVQQD
Ew9IZW5yaSBUb3JnZW1hbmUxKDAmBgkqhkiG9w0BCQEWGWhlbnJpX3RvcmdlbWFu
ZUB5YWhvby5jb22CCQC+5OV97Ar54jAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEB
BQUAA4GBABsXUJjiRAz+FeiVq4JMSBWeiiGcXTw+8sNv8SfWaWx3su+AgooKlBn3
nsGKf3BEDdmJCOSgY0+A5Pce9SRoAMhabHKwoLEogrtp2p8vRj2OTMjWBW7ylrxj
FvUpFdc8qFaqTtgH6+JiIYllGFlcsSV+6d9fDPaFDZEHjz5GweWJ
-----END CERTIFICATE-----
Both examples were taken from as3crypto.

Resources