Search code examples
javascriptcryptographypostmanuuidcryptojs

Postman CryptoJS alternative to crypto.createHash(‘md5’).update(input).digest();


I’m trying to generate a UUID from a string, I basically want to recreate what UUID.nameUUIDFromBytes does in Java. I found this article that works perfect outside of postman, Java's UUID.nameUUIDFromBytes to written in JavaScript? - Stack Overflow

but crypto is not available in Postman. I’ve also tried CryptoJS in postman and have gotten really close, the generated UUID is off by a single character…

function javaHash(test) {
    var md5Bytes = CryptoJS.MD5(test);
    console.log(md5Bytes);

    md5Bytes[6] &= 0x0f;  /* clear version        */
    md5Bytes[6] |= 0x30;  /* set to version 3     */
    md5Bytes[8] &= 0x3f;  /* clear variant        */
    md5Bytes[8] |= 0x80;  /* set to IETF variant  */
    console.log(md5Bytes);

    return md5Bytes.toString(CryptoJS.enc.Hex).replace(/-/g, "").replace(/(\w{8})(\w{4})(\w{4})(\w{4})(\w{12})/, "$1-$2-$3-$4-$5");
}

console.log(javaHash('123456789'));

From looking at the value in the console it doesnt look like the value is being changed by whatever magic (setting the version and variabnt) is supposed to happen in the middle of the method.

I’ve also tried importing crypto from here: https://cdnjs.com/libraries/crypto-js, with this method: Adding External Libraries in Postman | Postman Blog

function javaHash(test) {
    eval(pm.collectionVariables.get("crypto_library"));

    let md5Bytes = this.crypto.createHash('md5').update(test).digest();
    console.log(md5Bytes);
    md5Bytes[6] &= 0x0f;  /* clear version        */
    md5Bytes[6] |= 0x30;  /* set to version 3     */
    md5Bytes[8] &= 0x3f;  /* clear variant        */
    md5Bytes[8] |= 0x80;  /* set to IETF variant  */
    console.log(md5Bytes);

    return md5Bytes.toString(CryptoJS.enc.Hex).replace(/-/g, "").replace(/(\w{8})(\w{4})(\w{4})(\w{4})(\w{12})/, "$1-$2-$3-$4-$5");
}

but I get an error “There was an error in evaluating the Pre-request Script:TypeError: Cannot read properties of undefined (reading ‘lib’)”

Any ideas?


Solution

  • The problem in your CryptoJS code is that md5Bytes is not an array of bytes, so the subsequent byte manipulations fall flat.

    CryptoJS internally uses the WordArray type, i.e. an array consisting of 4-byte words (s. here). This type is also returned by CryptoJS.MD5(). Because of the MD5 output size of 16 bytes the WordArray consists of 4 words (a 4 bytes).
    Since the UUID algorithm modifies individual bytes, it is necessary to access and modify the bytes within the words. This is feasible e.g. by converting WordArray to Uint8Array and vice versa:

    var hashWA = CryptoJS.MD5('12345');
    
    // Conversion WordArray -> Uint8Array
    var binString = hashWA.toString(CryptoJS.enc.Latin1);
    var md5Bytes = Uint8Array.from(binString, x => x.charCodeAt(0))
    
    // Byte manipulations
    md5Bytes[6] &= 0x0f;  
    md5Bytes[6] |= 0x30; 
    md5Bytes[8] &= 0x3f;  
    md5Bytes[8] |= 0x80;  
    
    // Conversion Uint8Array -> WordArray 
    var uuidWA = CryptoJS.lib.WordArray.create(md5Bytes);
    
    var uuidHex = uuidWA.toString();
    var uuidFormatted = uuidHex.replace(/(\w{8})(\w{4})(\w{4})(\w{4})(\w{12})/, "$1-$2-$3-$4-$5");
    console.log(uuidFormatted)
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>


    More efficient is an MD5 implementation that returns the hash as an array of bytes (Array, ArrayBuffer, Uint8Array) so that the bytes can be accessed directly, e.g. js-md5:

    var md5Bytes = md5.array('12345');
    
    md5Bytes[6] &= 0x0f;  
    md5Bytes[6] |= 0x30; 
    md5Bytes[8] &= 0x3f;  
    md5Bytes[8] |= 0x80;
    
    var uuidHex = Array.prototype.map.call(new Uint8Array(md5Bytes), x => ('00' + x.toString(16)).slice(-2)).join('');
    var uuidFormatted = uuidHex.replace(/(\w{8})(\w{4})(\w{4})(\w{4})(\w{12})/, "$1-$2-$3-$4-$5");
    console.log(uuidFormatted)
    <script src="https://cdn.jsdelivr.net/npm/js-md5@0.7.3/src/md5.min.js"></script>

    However, I am not sure if with Postman js-md5 is an option or if there are similar libraries.


    Comparison with Java: UUID.nameUUIDFromBytes("12345".getBytes(StandardCharsets.UTF_8)).toString() returns the same output: 827ccb0e-ea8a-306c-8c34-a16891f84e7b.

    Note that the hex encoded MD5 hash for 12345 is 827ccb0e-ea8a-706c-4c34-a16891f84e7b (with equivalent formatting). The differences due to the byte manipulations are found at indexes 6 and 8 (0x30 instead of 0x70 and 0x8c instead of 0x4c).