Random bytes and encodings - random

I have a function that needs a random sequence of bytes as input (e.g. a salt for hashing a password). I generate that string using a CSPRNG function and then encode in to base64.
Now I pass that string to the function that needs it, but that function works with bytes, so if it receive a string it turns it into a byte-buffer by reading the string as utf8. The string given as input is not the same sequence of bytes generated with the CSPRNG function but is the utf8 decoded string of the base64 encoded random bytes. So if I generate N bytes, the transformations with encodings turns it in 4/3*N bytes. Can I assume that these expanded bytes are still random after the transformations? Are there any security implications?
Here's a pseudo code to make it more clear:
function needsRandBytes(rand) {
if (typeof rand == 'string') {
rand = Buffer.from(rand, 'utf8'); // here's the expansion
}
// use the rand bytes...
}
randBytes = generateRandomBytes(N); // cryptographically secure function
randString = randBytes.toString('base64');
needsRandBytes(randString);

Related

Encoding byte array for performUpkeep() in Chainlink Keepers

We have this code below which will encode a uint256 array into a byte array.
For some reason, performUpkeep() will be successful in Remix IDE but not using Hardhat.
function checkUpkeep(bytes calldata checkData) external view override returns (bool, bytes memory) {
bool flag = false;
uint256 counter = 0;
uint256[] memory numbers = new uint256[](10);
for (uint256 i = 0; i < 10; i+=2) {
numbers[counter++] = i;
}
bytes memory checkData = abi.encode(numbers);
return (flag, checkData);
}
function performUpkeep(bytes calldata performData) external override {
uint256[] memory numbers = abi.decode(performData, (uint256[]));
console.log("The first number is:", numbers[0]);
}
The end goal here is to pass data through to performUpkeep succesfully.
The performUpkeep() function takes an argument of type bytes. It then decodes that argument into an array of uint256. Before passing data to performUpkeep() in Hardhat, you must encode the array of uint256 you wish to pass as an argument according to the standard EVM encoding format.
To do so in Hardhat (Javascript) you can use the ethers.js library to access the defaultAbiCoder object and encode your arguments using it. An example can be seen below
let abiCoder = ethers.utils.defaultAbiCoder;
let dataEncoded = abiCoder.encode([ "uint[]"], [[ 1, 2, 3]]);
await keeperContract.performUpkeep(dataEncoded)

How to covert String input into byte array?

I'm trying to covert the string input in the serial to byte of array to write in a block in RFID card
String puta = "PUTANGINA";
byte blockcontent[16] = {puta};
I expect the output will be the string will be byte array.
You can use the getBytes() this copies the string's characters to the supplied buffer.
Syntax:
string.getBytes(buf, len)
Parameters:
string: a variable of type String
buf: the buffer to copy the characters into (byte [])
len: the size of the buffer (unsigned int)
Returns
None
https://www.arduino.cc/en/Reference.StringGetBytes

How do I decrypt data encrypted by Ruby's `symmetric-encryption` gem in another language?

I want to access data in a database created by Rails for use by non-Ruby code. Some fields use attr_encrypted accessors, and the library in use is the symmetric-encryption gem. I consistently get a "wrong final block length" error if I try to decrypt the data with, e.g., the NodeJS crypto library.
I suspect this has to do either with character encoding or with padding, but I can't figure it out based on the docs.
As an experiment, I tried decrypting data from symmetric-encryption in Ruby's own OpenSSL library, and I get either a "bad decrypt" error or the same problem:
SymmetricEncryption.cipher = SymmetricEncryption::Cipher.new(
key: "1234567890ABCDEF",
iv: "1234567890ABCDEF",
cipher_name: "aes-128-cbc"
)
ciphertext = SymmetricEncryption.encrypt("Hello world")
c = OpenSSL::Cipher.new("aes-128-cbc")
c.iv = c.key = "1234567890ABCDEF"
c.update(ciphertext) + c.final
That gives me a "bad decrypt" error.
Interestingly, the encrypted data in the database can be decrypted by the symmetric-encryption gem, but isn't the same as the output of SymmetricEncryption.encrypt (and OpenSSL doesn't successfully decrypt it, either).
Edit:
psql=# SELECT "encrypted_firstName" FROM people LIMIT 1;
encrypted_firstName
----------------------------------------------------------
QEVuQwBAEAAuR5vRj/iFbaEsXKtpjubrWgyEhK5Pji2EWPDPoT4CyQ==
(1 row)
Then
irb> SymmetricEncryption.decrypt "QEVuQwBAEAAuR5vRj/iFbaEsXKtpjubrWgyEhK5Pji2EWPDPoT4CyQ=="
=> "Lurline"
irb> SymmetricEncryption.encrypt "Lurline"
=> "QEVuQwAAlRBeYptjK0Fg76jFQkjLtA=="
Looking at the source for the symmetric-encryption gem, by default it adds a header to the output and base64 encodes it, although both of these are configurable.
To decrypt using Ruby’s OpenSSL directly, you will need to decode it and strip off this header, which is 6 bytes long in this simple case:
ciphertext = Base64.decode64(ciphertext)
ciphertext = ciphertext[6..-1]
c = OpenSSL::Cipher.new("aes-128-cbc")
c.decrypt
c.iv = "1234567890ABCDEF"
c.key = "1234567890ABCDEF"
result = c.update(ciphertext) + c.final
Of course, you may need to alter this depending on what settings you are using in symmetric-encryption, e.g. the header length may vary. In order to decrypt the result from the database you will need to parse the header. Have a look at the source.
Based on the Rust implementation done by #Shepmaster in my other question (and the source code for the symmetric-encryption gem), I have a working version in TypeScript. #matt is close with his answer, but the header can actually have additional bytes containing metadata about the encrypted data. Note that this doesn't handle (1) compressed encrypted data, or (2) setting the encryption algorithm from the header itself; neither situation is relevant to my use case.
import { createDecipher, createDecipheriv, Decipher } from "crypto";
// We use two types of encoding with SymmetricEncryption: Base64 and UTF-8. We
// define them in an `enum` for type safety.
const enum Encoding {
Base64 = "base64",
Utf8 = "utf8",
}
// Symmetric encryption's header contains the following data:
interface IHeader {
version: number, // The version of the encryption algo
isCompressed: boolean, // Whether the data is compressed (TODO: Implement)
hasIv: boolean, // Whether the header itself has the IV
hasKey: boolean, // Whether the header itself has the Key
hasCipherName: boolean, // Whether the header contains the cipher name
hasAuthTag: boolean, // Whether the header has an authorization tag
offset: number, // How many bytes into the encoded ciphertext the actual encrypted data starts
iv?: Buffer, // The IV, present only if `hasIv` is true
key?: Buffer, // The key, present only if `hasKey` is true
// The cipher name, present only if `hasCipherName` is true. Currently ignored.
cipherName?: string,
authTag?: string, // The authorization tag, present only if // `hasAuthTag` is true
}
// Byte 6 of the header contain bit flags
interface IFlags {
isCompressed: boolean,
hasIv: boolean,
hasKey: boolean,
hasCipherName: boolean,
hasAuthTag: boolean
}
// The 7th byte until the end of the header have the actual values. If all
// of the flags are false, the header ends at the 6th byte.
interface IValues {
iv?: Buffer,
key?: Buffer,
cipherName?: string,
authTag?: string,
size: number,
}
/**
* Represent the encoded ciphertext, complete with the SymmetricEncryption header.
*/
class Ciphertext {
// Bit flags corresponding to the data encoded in byte 6 of the
// header.
readonly FLAG_COMPRESSED = 0b1000_0000;
readonly FLAG_IV = 0b0100_0000;
readonly FLAG_KEY = 0b0010_0000;
readonly FLAG_CIPHER_NAME = 0b0001_0000;
readonly FLAG_AUTH_TAG = 0b0000_1000;
// The literal data encoded in bytes 1 - 4 of the header
readonly MAGIC_HEADER = "#EnC";
// If any of the values represented by the bit flags is present, the first 2
// bytes of the data tells us how long the actual value is. In other words,
// the first 2 bytes aren't the value itself, but rather give the info about
// the length of the rest of the value.
readonly LENGTH_INFO_SIZE = 2;
public header: IHeader | null;
public data: Buffer;
private cipherBuffer: Buffer;
constructor(private input: string) {
this.cipherBuffer = new Buffer(input, Encoding.Base64);
this.header = this.getHeader();
const offset = this.header ? this.header.offset : 0; // If no header, then no offset
this.data = this.cipherBuffer.slice(offset);
}
/**
* Extract the header from the data
*/
private getHeader(): IHeader | null {
let offset = 0;
// Bytes 1 - 4 are the literal `#EnC`. If that's absent, there's no
// SymmetricEncryption header.
if (this.cipherBuffer.toString(Encoding.Utf8, offset, offset += 4) != this.MAGIC_HEADER) {
return null;
}
// Byte 5 is the version
const version = this.cipherBuffer.readInt8(offset++); // Post increment
// Byte 6 is the flags
const rawFlags = this.cipherBuffer.readInt8(offset++);
const flags = this.readFlags(rawFlags);
// Bytes 7 - end are the values.
const values = this.getValues(offset, flags);
offset += values.size;
return Object.assign({ version, offset }, flags, values);
}
/**
* Get the values for `iv`, `key`, `cipherName`, and `authTag`, if any are
* set, based on the bitflags. Return that data, plus how many bytes in the
* header those values represent.
*
* #param offset - What byte we're on when we get to the values. Should be 7
* #param flags - The flags we've extracted, showing us which values to expect
*/
private getValues(offset: number, flags: IFlags): IValues {
let iv: Buffer | undefined = undefined;
let key: Buffer | undefined = undefined;
let cipherName: string | undefined = undefined;
let authTag: string | undefined = undefined;
let size = 0; // If all of the bit flags are false, there is no additional data.
// For each value, see if the flag is set to true. If it is, we need to
// read the value. Keys and IVs need to be `Buffer` types; other values
// should be strings.
[iv, size] = flags.hasIv ? this.readBuffer(offset) : [undefined, size];
[key, size] = flags.hasKey ? this.readBuffer(offset + size) : [undefined, size];
[cipherName, size] = flags.hasCipherName ? this.readString(offset + size) : [undefined, size];
[authTag, size] = flags.hasAuthTag ? this.readString(offset + size) : [undefined, size];
return { iv, key, cipherName, authTag, size };
}
/**
* Parse the 16-bit integer representing the bit flags into an object for
* easier handling
*
* #param flags - The 16-bit integer that contains the bit flags
*/
private readFlags(flags: number): IFlags {
return {
isCompressed: (flags & this.FLAG_COMPRESSED) != 0,
hasIv: (flags & this.FLAG_IV) != 0,
hasKey: (flags & this.FLAG_KEY) != 0,
hasCipherName: (flags & this.FLAG_CIPHER_NAME) != 0,
hasAuthTag: (flags & this.FLAG_AUTH_TAG) != 0
}
}
/**
* Read a string out of the value at the specified offset. Return the value
* itself, plus the number of bytes consumed by the value (including the
* 2-byte encoding of the length of the actual value).
*
* #param offset - The offset (bytes from the beginning of the encoded,
* encrypted Buffer) at which the value in question begins
*/
private readString(offset: number): [string, number] {
// The length is the first 2 bytes, encoded as a little-endian 16-bit integer
const length = this.cipherBuffer.readInt16LE(offset);
// The total size occupied in the header is the 2 bytes encoding length plus the length itself
const size = this.LENGTH_INFO_SIZE + length;
const value = this.cipherBuffer.toString(Encoding.Base64, offset + this.LENGTH_INFO_SIZE, offset + size);
return [value, size];
}
/**
* Read a Buffer out of the value at the specified offset. Return the value
* itself, plus the number of bytes consumed by the value (including the
* 2-byte encoding of the length of the actual value).
*
* #param offset - The offset (bytes from the beginning of the encoded,
* encrypted Buffer) at which the value in question begins
*/
private readBuffer(offset: number): [Buffer, number] {
// The length is the first 2 bytes, encoded as a little-endian 16-bit integer
const length = this.cipherBuffer.readInt16LE(offset);
// The total size occupied in the header is the 2 bytes encoding length plus the length itself
const size = this.LENGTH_INFO_SIZE + length;
const value = this.cipherBuffer.slice(offset + this.LENGTH_INFO_SIZE, offset + size);
return [value, size];
}
}
/**
* Allow decryption of data encrypted by Ruby's `symmetric-encryption` gem
*/
class SymmetricEncryption {
private key: Buffer;
private iv?: Buffer;
constructor(key: string, private algo: string, iv?: string) {
this.key = new Buffer(key);
this.iv = iv ? new Buffer(iv) : undefined;
}
public decrypt(input: string): string {
const ciphertext = new Ciphertext(input);
// IV can be specified by the user. But if it's encoded in the header
// itself, go with that instead.
const iv = (ciphertext.header && ciphertext.header.iv) ? ciphertext.header.iv : this.iv;
// Key can be specified by the user. but if it's encoded in the header,
// go with that instead.
const key = (ciphertext.header && ciphertext.header.key) ? ciphertext.header.key : this.key;
const decipher: Decipher = iv ?
createDecipheriv(this.algo, key, iv) :
createDecipher(this.algo, key);
// Terse version of `update()` + `final()` that passes type checking
return Buffer.concat([decipher.update(ciphertext.data), decipher.final()]).toString();
}
}
const s = new SymmetricEncryption("1234567890ABCDEF", "aes-128-cbc", "1234567890ABCDEF");
console.log(s.decrypt("QEVuQwAADWK0cKzgFIovdIThq9Scrg==")); // => "Hello world"

Encoded output through sha512 algorithm does not started with $6

public static String sha(String base) {
try{
MessageDigest digest = MessageDigest.getInstance("SHA-512");
byte[] hash = digest.digest(base.getBytes("UTF-8"));
StringBuffer hexString = new StringBuffer();
for (int i = 0; i < hash.length; i++) {
String hex = Integer.toHexString(0xff & hash[i]);
if(hex.length() == 1) hexString.append('0');
hexString.append(hex);
}
return hexString.toString();
} catch(Exception ex){
throw new RuntimeException(ex);
}
}
the method above generate a string which is not started by $6.
i.e :
"0000" --> c6001d5b2ac3df314204a8f9d7a00e1503c9aba0fd4538645de4bf4cc7e2555cfe9ff9d0236bf327ed3e907849a98df4d330c4bea551017d465b4c1d9b80bcb0
However , we know that the first 2 or 3 chars indicates the hashing algorithm .
ie :
Blowfish --> $2$ or $2a$
SHA-512 --> $6$
- Is there a difference between encoding and hashing ?
or what is the story ?
UPDATE :
Linux crypt command line generates a string with 86 chars, however Java8 generates 128 chars .
#man 3 crypt
MD5 | 22 characters
SHA-256 | 43 characters
SHA-512 | 86 characters
It’s not clear why you expect a Java API function to produce the output of a GNU extension of a POSIX API function. Besides that, as you are generating the string in the Java code, you are the one who controls the size of the generated string.
The byte array returned by digest of SHA-512 has exactly 66 bytes which shouldn’t come to surprise as 66 (bytes) * 8 == 512 (bits). This is what an SHA-512 hash is all about. It was your decision to encode it in hexadecimal form, which needs one character for four bits, hence 512/4 == 128 characters.
By the way, even for that functionality, it’s worth getting used to the existing APIs, Java programming language features and coding conventions:
public static String sha(String base) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-512");
StringBuilder hexString = new StringBuilder();
for(byte b: digest.digest(base.getBytes(StandardCharsets.UTF_8)))
hexString.append(String.format("%02x", b&0xff));
return hexString.toString();
} catch(NoSuchAlgorithmException ex){
throw new RuntimeException(ex);
}
}
However, if the crypt C function generates 86 bytes, it’s most likely using the Base64 encoding, which needs one character for six bits, as 512 / 6 == 85,33…, thus at least 86 chars.
If you want to encode using base64, you can use a standard API starting with Java 8, for older versions you need either a 3rd party library or implement it yourself.
public static String sha(String base) {
try{
MessageDigest digest = MessageDigest.getInstance("SHA-512");
byte[] hash = digest.digest(base.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(hash);
} catch(NoSuchAlgorithmException ex){
throw new RuntimeException(ex);
}
}
Note that the encoded string will have 88 characters as the standard base64 format has a padding, i.e. there will be always two = characters at the end here. If you know from context (like with SHA-512), that it has to be 512 bits, you can omit them when storing the result:
public static String sha(String base) {
try{
MessageDigest digest = MessageDigest.getInstance("SHA-512");
byte[] hash = digest.digest(base.getBytes(StandardCharsets.UTF_8));
String encoded = Base64.getEncoder().encodeToString(hash);
assert encoded.length()==88 && encoded.endsWith("==");
return encoded.substring(0, 86);
} catch(NoSuchAlgorithmException ex){
throw new RuntimeException(ex);
}
}
crypt(3) is a password hashing algorithm that is only based on SHA-512.
You can find a Java implementation in the Apache Commons Codec project:
https://commons.apache.org/proper/commons-codec/apidocs/org/apache/commons/codec/digest/Crypt.html
https://commons.apache.org/proper/commons-codec/apidocs/org/apache/commons/codec/digest/Sha2Crypt.html
The algorithm actually feeds the result of the first SHA-512 digest to it self several thousand times to be deliberately slow as this makes bruteforce cracking the password harder.

How to convert a number to a UTF8 char?

How to convert a number to a UTF8 char ?
It must not be a CTFE function because the input parameter is varying during the run-time. I guess there must be a std function for this but I cant find it. Thx.
import std.stdio, std.conv;
char utf8_RT(int nbr)
{
return to!char(nbr);
}
void main(string args[])
{
assert( utf8_RT(2665) == '\u2665' );
}
obviously fails.
I don't know what you mean by "UTF8 char".
If you want an UTF-32 character (i.e. dchar), you can simply use dchar(2665).
If you want an UTF-8 string (one Unicode character encoded as one or more UTF-8 code units, i.e. bytes), you can use to!string(dchar(2665)). Don't forget to import std.conv.
Replace 2665 with the name of your int variable, naturally.

Resources