Reputation: 45
I have an exisiting solution working good in ts and nextjs using web/crypto library and decrypts all data. Due to some reason I have to perform decryption in golang. For that purpose I wrote following go-code it works fine for some data whereas it doesn't work really well for remaining.
/* eslint-disable @typescript-eslint/ban-ts-comment */
/* eslint-disable @typescript-eslint/no-var-requires */
import { Controller, Get, Res, Param, Req, HttpStatus } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
import { response, type Response } from 'express';
import { Readable } from 'stream';
import { firstValueFrom, map } from 'rxjs';
import { FileService } from './file.service';
const fs = require('fs');
const crypto = require('crypto').webcrypto;
//import crypto, { subtle } from "crypto";
async function generateSecretKeyForEncryption(
secreteKeyString: string,
userSalt: string,
) {
const key = await crypto.subtle.importKey(
'raw',
new TextEncoder().encode(secreteKeyString),
{ name: 'PBKDF2', hash: 'SHA-256' },
false,
['deriveKey'],
);
const derivedKey = await crypto.subtle.deriveKey(
{
name: 'PBKDF2',
salt: new TextEncoder().encode(userSalt),
iterations: 1000,
hash: 'SHA-256',
},
key,
{ name: 'AES-GCM', length: 256 },
true,
['encrypt', 'decrypt'],
);
return derivedKey;
}
const fromHexString = (hexString) =>
Uint8Array.from(hexString.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)));
const decryptedSecretKeyAndFile = async (
data,
secretKey,
accessKey,
iv,
fileData,
userSalt,
) => {
console.log("🚀 ~ file: file.controller.ts:52 ~ iv:", iv)
const newDataArray = fromHexString(data);
console.log("🚀 ~ file: file.controller.ts:55 ~ newDataArray:", newDataArray)
const key = await generateSecretKeyForEncryption(secretKey, userSalt);
console.log("🚀 ~ file: file.controller.ts:57 ~ key:", key)
const encryptionKey = await crypto.subtle.decrypt(
{
name: 'AES-GCM',
iv: new TextEncoder().encode(accessKey),
tagLength: 128,
},
key,
newDataArray,
);
console.log("🚀 ~ file: file.controller.ts:65 ~ encryptionKey:", encryptionKey)
const ecnryptionKeyForFile = await crypto.subtle.importKey(
'raw',
new Uint8Array(encryptionKey),
{ name: 'AES-GCM' },
true,
['encrypt', 'decrypt'],
);
//console.log("🚀 ~ file: file.controller.ts:74 ~ ecnryptionKeyForFile:", ecnryptionKeyForFile)
const encrtedData = await crypto.subtle.decrypt(
{
name: 'AES-GCM',
iv: new TextEncoder().encode(iv)
},
ecnryptionKeyForFile,
fileData,
);
console.log("🚀 ~ file: file.controller.ts:84 ~ fileData:", fileData.length)
console.log("🚀 ~ file: file.controller.ts:81 ~ encrtedData:", encrtedData)
return encrtedData;
};
This code decrypts the data very well I tried to write a golang script an exact copy of this.
// Function to decrypt filedata using decrypted key iv and filedata
func decryptor(key, trimiv, data []byte) ([]byte, error) {
// Create a new AES block cipher with the key
b, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
// Create a GCM cipher mode
aesgcm, err := NewGCMWithNonceSize(b, 32)
if err != nil {
return nil, err
}
// Decrypt the data
decryptedData, err := aesgcm.Open(nil, trimiv, data, nil)
if err != nil {
return nil, err
}
return decryptedData, nil
}
// Function to derive pbkf2 keyfrom secret key and salt
func deriveKey(secretKey string, userSalt string) ([]byte) {
// Derive the key using PBKDF2 with provided salt and other parameters
derivedKey := pbkdf2.Key([]byte(secretKey), []byte(userSalt), 1000, 32, sha256.New)
// Return the derived key
return derivedKey
}
// Function to decrypt key and then decrypt data using AES-GCM
func DecryptedSecretKeyAndFile(data, secretKey, accessKey, iv, userSalt string, fileData []byte) ([]byte, error) {
//Nonce and data to decrypt Master Key
//nonce/iv to decrypt key
hexaccessKey, _ := hex.DecodeString(accessKey)
trimaccessKey := hexaccessKey[:32]
//data to decrypt key
hexdata, _ := hex.DecodeString(data)
//Nonce and data to decrypt original data
//nonce/iv to decrypt data
hexiv, _ :=hex.DecodeString(iv)
trimiv := hexiv[:32]
//fileData contains the original data to be decrypted
//gcm method
key:= deriveKey(secretKey, userSalt)
decryptedKey, err := decryptor(key, trimaccessKey, hexdata)
if err != nil {
return nil, err
}
fmt.Println("DecryptedKey accessed",decryptedKey)
//Decrypt the Data
decryptedData, err := decryptor(decryptedKey, trimiv, fileData)
if err != nil {
fmt.Println("Error:", err)
return nil, err
}
fmt.Println("Decrypted Data accessed",len(fileData), len(decryptedData), decryptedData[:32])
//return Decrypted Data
return decryptedData, nil
}
This code works well for some data but for some data it throws authentication failure. And when I forked the cipher library of go I found out.
Code is breaking on this check
if subtle.ConstantTimeCompare(expectedTag[:g.tagSize], tag) != 1 {
// The AESNI code decrypts and authenticates concurrently, and
// so overwrites dst in the event of a tag mismatch. That
// behavior is mimicked here in order to be consistent across
// platforms.
for i := range out {
out[i] = 0
}
return nil, errOpen
}
https://go.dev/src/crypto/cipher/gcm.go?s=3702:3764
Here is link to my github repo
https://github.com/Rusted2361/media-file-server-go/tree/server-test
Upvotes: 0
Views: 305