Files
core_ng/@sammo/secure_token/src/EncryptedSimple.ts
T
Hide_D e59f9a9659 monorepo 버전 준비
## @strpc
기존 RPC를 package화

### @strpc/express
express의 middleware + router 결함

## @sammo
게임 전체
- server, client
- gateway_server, gateway_client
2023-09-23 16:29:18 +00:00

79 lines
3.0 KiB
TypeScript

import { sha512 } from "@sammo/crypto/SHA2";
import { BSON } from "bson";
import type { PlainBson } from '@sammo/util';
import { calcBase64Len } from "@sammo/util";
import { AES_GCM_Decrypt, AES_GCM_Encrypt } from "@sammo/crypto/AES";
import { z } from "zod";
const nonceLen = 16;
const tagLen = 16;
export type EncryptedSimpleItem<T> = {
nonce: string;
ct: string;
}
export const zodEncryptedSimpleItem = z.object({
nonce: z.string().length(calcBase64Len(nonceLen)),
ct: z.string().min(calcBase64Len(tagLen)),
});
export type DecryptedSimpleItem<T extends EncryptedSimpleItem<unknown>> = T extends EncryptedSimpleItem<infer U> ? U : never;
const staticPresharedTokenSecret: string | undefined = process.env.PRESHARED_SECURE_TOKEN_SECRET;
export async function encryptSimpleItem<T extends PlainBson>(data: T, presharedTokenSecret?: string): Promise<EncryptedSimpleItem<T>> {
if (!presharedTokenSecret) {
if (!staticPresharedTokenSecret) {
throw new Error("PRESHARED_SECURE_TOKEN_SECRET is not set");
}
presharedTokenSecret = staticPresharedTokenSecret;
}
const secretLength = Buffer.byteLength(presharedTokenSecret, 'utf8');
const secretBuffer = Buffer.alloc(secretLength + nonceLen);
secretBuffer.write(presharedTokenSecret, 0, secretLength, 'utf8');
//secretBuffer에서 Buffer를 바로 준비해도 되지만, 혹시모를 안전상의 이유로 별도로 할당하고 복사
const nonce = Buffer.from(crypto.getRandomValues(new Uint8Array(nonceLen)));
secretBuffer.set(nonce, secretLength);
const keyBuffer = Buffer.from(await sha512(secretBuffer));
const key = new Uint8Array(keyBuffer.buffer, 0, 32);
const iv = new Uint8Array(keyBuffer.buffer, 32, 12);
const payload = BSON.serialize(data);
const ciphertext = Buffer.from(await AES_GCM_Encrypt(key, iv, payload));
return {
nonce: nonce.toString('base64'),
ct: ciphertext.toString('base64'),
};
}
export async function decryptSimpleItem<T extends PlainBson>(data: EncryptedSimpleItem<T>, presharedTokenSecret?: string): Promise<T> {
if (!presharedTokenSecret) {
if (!staticPresharedTokenSecret) {
throw new Error("PRESHARED_SECURE_TOKEN_SECRET is not set");
}
presharedTokenSecret = staticPresharedTokenSecret;
}
const secretLength = Buffer.byteLength(presharedTokenSecret, 'utf8');
const secretBuffer = Buffer.alloc(secretLength + 16);
secretBuffer.write(presharedTokenSecret, 0, secretLength, 'utf8');
const nonce = Buffer.from(data.nonce, 'base64');
secretBuffer.set(nonce, secretLength);
const keyBuffer = Buffer.from(await sha512(secretBuffer));
const key = new Uint8Array(keyBuffer.buffer, 0, 32);
const iv = new Uint8Array(keyBuffer.buffer, 32, 12);
const ct = Buffer.from(data.ct, 'base64');
const payload = BSON.deserialize(Buffer.from(await AES_GCM_Decrypt(key, iv, ct))) as T satisfies DecryptedSimpleItem<EncryptedSimpleItem<T>>;
return payload;
}