3

I've been looking at SublteCrypto to encrypt/decrypt text messages, and wanted to extract the key that is used to String, but using the same interface "SubtleCrypto" produces weird Strings for the key.

Now this is the code that I was playing with:

    Internal.crypto = {
    getRandomBytes: function(size) {
        var array = new Uint8Array(size);
        crypto.getRandomValues(array);
        return array.buffer;
    },
    encrypt: function(key, data, iv) {
        return crypto.subtle.importKey('raw', key, {name: 'AES-CBC'}, false, ['encrypt']).then(function(key) {
            return crypto.subtle.encrypt({name: 'AES-CBC', iv: new Uint8Array(iv)}, key, data);
        });
    },
    decrypt: function(key, data, iv) {
        return crypto.subtle.importKey('raw', key, {name: 'AES-CBC'}, false, ['decrypt']).then(function(key) {
            return crypto.subtle.decrypt({name: 'AES-CBC', iv: new Uint8Array(iv)}, key, data);
        });
    },
    sign: function(key, data) {
        return crypto.subtle.importKey('raw', key, {name: 'HMAC', hash: {name: 'SHA-256'}}, false, ['sign']).then(function(key) {
            return crypto.subtle.sign( {name: 'HMAC', hash: 'SHA-256'}, key, data);
        });
    },

    hash: function(data) {
        return crypto.subtle.digest({name: 'SHA-512'}, data);
    },

    HKDF: function(input, salt, info) {
        // Specific implementation of RFC 5869 that only returns the first 3 32-byte chunks
        // TODO: We dont always need the third chunk, we might skip it
        return Internal.crypto.sign(salt, input).then(function(PRK) {
            var infoBuffer = new ArrayBuffer(info.byteLength + 1 + 32);
            var infoArray = new Uint8Array(infoBuffer);
            infoArray.set(new Uint8Array(info), 32);
            infoArray[infoArray.length - 1] = 1;
            return Internal.crypto.sign(PRK, infoBuffer.slice(32)).then(function(T1) {
                infoArray.set(new Uint8Array(T1));
                infoArray[infoArray.length - 1] = 2;
                return Internal.crypto.sign(PRK, infoBuffer).then(function(T2) {
                    infoArray.set(new Uint8Array(T2));
                    infoArray[infoArray.length - 1] = 3;
                    return Internal.crypto.sign(PRK, infoBuffer).then(function(T3) {
                        return [ T1, T2, T3 ];
                    });
                });
            });
        });
    },

    // Curve 25519 crypto
    createKeyPair: function(privKey) {
        if (privKey === undefined) {
            privKey = Internal.crypto.getRandomBytes(32);
        }
        return Internal.Curve.async.createKeyPair(privKey);
    },
    ECDHE: function(pubKey, privKey) {
        return Internal.Curve.async.ECDHE(pubKey, privKey);
    },
    Ed25519Sign: function(privKey, message) {
        return Internal.Curve.async.Ed25519Sign(privKey, message);
    },
    Ed25519Verify: function(pubKey, msg, sig) {
        return Internal.Curve.async.Ed25519Verify(pubKey, msg, sig);
    }
};

What I've changed to get the key in String was:

    encrypt: function(key, data, iv) {
        var sKey = String.fromCharCode.apply(null, new Uint8Array(key));
        console.log('key ' + sKey);
        return crypto.subtle.importKey('raw', key, {name: 'AES-CBC'}, true, ['encrypt']).then(function(key) {
            return crypto.subtle.encrypt({name: 'AES-CBC', iv: new Uint8Array(iv)}, key, data);
        });
    },
    decrypt: function(key, data, iv) {
        var sKey = String.fromCharCode.apply(null, new Uint8Array(key));
        console.log('key ' + sKey);
        return crypto.subtle.importKey('raw', key, {name: 'AES-CBC'}, true, ['decrypt']).then(function(key) {
            return crypto.subtle.decrypt({name: 'AES-CBC', iv: new Uint8Array(iv)}, key, data);
        });
    }

I know that key in encrypt: function(key, data, iv) and decrypt: function(key, data, iv) is in ArrayBuffer, but I've tried everything to convert from ArrayBuffer to String, and it always produces incorrect encoded strings like Sask���ç é

Ahmed
  • 49
  • 2
  • 9
  • Can you show us how the call to `encrypt` or `decrypt` is made? How is created the `key` passed to it? Also, with your example of "incorrect" string, can you show the array values associated to it? (Note that if randomly generated bytes, they may fall in control characters range, that cannot be "displayed" in a regular log) – Kaddath May 15 '18 at 08:09

1 Answers1

3

A cryptographic keys isn't text, or text encoded as bytes. It is a random byte sequence, so trying to decode it as a string is likely to irrevocably lose data.

Instead, use base-64 encoding (btoa()) and decoding (atob()) to convert binary data to strings and back again. The same applies to cipher text.

erickson
  • 265,237
  • 58
  • 395
  • 493
  • 6
    but the `.sign` function returns an ArrayBuffer, how can we run it with `btoa`. `btoa` is expecting a string not an ArryBuffer – jojo Aug 29 '18 at 00:32