summaryrefslogtreecommitdiff
path: root/packages/anastasis-core/src/crypto.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/anastasis-core/src/crypto.ts')
-rw-r--r--packages/anastasis-core/src/crypto.ts107
1 files changed, 81 insertions, 26 deletions
diff --git a/packages/anastasis-core/src/crypto.ts b/packages/anastasis-core/src/crypto.ts
index da8338636..8bc004e95 100644
--- a/packages/anastasis-core/src/crypto.ts
+++ b/packages/anastasis-core/src/crypto.ts
@@ -1,22 +1,38 @@
+/*
+ This file is part of GNU Anastasis
+ (C) 2021-2022 Anastasis SARL
+
+ GNU Anastasis is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ GNU Anastasis; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
import {
- bytesToString,
canonicalJson,
decodeCrock,
encodeCrock,
getRandomBytes,
- kdf,
kdfKw,
secretbox,
crypto_sign_keyPair_fromSeed,
stringToBytes,
secretbox_open,
+ hash,
+ bytesToString,
+ hashArgon2id,
} from "@gnu-taler/taler-util";
-import { gzipSync } from "fflate";
-import { argon2id } from "hash-wasm";
export type Flavor<T, FlavorT extends string> = T & {
_flavor?: `anastasis.${FlavorT}`;
};
+
export type FlavorP<T, FlavorT extends string, S extends number> = T & {
_flavor?: `anastasis.${FlavorT}`;
_size?: S;
@@ -55,15 +71,13 @@ export async function userIdentifierDerive(
): Promise<UserIdentifier> {
const canonIdData = canonicalJson(idData);
const hashInput = stringToBytes(canonIdData);
- const result = await argon2id({
- hashLength: 64,
- iterations: 3,
- memorySize: 1024 /* kibibytes */,
- parallelism: 1,
- password: hashInput,
- salt: decodeCrock(serverSalt),
- outputType: "binary",
- });
+ const result = await hashArgon2id(
+ hashInput, // password
+ decodeCrock(serverSalt), // salt
+ 3, // iterations
+ 1024, // memoryLimit (kibibytes)
+ 64, // hashLength
+ );
return encodeCrock(result);
}
@@ -111,6 +125,46 @@ export async function decryptRecoveryDocument(
return anastasisDecrypt(asOpaque(userId), recoveryDocData, "erd");
}
+export interface PolicyMetadata {
+ secret_name: string;
+ policy_hash: string;
+}
+
+export async function encryptPolicyMetadata(
+ userId: UserIdentifier,
+ metadata: PolicyMetadata,
+): Promise<OpaqueData> {
+ const metadataBytes = typedArrayConcat([
+ decodeCrock(metadata.policy_hash),
+ stringToBytes(metadata.secret_name),
+ ]);
+ const nonce = encodeCrock(getRandomBytes(nonceSize));
+ return anastasisEncrypt(
+ nonce,
+ asOpaque(userId),
+ encodeCrock(metadataBytes),
+ "rmd",
+ );
+}
+
+export async function decryptPolicyMetadata(
+ userId: UserIdentifier,
+ metadataEnc: OpaqueData,
+): Promise<PolicyMetadata> {
+ // @ts-ignore
+ console.log("metadataEnc", metadataEnc);
+ const plain = await anastasisDecrypt(asOpaque(userId), metadataEnc, "rmd");
+ // @ts-ignore
+ console.log("plain:", plain);
+ const metadataBytes = decodeCrock(plain);
+ const policyHash = encodeCrock(metadataBytes.slice(0, 64));
+ const secretName = bytesToString(metadataBytes.slice(64));
+ return {
+ policy_hash: policyHash,
+ secret_name: secretName,
+ };
+}
+
export function typedArrayConcat(chunks: Uint8Array[]): Uint8Array {
let payloadLen = 0;
for (const c of chunks) {
@@ -175,11 +229,11 @@ async function anastasisDecrypt(
const nonceBuf = ctBuf.slice(0, nonceSize);
const enc = ctBuf.slice(nonceSize);
const key = await deriveKey(keySeed, encodeCrock(nonceBuf), salt);
- const cipherText = secretbox_open(enc, nonceBuf, key);
- if (!cipherText) {
+ const clearText = secretbox_open(enc, nonceBuf, key);
+ if (!clearText) {
throw Error("could not decrypt");
}
- return encodeCrock(cipherText);
+ return encodeCrock(clearText);
}
export const asOpaque = (x: string): OpaqueData => x;
@@ -248,7 +302,6 @@ export async function coreSecretRecover(args: {
args.encryptedMasterKey,
"emk",
);
- console.log("recovered master key", masterKey);
return await anastasisDecrypt(masterKey, args.encryptedCoreSecret, "cse");
}
@@ -283,20 +336,22 @@ export async function coreSecretEncrypt(
};
}
+export async function pinAnswerHash(pin: number): Promise<SecureAnswerHash> {
+ return encodeCrock(hash(stringToBytes(pin.toString())));
+}
+
export async function secureAnswerHash(
answer: string,
truthUuid: TruthUuid,
questionSalt: TruthSalt,
): Promise<SecureAnswerHash> {
- const powResult = await argon2id({
- hashLength: 64,
- iterations: 3,
- memorySize: 1024 /* kibibytes */,
- parallelism: 1,
- password: stringToBytes(answer),
- salt: decodeCrock(questionSalt),
- outputType: "binary",
- });
+ const powResult = await hashArgon2id(
+ stringToBytes(answer), // password
+ decodeCrock(questionSalt), // salt
+ 3, // iterations
+ 1024, // memorySize (kibibytes)
+ 64, // hashLength
+ );
const kdfResult = kdfKw({
outputLength: 64,
salt: decodeCrock(truthUuid),