1

I need to modify this (working) code and replace CryptoJS functions with native JS.

The variable b64encoded has the correct value, but my attempt with native code (encodedIncorrect) is not. Can you help feed btoa the correct input?

Working, but 3rd party libs cannot be used on the platform I'm developing for:

const encrypted = CryptoJS.HmacSHA1("message", "secret");
// encrypted = "0caf649feee4953d87bf903ac1176c45e028df16" << Hexadecimal encoded string
const b64encoded = CryptoJS.enc.Base64.stringify(encrypted);
// b64encoded = "DK9kn+7klT2Hv5A6wRdsReAo3xY="

Not working, specifically the encoding of a binary string by btoa:

const encrypted_vendor = VENDOR.hmacSHA1("message", "secret") // Vendor-provided HMAC-SHA1 func
// encrypted_vendor = "0caf649feee4953d87bf903ac1176c45e028df16"

// Split hex string into two-character byte representations for binary conversion
function splitHex(hexString) {
    let chunks = [];
    for (var i = 0, charsLength = hexString.length; i < charsLength; i += 2) {
        chunks.push(hexString.substring(i, i + 2));
    }
}
// Convert hex characters to binary value
function hexCharToBinary(value) {
    return (parseInt(value, 16).toString(2)).padStart(8, '0');
}

binaryChunks = splitHex(encrypted)
var binaryStr = ""
binaryChunks.forEach(i => binaryStr += hexCharToBinary(i))
// binaryStr = "0000110010101111011001001001111111101110111001001001010100111101100001111011111110010000001110101100000100010111011011000100010111100000001010001101111100010110"
encodedIncorrect = btoa(binaryStr)
// encodedIncorrect = "MDAwMDExMDAxMDEwMTExMTAxMTAwMTAwMTAwMTExMTExMTEwMTExMDExMTAwMTAwMTAwMTAxMDEwMDExMTEwMTEwMDAwMTExMTAxMTExMTExMDAxMDAwMDAwMTExMDEwMTEwMDAwMDEwMDAxMDExMTAxMTAxMTAwMDEwMDAxMDExMTEwMDAwMDAwMTAxMDAwMTEwMTExMTEwMDAxMDExMA=="

I've also verified that the binary output is correct using this site (https://cryptii.com/pipes/binary-to-base64), which encodes it to the correct base64 when set to binary input.

Phil Sheard
  • 2,102
  • 1
  • 17
  • 38
  • You are encoding the string representation of the binary digits, not the actual data. You can also easily see this from the always repeating "MDA", "MDE", and similar, in your incorrect result. – ASDFGerte Dec 19 '19 at 13:01
  • Yes, I know! That's the question I have - how do I get the raw binary data into a variable to pass to `btoa`? – Phil Sheard Dec 19 '19 at 13:03
  • You should look at the [docs](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/btoa), which point you to what a [binary string](https://developer.mozilla.org/en-US/docs/Web/API/DOMString/Binary) is in this context. In other words, each symbol in the string should be a code point from 0-255, one byte of data. – ASDFGerte Dec 19 '19 at 13:04
  • I appreciate the help. I have reviewed the docs before posting and there's no examples or guidance on how to convert the strings I have into raw binary data. If you can help with code then I would be very appreciative, as I've tried all sorts to no success. – Phil Sheard Dec 19 '19 at 13:08
  • 1
    `btoa("0caf649feee4953d87bf903ac1176c45e028df16".match(/../g).map(e => String.fromCodePoint(parseInt(e, 16))).join(""));` – ASDFGerte Dec 19 '19 at 13:09
  • 1
    tbh, using `fromCharCode` is probably a bit better, but as the argument should always be an integer in [0, 255], that doesn't really matter much. – ASDFGerte Dec 19 '19 at 13:17
  • Thanks for your advice - I've done some searches and got a working approach using `fromCharCode`. [...] binaryChunks = splitHex(encrypted_vendor) var binaryStr = "" binaryChunks.forEach(i => binaryStr += hexCharToBinary(i)) var binStringTest = "" binaryChunks.forEach(item => binStringTest += String.fromCharCode(parseInt(item, 16))) btoa(binStringTest) // DK9kn+7klT2Hv5A6wRdsReAo3xY= If you want to paste as the answer I'm happy to accept as you got me 99% there. If not, then thanks anyway and I'll self post – Phil Sheard Dec 19 '19 at 14:34

1 Answers1

2

You are encoding the string representation of the binary digits, not the actual data.

You need a binary string, which you can construct e.g. using String.fromCharCode:

const hexString = "0caf649feee4953d87bf903ac1176c45e028df16"

// split into two character groups (one byte each), see e.g.
// https://stackoverflow.com/questions/6259515/how-can-i-split-a-string-into-segments-of-n-characters
// using /.{1,2}/g may be preferrable for potential error handling
// but if there is always an even amount of characters, it doesn't matter.
let hexCharacterPairs = hexString.match(/../g);

// parse two hex characters as number
let bytes = hexCharacterPairs.map(e => parseInt(e, 16));

// generate symbols from the bytes
let binaryStringSymbols = bytes.map(e => String.fromCharCode(e));

// get a string from all the symbols
let binaryString = binaryStringSymbols.join("");

// use the binary string to get a base64 encode
let base64 = btoa(binaryString);

console.log(base64);

// or in one line
console.log(btoa("0caf649feee4953d87bf903ac1176c45e028df16".match(/../g).map(e => String.fromCharCode(parseInt(e, 16))).join("")));

Note, that the above snippet has no error handling, e.g. for odd numbers of characters in the input, invalid characters, etc.

ASDFGerte
  • 4,695
  • 6
  • 16
  • 33