How to generate an ECDSA sign without need of PEM key format in NodeJS? - node.js

I have to generate an ECDSA sign and decode it after.
I tried with two different forms: one gave me a sign of size 135, while the other gave me a sign with size 70. The first one was generated with a pem key (with asn1js and bn modules as the answer in this link) and the second was generated with 'raw' key (eccrypto module), as we can seen below:
const ecdsa = require('eccrypto');
shaMsg = crypto.createHash('sha256').update(myData).digest();
ecdsa.sign(privateKey, shaMsg).then(function(sig) {
console.log("Signature in DER format:", sig, '-------size: ', sig.length);
ecdsa.verify(publicKey(), msg, sig).then(function() {
console.log("Signature is OK");
}).catch(function() {
console.log("Signature is BAD");
});
});
The output for this code is:
Signature in DER format: < Buffer 30 44 02 20 56 5c 61 76 a7 17 3c 67 8d 04 54 dd d5 9a 81 3a e9 1f af a6 7e 2d 34 3e 78 78 47 fb 5e 8e 9c 79 02 20 78 a4 bb f6 20 41 7c 8c 59 1b 93 43 ... > -------size: 70
I need to decode my sign to make other operations. Can anyone help me with this?

As nobody helped, I did what I wanted with another module (elliptic):
var EC = require("elliptic").ec;
var ec = new EC("secp256k1");
var shaMsg = crypto.createHash("sha256").update(myData.toString()).digest();
var mySign = ec.sign(shaMsg, privateKey, {canonical: true});
I hope this helps someone else.

Related

Nodejs WebSocket response is encrypted or not readable (connecting to a exchange socket)

I am trying to make a request to a WebSocket of a crypto-exchange to get live market data for analysis.
In the Browser the data is shown in readable Text as it is used to populate the orderbook.
When trying to connect to it via nodejs I am getting a hex code that when converted still doesn't make sense.
const WebSocket = require('ws');
const sendData = '{"method":"deals.subscribe","params":["ETHBTC"],"id":18}'
// Establish a WebSocket connection to the echo server
const ws = new WebSocket('wss://ws.exchnageAddres.com');
ws.addEventListener('open', () => {
console.log('Sending:', sendData);
// Send message to the WebSocket server
ws.send(sendData);
});
ws.addEventListener('message', event => {
console.log('Received:', event.data);
});
Response:
Received: <Buffer 1f 8b 08 00 00 00 00 00 00 03 ab 56 4a 2d 2a ca 2f 52 b2 52 c8 2b cd c9 d1 51 50 2a 4a 2d 2e cd 29 01 f2 ab 95 8a 4b 12 4b 4a 8b 81 4c a5 e2 d2 e4 e4 ... 23 more bytes>
Received: <Buffer 1f 8b 08 00 00 00 00 00 00 03 9d 9a df 8a 5c 47 0e c6 5f 25 cc 75 28 24 95 a4 52 ed 65 96 85 7d 80 dc 85 5c 78 d7 86 35 d8 89 89 ed 8b 10 f2 ee fb 95 ... 2012 more bytes>
Received: <Buffer 1f 8b 08 00 00 00 00 00 00 03 55 8c b1 0a 83 30 14 45 7f 45 de 5c 42 e2 6b 92 da d1 52 e8 07 b8 89 43 da 04 2a 18 0d 26 19 44 fc f7 e6 15 3a 74 3d f7 ... 106 more bytes>
How can I connect to the ws in node?
Do I have to use a certificate or anything? Would be awesome if someone could help me out here.
Testing the connection on a chrome extension is giving me a Blob object but I can't really convert that to an array.
Using Burp Decoder works but only there.
Thanks and best regards.

publicEncrypt from crypto module, with RSA_PKCS1_PADDING, might return 255 bytes instead of 256

A project I work on uses the crypto module publicEncrypt function to encrypt some 32 bytes long session keys.
Due to a compatibility issue with the node-forge library we realized that, about once in a hundered iterations, the output of the publicEncrypt is 255 bytes long instead of 256.
We verified this behavior in Chrome, Firefox, and Opera, however we could not replicate the issue in a unit test (using Jest), because in this setting the output of publicEncrypt is consistently 256 bytes long.
Is this an expected behavior of the crypto module? By checking the RFC https://www.rfc-editor.org/rfc/rfc8017#page-28 this seems to be a bug to me.
Below the public key used in our tests and the unit test to illustrate the issue. To clarify, the unit test succeeeds in Jest, but calls to function publicCipher done within a browser sometimes create a 255 bytes long encrypted output.
Key Algorithm: RSA
Key Parameters: 05 00
Key Size: 2048
Key SHA1 Fingerprint: 89 BD 3D 96 DA 7E 65 60 DE B5 C2 33 5D 22 89 95 F0 44 90 EB
Public Key: 30 82 01 0A 02 82 01 01 00 AA 20 54 0F A4 1C 2A B3 BA FA C2 FB B3 C3 17 53 A0 3F C8 AB 7F 22 E0 60 E6 0A AD 80 0D EC 07 C7 FD E3 4A CD 5C 77 BC 0E FE 63 9F 6D 40 6B 21 84 20 2E F9 C4 6D 93 01 E4 CB EB D0 A5 C2 F5 F5 4F C4 F2 9E D8 C7 F5 2A C4 F2 D8 2D 87 A9 18 2B 6D B3 E2 04 EA 7B A6 5D 4A 55 1A 6D C9 1A E1 AF 4B 0C E1 FD 43 74 37 5B 1A 70 11 86 E7 E4 40 DD 6B 27 7C 72 C5 D0 15 79 B0 6D BC 07 F1 43 C7 BF D3 13 EC 94 83 D3 77 44 7B 58 2E 37 82 22 AC 07 7E 61 5F 7D A7 57 09 6C DB 24 24 5D 10 ED 83 DC AE 35 CE 6C 1E AB C2 9E 3A 1D 99 01 F9 0E 2C 6B A4 74 E0 87 1E 86 04 BC 72 A3 3E CF 46 76 57 AC 78 EC 41 AD 2D 82 E5 F5 A8 76 9D 25 0A 87 D8 D4 74 FA 64 9D 14 BF F4 17 B5 CF 2E D7 FF 71 41 94 5D 30 56 F0 17 9C ED 2F CB 66 D3 E5 7F 99 F9 31 42 EE DE 16 AE ED 38 BD E6 9A DD A6 50 75 30 5B D5 02 03 01 00 01
import { publicEncrypt, randomBytes } from 'crypto'
import constants from 'constants'
describe('Encryption output size test.', () => {
const publicKeyPem = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqiBUD6QcKrO6+sL7s8MX
U6A/yKt/IuBg5gqtgA3sB8f940rNXHe8Dv5jn21AayGEIC75xG2TAeTL69ClwvX1
T8TyntjH9SrE8tgth6kYK22z4gTqe6ZdSlUabcka4a9LDOH9Q3Q3WxpwEYbn5EDd
ayd8csXQFXmwbbwH8UPHv9MT7JSD03dEe1guN4IirAd+YV99p1cJbNskJF0Q7YPc
rjXObB6rwp46HZkB+Q4sa6R04IcehgS8cqM+z0Z2V6x47EGtLYLl9ah2nSUKh9jU
dPpknRS/9Be1zy7X/3FBlF0wVvAXnO0vy2bT5X+Z+TFC7t4Wru04veaa3aZQdTBb
1QIDAQAB
-----END PUBLIC KEY-----
`
it('Should create encrypted keys of 256 bytes.', (done) => {
for (let i = 0; i < 1000; i++) {
expect(publicCipher(publicKeyPem, randomBytes(32)).length).toEqual(256)
}
done()
})
})
function publicCipher(publicKeyPem: string, clearText: Buffer): Buffer {
return publicEncrypt({ key: publicKeyPem, padding: constants.RSA_PKCS1_PADDING }, clearText)
}
Update: the issue is due to the removal of leftmost 0 bytes.
I will try find a way to make this issue reproducible, meanwhile I think the following unit test should clarify what is happening (the key pair is self signed and used for testing purposes only, so I have no problem publishing the private key):
import forge from 'node-forge'
import { privateDecrypt } from 'crypto'
import * as constants from 'constants'
it('buggy encrypted random bits from crypto', (done) => {
const encryptedRandomBits = 'inYgmlfL4y0ab5tplmDKCeP0p7q2lcnMqUDjfQpUjqFJkiqoVXQ9A8pA8hyWBf2onea/+iVMtU/Xj9IyLvPQiLkUE9OWptzmwTqnXzzG3ulXUIjud+rpvJdENSJzsX7IwiNaAPWo3EmM57B/fdXaPPGZME203SDPRVWst2XZw7zBfeM2XmS4nSMvs7NM7rGR/Ti3Hb+p7D+PJZUm7oi3npFZHSS1C/SsAXUaAja3HPaXuve+VZfclqrSbQ2P+45cRyQpvcf+ueXqdk/Yqg/sOuufR8eFDyDgRudzMOzLuH6rzrNKYtMXpD9ueFWFRh0G2wOlXNc/acbv0nSN5Dvu'
const publicKeyPem = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqiBUD6QcKrO6+sL7s8MX
U6A/yKt/IuBg5gqtgA3sB8f940rNXHe8Dv5jn21AayGEIC75xG2TAeTL69ClwvX1
T8TyntjH9SrE8tgth6kYK22z4gTqe6ZdSlUabcka4a9LDOH9Q3Q3WxpwEYbn5EDd
ayd8csXQFXmwbbwH8UPHv9MT7JSD03dEe1guN4IirAd+YV99p1cJbNskJF0Q7YPc
rjXObB6rwp46HZkB+Q4sa6R04IcehgS8cqM+z0Z2V6x47EGtLYLl9ah2nSUKh9jU
dPpknRS/9Be1zy7X/3FBlF0wVvAXnO0vy2bT5X+Z+TFC7t4Wru04veaa3aZQdTBb
1QIDAQAB
-----END PUBLIC KEY-----
`
const privateKeyPem = `-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAqiBUD6QcKrO6+sL7s8MXU6A/yKt/IuBg5gqtgA3sB8f940rN
XHe8Dv5jn21AayGEIC75xG2TAeTL69ClwvX1T8TyntjH9SrE8tgth6kYK22z4gTq
e6ZdSlUabcka4a9LDOH9Q3Q3WxpwEYbn5EDdayd8csXQFXmwbbwH8UPHv9MT7JSD
03dEe1guN4IirAd+YV99p1cJbNskJF0Q7YPcrjXObB6rwp46HZkB+Q4sa6R04Ice
hgS8cqM+z0Z2V6x47EGtLYLl9ah2nSUKh9jUdPpknRS/9Be1zy7X/3FBlF0wVvAX
nO0vy2bT5X+Z+TFC7t4Wru04veaa3aZQdTBb1QIDAQABAoIBAAEK0KkF3txOHJOj
tSoNRGvRPALNNiqvCDjwkM6Lh0om6gHF+Asceqz21wJnh3EAlBAkaYhQCYoF7k/k
B9fxbM7PzJK3jkYMvIcmVuURXqIJeoQkdpFKXCDz84T/qef/DG0oR0ZuBFbNCym4
U1xdH14kMEl9t8Ah7jhF4iCSwzsEG/YUU2czCkHwnFaALsnTk6sXhcUvy8BFelIP
TU3QWxMIv2hmEJcn8GXXtORKtirpvU7WlvGSOFwKbd5yxM0oZh5ZneR2oXL/4lre
fP+jQAuAAftfMzvo4EjR/6DaHpg+YWMJMM0sncu6FwFLo0V1l8gdaiPiWRZXkTVq
aujZg9ECgYEA5Xkw2rBMWyLYVm+DgjZ2mhbRop2lWrjGKh8CYFuHXP1C1TRvUsGA
zpNyF4Gz9OAPsUm7JoG3dChr2+/or2UGrhD2BuSDPz9XIhQkQ+SOuBKTV3EcZVDz
BKajm7M+EkdVQdD8Rv5gKTHlTNtGSK+WxmSbjobCVZtjlmWDUm7DwykCgYEAvcrg
pKh20H3iLSZY+6GlngvyXe7fT6gj5t5hylHOBkwAZnlgSoRxjNWkYBsm6lCpWGvS
zYNmgxo2KyjsH59P25gyTzNOIgGpabvK+3qkiO/rDErHMM3K+/PCvYI9Ggc6idWa
p0LJniG1WD+xJTsGaFKpqXDczGDmIiEFxhBa9M0CgYAOIbyEez74jdjM2ek7Z6c+
LhGS9ipsv9xUU7yNupVHgMFQ5/3DFu+byTDJic9PUU0mGehQosRtft/Fl6y4wHv8
1EaSfhgnGPuAucTR+Y1ggKRaSjj5dFbC/pAs0okMDyCNARXIOXoqj0wTw508dLPQ
W/nIeTwWkY88f9vqg6/CoQKBgG/NKRQWIRekcC4EnvpsOLGne/iVlHrKI4wGiDi0
g9b1wm6bJXwAFRvPZmsu03fIWFm7+owR0bt9H1fBXYcrQ9GqEEjTgj1KGVAtzd7i
WjZIjn/JNRmswNw2tgsIw5GnnHZBnD7Xewlp9fesXV//K0EdINCtqYPDuQ11wn7w
d4QpAoGBAJoESzi2a+kQhIoqF+sdCWRXmtieUBvQMFqoIrIYuWMHmbUBewTByRzJ
2LAet1kkPQEScWX3OifuzScuQ2E+UB6q4JFqie+VjFItJU5OE4/45nf6IYyVBxwj
TcZR1bO+Rx1eFnj0Xyhq4Zy9iuGzJhZxpzD7P+LJG9t3rlAkOIyL
-----END RSA PRIVATE KEY-----`
const decryptedWithCrypto = privateDecrypt (
{ key: privateKeyPem, padding: constants.RSA_PKCS1_PADDING },
Buffer.from(encryptedRandomBits, 'base64')
)
const nodePrivateKey = forge.pki.privateKeyFromPem(privateKeyPem) as forge.pki.rsa.PrivateKey
const padding = Buffer.alloc(1)
padding.writeUInt8(0,0)
const padded = Buffer.concat( [ padding, Buffer.from(encryptedRandomBits, 'base64') ])
//node-forge cannot decrypt without the padding!
const decryptedFromPaddedWithNodeForge: string = nodePrivateKey.decrypt(forge.util.decode64(padded.toString('base64')))
const decryptedFromPaddedWithCrypto = privateDecrypt (
{ key: privateKeyPem, padding: constants.RSA_PKCS1_PADDING }, padded
)
expect(btoa(decryptedFromPaddedWithNodeForge)).toEqual(decryptedWithCrypto.toString('base64'))
expect(btoa(decryptedFromPaddedWithNodeForge)).toEqual(decryptedFromPaddedWithCrypto.toString('base64'))
done()
})

NodeJS RSA prehashed sign

I'm running NodeJS 8.12.0 and have to set a signature over a hash, without re-hashing it, performing a raw signature. Or, in other words, encrypt the hashed value with the private key.
const crypto = require('crypto');
// 4096 bits key.
let pk = "<BASE64 DER>";
let pub = "<BASE64 DER>";
// Transform them to PEM.
pk = `-----BEGIN PRIVATE KEY-----\n${pk.replace('\n', '')}\n-----END PRIVATE KEY-----\n`;
pub = `-----BEGIN PUBLIC KEY-----\n${pub.replace('\n', '')}\n-----END PUBLIC KEY-----\n`;
// Load the data to sign and set the signature.
const fingerprint = Buffer.from('2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824','hex');
const signature = crypto.privateEncrypt({
key: pk,
padding: crypto.constants.RSA_PKCS1_PADDING
},
fingerprint
);
// Unfortunately, the server is not able to verify the signature...
console.log(signature.toString('hex'));
So I took a look at raw encryption of the message hash with my private key and ended up with some sort of EMSA encoding and followed these steps:
Apply the hash function to the message
Encode the algorithm ID for the hash function and the hash into ASN.1 value of DigestInfo (Appendix A.2.4)
Generate an octet string PS consisting of emLen - tLen - 3 octets of 0xff
Concatenate PS, the DER encoded value T, and other padding to form the encoded message EM as EM = 0x00 || 0x01 || PS || 0x00 || T
So, working that out
// 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
H = 2c f2 4d ba 5f b0 a3 0e 26 e8 3b 2a c5 b9 e2 9e 1b 16 1e 5c 1f a7 42 5e 73 04 33 62 93 8b 98 24
emLen = 512
T = 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 2c f2 4d ba 5f b0 a3 0e 26 e8 3b 2a c5 b9 e2 9e 1b 16 1e 5c 1f a7 42 5e 73 04 33 62 93 8b 98 24
PS = 04 06 02 00 33 ff ff ff
// 00010406020033ffffff003031300d0609608648016503040201050004202cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
EM = 00 01 04 06 02 00 33 ff ff ff 00 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 2c f2 4d ba 5f b0 a3 0e 26 e8 3b 2a c5 b9 e2 9e 1b 16 1e 5c 1f a7 42 5e 73 04 33 62 93 8b 98 24
But when I put that into the privateEncrypt, I do not get the correct output either. Can anyone help me here?
You are trying to do the PKCS1-padding (RSASSA-PKCS1-V1_5) manually. But this isn't necessary. It's enough to concatenate the Hash-ID (here for SHA-256) and your data (fingerprint), the rest is done by the implicitly selected padding (crypto.constants.RSA_PKCS1_PADDING), i.e.
// Signing
var fingerprint = Buffer.from('2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824','hex');
var id = Buffer.from([0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20]);
var allData = Buffer.concat([id, fingerprint]);
var signature = crypto.privateEncrypt(privateKey, allData); // crypto.constants.RSA_PKCS1_PADDING by default
// Verifying with createVerify
var verify = crypto.createVerify('RSA-SHA256');
verify.update('<the signed data>');
var verified = verify.verify(publicKey, signature); // provides true
// Verifying with publicDecrypt
var decryptedFingerprint = crypto.publicDecrypt(publicKey, Buffer.from(signature)).slice(-32); // provides fingerprint
Note: If you want to do the padding manually, you have to set the byte sequence 0x00 || 0x01 || PS || 0x00 before the allData-buffer. PS consists of as many 0xff-bytes as necessary to reach the key length (in bytes). In addition, the flag
crypto.constants.RSA_NO_PADDING must be set explicitly in the privateEncrypt-call. However, this isn't necessary, since the result is the same. The details are described in RFC 8017, Section 8.2.1

Buffer conversion decodes correctly the <Topic> in a PUB/SUB, but not the messages. Why?

This is the code for a SUB-side handler of a ZeroMQ PUB / SUB socket:
sock.on('message', function(topic, message) {
console.log("--Topic--");
console.log(topic);
console.log(topic.toString('utf8'));
console.log("--Message--");
console.log(message);
console.log(message.toString('utf8'));
console.log("");
});
Probably missing something here, but I am having a hard time working out why my buffer conversion is returning junk:
--Topic--
<Buffer 70 72 65 73 65 6e 63 65>
presence
--Message--
<Buffer 08 a0 43 10 f9 dd d3 cb 05 18 00 20 18 2a 06 00 0c 29 f2 d5 b4 aa 1f 30 10 00 1a 14 13 00 b3 75 48 ec 55 a7 92 a2 bd ee 9d 4b b6 25 a0 77 a5 d3 22 0c ... >
�C���� *
)�մ�0�uH�U������K�%�w��"
Aruba AP 215*
��۟
The first item (topic) decodes without issue, but the message doesn't seem to.

Nodejs Format Preserving Encryption bad Cipher Output

I am currently trying to implement the NIST FPE on FF3 mode from this document:
http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38G.pdf
In there its given an example on various calculations done during steps 1) to 5).
http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/FF3samples.pdf
At the moment i am getting it all right except 4.iii) (REVB(CIPHREVB(K) REVB(P)).
I have the following code:
var reversedP = new Buffer('cd8101000000000000000000730a33fa', 'hex');
var reversedKey = new Buffer('946afc046f6d037f4faa80d5d85943ef', 'hex');
var cipher = crypto.createCipher('aes-128-ecb', reversedKey);
var cypherText = Buffer.concat([cipher.update(reversedP), cipher.final()]);
var s = reverseArrayToBuffer(cypherText);
in my example cypherText =
0a 30 e9 3e a5 6e b7 bc c7 e1 9a 64 83 9b ec 3a cb e9 f2 60 5f 1c 90 d9 e6 56 42 8e 63 83 11 6f
and s =
6f 11 83 63 8e 42 56 e6 d9 90 1c 5f 60 f2 e9 cb 3a ec 9b 83 64 9a e1 c7 bc b7 6e a5 3e e9 30 0a
And according to the nist example it should be something like this:
DE7F3E4A 210E3DFF 346924FB FF750039
Thanks for the help in advance.

Resources