Search code examples
javascriptwebcrypto-api

crypto.subtle.deriveKey always returns undefined


The background to this question is as follows - I'm generating some encrypted data on the server in C#, using a passphrase to encrypt the data. I'm now trying to decrypt on the client using the WebCrypto API, but the call to deriveKey always returns undefined (no errors). I've tried to create a MCVE as follows:

window.crypto.subtle.importKey(
    'raw',
    new TextEncoder().encode('passphrase'),
    { 
        'name': 'PBKDF2' 
    },
    false,
    ['deriveBits', 'deriveKey']
)
.then((baseKey) => {
    console.dir(baseKey) // This looks ok!
    window.crypto.subtle.deriveKey({
            'name': 'PBKDF2',
            salt: new TextEncoder().encode('my_salt'),
            iterations: 100000,
            hash: 'SHA-256'
        },
        baseKey,
        { 
            'name': 'AES-GCM', 
            iv: new TextEncoder().encode('iv_from_server'), 
            length: 256 
        },
        true,
        ['decrypt'])
})
.then((key2) => {
    console.log('generated new key')
    console.dir(key2) // This is always undefined
})
.catch((error) => console.dir(error))

I've tried fiddling with some of the parameters to no avail. It does need to use PBKDF2 and AES-GCM to match the server. I don't know if I'm trying to do something that's not supported by those algorithms, or if I've got something else wrong....


Solution

  • You're getting undefined because you're not returning anything from the previous promise. If you put a return before your deriveKey invocation, it'll fix your issue. Here's your code with the fix:

    window.crypto.subtle.importKey(
        'raw',
        new TextEncoder().encode('passphrase'),
        { 
            'name': 'PBKDF2' 
        },
        false,
        ['deriveBits', 'deriveKey']
    )
    .then((baseKey) => {
        console.dir(baseKey) // This looks ok!
    
        // ADD A RETURN TO RESOLVE WITH THE KEY IN THE NEXT PROMISE:
        return window.crypto.subtle.deriveKey({
                'name': 'PBKDF2',
                salt: new TextEncoder().encode('my_salt'),
                iterations: 100000,
                hash: 'SHA-256'
            },
            baseKey,
            { 
                'name': 'AES-GCM', 
                iv: new TextEncoder().encode('iv_from_server'), 
                length: 256 
            },
            true,
            ['decrypt'])
    })
    .then((key2) => {
        console.log('generated new key')
        console.dir(key2) // NOW IT WORKS
    })
    .catch((error) => console.dir(error))