1

I'm trying to decrypt a string that was encrypted with SubtleCrypto AES-GCM in the browser (https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/encrypt). Using the following code in Javascript:


async function aesGcmEncrypt(plaintext, password) {
    const pwUtf8 = new TextEncoder().encode(password);                                 // encode password as UTF-8
    const pwHash = await crypto.subtle.digest('SHA-256', pwUtf8);                      // hash the password

    const iv = crypto.getRandomValues(new Uint8Array(12));                             // get 96-bit random iv
    const ivStr = Array.from(iv).map(b => String.fromCharCode(b)).join('');            // iv as utf-8 string

    const alg = { name: 'AES-GCM', iv: iv };                                           // specify algorithm to use

    const key = await crypto.subtle.importKey('raw', pwHash, alg, false, ['encrypt']); // generate key from pw

    const ptUint8 = new TextEncoder().encode(plaintext);                               // encode plaintext as UTF-8
    const ctBuffer = await crypto.subtle.encrypt(alg, key, ptUint8);                   // encrypt plaintext using key

    const ctArray = Array.from(new Uint8Array(ctBuffer));                              // ciphertext as byte array
    const ctStr = ctArray.map(byte => String.fromCharCode(byte)).join('');             // ciphertext as string

    return btoa(ivStr) + "." + btoa(ctStr);                                                             // iv+ciphertext base64-encoded
};


And im trying to decrypt the string in Go using:


func Decrypt(encryptedString string, keyString string) string {

    split := strings.Split(encryptedString, ".")

    key := []byte(keyString)
    enc, err := base64.StdEncoding.DecodeString(split[1])
    if err != nil {
        panic(err.Error())
    }
    nonce, err := base64.StdEncoding.DecodeString(split[0])
    if err != nil {
        panic(err.Error())
    }

    //Create a new Cipher Block from the key
    block, err := aes.NewCipher(key)
    if err != nil {
        panic(err.Error())
    }

    //Create a new GCM
    aesGCM, err := cipher.NewGCM(block)
    if err != nil {
        panic(err.Error())
    }

    //Decrypt the data
    plaintext, err := aesGCM.Open(nil, nonce, enc, nil)
    if err != nil {
        panic(err.Error())
    }

    return string(plaintext)

}

However, every time I run into: "cipher: message authentication failed".

I guess there's something different with the implementations but I can't seem figure out what.

I tried using HEX instead of base64 (encoding and decoding) and tried using "cipher.NewGCMWithNonceSize()" instead of "cipher.NewGCM(block)".

elw
  • 13
  • 3
  • In the Go code the key derivation via SHA-256 is missing (by the way a key derivation via a fast hash like SHA-256 is a vulnerability, better apply a dedicated key derivation function like PBKDF2). – Topaco Nov 11 '22 at 15:21
  • Following Topaco's hint, you could use something like this: instead of `key := []byte(keyString)` do `sha := sha256.Sum256([]byte(keyString))` and then `key := sha[:]`. An example ciphertext generated with the JS `Jo+aYEtkt+AP11su.PMNaW37G2uT6dwWU5prAxu4Wy9C71QmKfJYrgQ==` with password `totally secr3t` would then correctly decrypt to `Hello World!`. – Stephan Schlecht Nov 11 '22 at 19:52

0 Answers0