Skip to content

Instantly share code, notes, and snippets.

@artjomb
Last active January 9, 2024 16:43
Show Gist options
  • Star 19 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save artjomb/7ef1ee574a411ba0dd1933c1ef4690d1 to your computer and use it in GitHub Desktop.
Save artjomb/7ef1ee574a411ba0dd1933c1ef4690d1 to your computer and use it in GitHub Desktop.
Convert a byte array to a word array and back in CryptoJS-compatible fashion
function byteArrayToWordArray(ba) {
var wa = [],
i;
for (i = 0; i < ba.length; i++) {
wa[(i / 4) | 0] |= ba[i] << (24 - 8 * i);
}
return CryptoJS.lib.WordArray.create(wa, ba.length);
}
function wordToByteArray(word, length) {
var ba = [],
i,
xFF = 0xFF;
if (length > 0)
ba.push(word >>> 24);
if (length > 1)
ba.push((word >>> 16) & xFF);
if (length > 2)
ba.push((word >>> 8) & xFF);
if (length > 3)
ba.push(word & xFF);
return ba;
}
function wordArrayToByteArray(wordArray, length) {
if (wordArray.hasOwnProperty("sigBytes") && wordArray.hasOwnProperty("words")) {
length = wordArray.sigBytes;
wordArray = wordArray.words;
}
var result = [],
bytes,
i = 0;
while (length > 0) {
bytes = wordToByteArray(wordArray[i], Math.min(4, length));
length -= bytes.length;
result.push(bytes);
i++;
}
return [].concat.apply([], result);
}
@SwissMaWi
Copy link

SwissMaWi commented Mar 5, 2021

I wrote an unit test for this. This implementation works only for converting Uint8Array to CryptoJS.lib.WordArray and back.
It fails for converting CryptoJS.lib.WordArray to Uint8Array and back, the reverse conversion returns different values. That's really bad.

code:
` // ORIGINAL IMPLEMENTATION

static ba2wa(ba) {
var wa = [],
i;
for (i = 0; i < ba.length; i++) {
wa[(i / 4) | 0] |= ba[i] << (24 - 8 * i);
}

return CryptoJS.lib.WordArray.create(wa, ba.length);

}

private static w2ba(word, length) {
var ba = [],
i,
xFF = 0xFF;
if (length > 0)
ba.push(word >>> 24);
if (length > 1)
ba.push((word >>> 16) & xFF);
if (length > 2)
ba.push((word >>> 8) & xFF);
if (length > 3)
ba.push(word & xFF);

return ba;

}

static wa2ba(wordArray, length) {
if (wordArray.hasOwnProperty("sigBytes") && wordArray.hasOwnProperty("words")) {
length = wordArray.sigBytes;
wordArray = wordArray.words;
}

var result = [],
  bytes,
  i = 0;
while (length > 0) {
  bytes = this.w2ba(wordArray[i], Math.min(4, length));
  length -= bytes.length;
  result.push(bytes);
  i++;
}
return [].concat.apply([], result);

}
`

test:
`
it('should convert a large CryptoJS.lib.WordArray to a Uint8Array and back to the same CryptoJS.lib.WordArray',
() => {
// Arrange
const input = CryptoJS.lib.WordArray.random(10);
console.log(JSON.stringify(input.words));

    // Act
    const result1 = WordArrayExtensions.wa2ba(input, null);
    const result2 = WordArrayExtensions.ba2wa(result1);

    // Assert
    expect(result1).toBeDefined();
    expect(result2).toBeDefined();
    expect(result2.words).toEqual(input.words);
  });

`

@whossein
Copy link

thank a lot <3

@julianmetza
Copy link

julianmetza commented Apr 27, 2023

Combined and rewrote the words-to-bytes-function to be a bit faster. Just in case someone needs it.

function cryptArrToByteArr(wordArray) {
        var bytes = new Uint8Array(wordArray.sigBytes);

        var fullwords = wordArray.sigBytes >>> 2; //equal div 4
        var lastbytes = wordArray.sigBytes % 4;

        var word, offs;
        for (var i = 0; i < fullwords; i++) {
                word = wordArray.words[i];
                offs = i<<2;
                bytes[offs] = word >>> 24;
                bytes[offs+1] = (word >>> 16) & 0xFF;
                bytes[offs+2] = (word >>> 8) & 0xFF;
                bytes[offs+3] = word & 0xFF;
        }
        if (lastbytes > 0) {
                word = wordArray.words[fullwords];
                offs = fullwords<<2;
                bytes[offs] = word >>> 24;
                if (length > 1) bytes[offs+1] = (word >>> 16) & 0xFF;
                if (length > 2) bytes[offs+2] = (word >>> 8) & 0xFF;
        }
        return bytes;
}

@shampain6660
Copy link

The above code has errors too, although I definitely prefer its intended efficiency:

     if (lastbytes > 0) {
             word = wordArray.words[fullwords];
             offs = fullwords<<2;
             bytes[offs] = word >>> 24;
             if (lastbytes > 1) bytes[offs+1] = (word >>> 16) & 0xFF;
             if (lastbytes > 2) bytes[offs+2] = (word >>> 8) & 0xFF;
     }

@shampain6660
Copy link

In case anyone comes here looking for a solution, here is what I ended up using:

/** Returns a crypto-JS compatible Word Array, based on the byte array provided */
  static toWordArray(bytes) {
    var words = [];
    for (var j = 0; j < bytes.length; j++) {
      words[(j>>>2)] |= bytes[j] << (24 - (8 * (j%4)));
    }
    return CryptoJS.lib.WordArray.create(words, bytes.length);
  }

  /** Returns a byte array, based on the crypto-JS compatible word array provided */
  static fromWordArray(wordArray) {
    var bytes = new Uint8Array(wordArray.sigBytes);
    for (var j=0; j<wordArray.sigBytes; j++) {
      bytes[j] = (wordArray.words[(j>>>2)] >>> (24 - (8 * (j%4)))) & 0xFF
    }
    return bytes;
  }    

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment