(wip): call migration stuff
This commit is contained in:
parent
2cd326d0b1
commit
27f08e92e5
4 changed files with 264 additions and 53 deletions
112
packages/crypto/src/callSecret.ts
Normal file
112
packages/crypto/src/callSecret.ts
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
import { crypto } from "mtp";
|
||||
|
||||
const textEncoder = new TextEncoder();
|
||||
const textDecoder = new TextDecoder();
|
||||
|
||||
export const CALL_SECRET_WRAPPING_SCHEME =
|
||||
"mtp-call-secret-kem-chacha20poly1305-hkdf-sha256-v1";
|
||||
|
||||
const CALL_SECRET_SALT = textEncoder.encode("tensamin-call-secret-v1");
|
||||
|
||||
export function deriveCallSecretId(callId: string): string {
|
||||
return `call:${callId}:main`;
|
||||
}
|
||||
|
||||
export function ownKemPublicKeyFromKeyring(keyring: string): Uint8Array {
|
||||
return crypto.keyringToKeys(keyring).kemPublicKey;
|
||||
}
|
||||
|
||||
export function kemPublicKeyFromPublicKeyBundle(publicKey: string): Uint8Array {
|
||||
return crypto.publicKeyBundleToKeys(publicKey).kemPublicKey;
|
||||
}
|
||||
|
||||
export async function wrapCallSecret(args: {
|
||||
callSecret: string;
|
||||
recipientKemPublicKey: Uint8Array;
|
||||
callId: string;
|
||||
secretId: string;
|
||||
version: number;
|
||||
}): Promise<{
|
||||
encryptedSecret: Uint8Array;
|
||||
kemCiphertext: Uint8Array;
|
||||
wrappingScheme: string;
|
||||
}> {
|
||||
const enc = crypto.encapsulate(args.recipientKemPublicKey);
|
||||
try {
|
||||
const wrappingKey = deriveWrappingKey({
|
||||
sharedSecret: enc.shared_secret,
|
||||
callId: args.callId,
|
||||
secretId: args.secretId,
|
||||
version: args.version,
|
||||
});
|
||||
|
||||
try {
|
||||
return {
|
||||
encryptedSecret: await crypto.encrypt(
|
||||
wrappingKey,
|
||||
textEncoder.encode(args.callSecret),
|
||||
),
|
||||
kemCiphertext: enc.ciphertext,
|
||||
wrappingScheme: CALL_SECRET_WRAPPING_SCHEME,
|
||||
};
|
||||
} finally {
|
||||
wrappingKey.fill(0);
|
||||
}
|
||||
} finally {
|
||||
enc.shared_secret.fill(0);
|
||||
}
|
||||
}
|
||||
|
||||
export async function unwrapCallSecret(args: {
|
||||
encryptedSecret: Uint8Array;
|
||||
kemCiphertext: Uint8Array;
|
||||
keyring: string;
|
||||
callId: string;
|
||||
secretId: string;
|
||||
version: number;
|
||||
wrappingScheme: string;
|
||||
}): Promise<string> {
|
||||
if (args.wrappingScheme !== CALL_SECRET_WRAPPING_SCHEME) {
|
||||
throw new Error(
|
||||
`Unsupported call secret wrapping scheme: ${args.wrappingScheme}`,
|
||||
);
|
||||
}
|
||||
|
||||
const ownKeys = crypto.keyringToKeys(args.keyring);
|
||||
const sharedSecret = crypto.decapsulate(
|
||||
ownKeys.kemSecretKey,
|
||||
args.kemCiphertext,
|
||||
);
|
||||
|
||||
try {
|
||||
const wrappingKey = deriveWrappingKey({
|
||||
sharedSecret,
|
||||
callId: args.callId,
|
||||
secretId: args.secretId,
|
||||
version: args.version,
|
||||
});
|
||||
|
||||
try {
|
||||
return textDecoder.decode(
|
||||
await crypto.decrypt(wrappingKey, args.encryptedSecret),
|
||||
);
|
||||
} finally {
|
||||
wrappingKey.fill(0);
|
||||
}
|
||||
} finally {
|
||||
sharedSecret.fill(0);
|
||||
}
|
||||
}
|
||||
|
||||
function deriveWrappingKey(args: {
|
||||
sharedSecret: Uint8Array;
|
||||
callId: string;
|
||||
secretId: string;
|
||||
version: number;
|
||||
}): Uint8Array {
|
||||
return crypto.deriveEncryptionKey(
|
||||
args.sharedSecret,
|
||||
CALL_SECRET_SALT,
|
||||
textEncoder.encode(`${args.callId}:${args.secretId}:${args.version}`),
|
||||
);
|
||||
}
|
||||
Loading…
Reference in a new issue