Generate multiple hash values from one stream with Crypto in Node.js - javascript

I am working on a Node.js application. Readable stream from a child process' output is being piped into a writable stream from a Crypto module to generate 4 hash values (md5, sha1, sha256 and sha512). This module only allows to generate one hash at a time. After hashes are created, both this stream and hash values should be somehow passed further in order to be recorded to a file. I am currently stuck at creating the first hash value. How can I solve this problem? Part of the code can be seen below.
import Crypto from 'crypto';
let md5 = Crypto.createHash('md5');
//How to generate another hash using the same input
md5.on('readable', () => {
const data = md5.read();
if (data) {
console.log(md5.read());
}
})
...
childProc.stdout.pipe(md5);

You can create multiple hashes from a single stream by piping to multiple transform streams.
import Crypto from 'crypto';
let md5 = Crypto.createHash('md5');
let sha1 = Crypto.createHash('sha1');
md5.on('readable', () => {
const data = md5.read();
if (data) console.log(data.toString('hex'));
})
sha1.on('readable', () => {
const data = sha1.read();
if (data) console.log(data.toString('hex'));
})
childProc.stdin.pipe(md5)
childProc.stdin.pipe(sha1)
This code will print the hash for each algorithm to stdout.

Related

React Native Expo - FileSystem readAsStringAsync Byte Allocation Failed (Out of Memory)

I am creating an Android App using React Native with Expo Module (FileSystem and Expo AV) to record a local video using the phone's camera, then I send the encoded base64 video to the server.
The code to send the base64 string looks like this:
const encodeBase64 = async () => {
const fileUri = videoUri;
const options = {
encoding: FileSystem.EncodingType.Base64,
};
let result = await FileSystem.readAsStringAsync(fileUri, options);
return result;
};
const upload = async () => {
const base64 = await encodeBase64(videoUri);
const result = await myAPI(base64);
}
It works on my phone (Oppo A3s), but on another phone like Samsung A51, it gives memory allocation error like this:
How to solve this problem?
This is memory error.
Every phone's storage is different each other.
You can use chunk buffer.
So in this case you can split your base64 data to post to server and combine data in server.
ex: client=> chunkbuffer=>1024*100(size)
server=> combine(array of client's data)
Good luck.
If you have any question please contact me.
I will help you anytime.

How can I get window.crypto.subtle to output the same signature as the 'crypto' js library?

I'm wanting to use the built in browser crypto object instead of importing the crypto library in my browser project.
I want to not need this.
npm i crypto
The following code illustrates trying to sign a simple message using both libraries, however I get a different result when using the built in crypto object. The outputs are logged to console.
// START CRYPTO LIBRARY METHOD
import crypto from 'crypto';
const HMAC_SHA256_KEY = {
API_KEY: '***',
SECRET_KEY: '***'
};
const queryString = `timestamp=${(new Date().getTime())}`;
const signature = crypto
.createHmac('sha256', HMAC_SHA256_KEY.SECRET_KEY)
.update(queryString)
.digest('hex')
console.log("standard encrypt", signature);
// START NATIVE LIBRARY METHOD
const getUtf8Bytes = (str: string) => {
return new Uint8Array(
[...(unescape(encodeURIComponent(str)))].map(c => c.charCodeAt(0))
);
};
// #ts-ignore
const builtInCrypto = window.crypto || window.msCrypto;
builtInCrypto.subtle.importKey(
'raw',
getUtf8Bytes(HMAC_SHA256_KEY.SECRET_KEY),
{
name: 'HMAC', hash: 'SHA-256'
},
true, ['sign']
).then(key => {
builtInCrypto.subtle.sign(
'HMAC',
key,
getUtf8Bytes(queryString))
.then(signature => {
console.log('builtIn Signature', btoa(String.fromCharCode(...(new Uint8Array(signature)))));
})
})
Clearly I'm doing something wrong, but what? Why am I getting a different output for the native encryption?
For the same values of HMAC_SHA256_KEY.SECRET_KEY and queryString (the last parameter depends on time and therefore has a different value for each run) both scripts return the same HMac but in different encodings, resulting in different output. In the NodeJS / crypto code the HMac is hex encoded, in the JavaScript / Web Crypto API code Base64 encoded.
To make sure that the output is identical, the same encoding must be used in both scripts. Either the HMac in the JavaScript / Web Crypto API code must also be hex encoded, see e.g. here for a suitable method, or the HMac in the NodeJS / crypto code must be Base64 encoded (by passing 'base64' instead of 'hex' as argument of digest).

Cannot get the same md5 hash of photo client-side to successfully pass Firebase Storage security rules

I'd like to upload files with uniform names by generating a md5 hash client-side. However, I didn't manage to successfully pass the Firebase Storage security rules at this point:
match /{userUid}/{photoHash} {
allow create: if request.resource.md5Hash == photoHash;
}
Client-side code snippet uploading the photo:
const compressedPhoto = await this.compressPhoto(photo)
const base64 = await this.toBase64(compressedPhoto)
const photoHash = CryptoJS.MD5(compressedPhoto).toString(CryptoJS.enc.Base64)
const uploadTask = this.storageRef.child(photoHash).put(compressedPhoto)

Node HmacSHA1 Seed

I'm trying to send a SOAP request via Node, talking to a service which is secured with WSS.
I need to sign the XML response with a SignedInfo element which requires me combining a Nonce Binary secret I generated, with a Nonce binary secret returned from the initial token request - PSHA1 format.
I've been able to validate this using Java, by utilising the following class (Where the secret is my client nonce and the seed is the server nonce):
https://github.com/apache/wss4j/blob/trunk/ws-security-common/src/main/java/org/apache/wss4j/common/derivedKey/P_SHA1.java#L57
With the following Java code:
Mac mac = Mac.getInstance("HmacSHA1");
SecretKeySpec key = new SecretKeySpec(getSharedKey(), "HmacSHA1");
mac.init(key);
String bytesToSign = "<XML_TO_SIGN_GOES_HERE>";
String signature = Base64.encodeBytes(mac.doFinal(bytesToSign.getBytes()));
I need to do this in a Node project though, I've looked at the Crypto API and numerous plugins but I'm unable to generate the same signature.
How do I specify a seed for a HmacSHA1 using node?
I managed to get there in the end, there's an NPM module called psha1 (https://www.npmjs.com/package/psha1).
Using that library I created the following a generateSignature module which looks as follows:
const crypto = require('crypto');
const psha1 = require('psha1');
export const generateSignatureValue = ({
clientSecret,
serverSecret,
messageToSign,
}) => {
const secretKey =
psha1(clientSecret, serverSecret, 256);
const hash =
crypto
.createHmac('sha1', Buffer.from(secretKey, 'base64'))
.update(messageToSign)
.digest('binary');
return Buffer
.from(hash, 'binary')
.toString('base64');
};
export default generateSignatureValue;
This gives me the desired output :)

How to pipe a data stream into a function that's assigned to a constant?

The example below from Google works but it uses pipe. For my situation I'm listening to a websocket that sends packets in 20ms increments and from what I've been able to find there is no way to pipe that data into a function.
The first argument that must be passed on initialization is a config object. After that only data is accepted. So I set the function up as a variable, then pass the config. But I can't figure out how to pass the stream of data into it afterwards. How do I pass data into recognizeStream without using pipe? Or is there are a way to use pipe with websocket
I can vouch for this setup working by reading and writing from temporary files at certain intervals but this has the obvious disadvantages of 1) all of that overhead and 2) most importantly, not being a real-time stream.
There are two solutions that I think would work but have not been able to implement:
There is some way to setup a pipe from websocket (This is ideal)
Simultaneously writing the data to a file while at the same time reading it back using createReadStream from a file using some implementation of fs (This seems like a minefield of problems)
tl;dr I need to send the stream of data from a websocket into a function assigned to a const as the data comes in.
Example setup from Google Docs
const Speech = require('#google-cloud/speech');
// Instantiates a client
const speech = Speech();
// The encoding of the audio file, e.g. 'LINEAR16'
const encoding = 'LINEAR16';
// The sample rate of the audio file, e.g. 16000
const sampleRate = 16000;
const request = {
config: {
encoding: encoding,
sampleRate: sampleRate
}
};
const recognizeStream = speech.createRecognizeStream(request)
.on('error', console.error)
.on('data', (data) => process.stdout.write(data.results));
// Start recording and send the microphone input to the Speech API
record.start({
sampleRate: sampleRate,
threshold: 0
}).pipe(recognizeStream);
Websocket setup
const WebSocketServer = require('websocket').server
wsServer.on('connect', (connection) => {
connection.on('message', (message) => {
if (message.type === 'utf8') {
console.log(message.utf8Data)
} else if (message.type === 'binary') {
// send message.binaryData to recognizeStream
}
})
})
You should just be able to do:
recognizeStream.write(message.binaryData)

Categories

Resources