@linkedmink/passport-mutual-key-challenge
Version:
Implements a Passport strategy to authenticate the public key of a user by issuing a dynamic generated challenge
95 lines (79 loc) • 2.9 kB
text/typescript
import crypto, { BinaryLike, KeyLike, KeyObject } from "crypto";
import { CryptographyOptions } from "./MutualKeyChallengeOptions";
import { isFunction, isKeyObject, isPromise } from "./Helpers/TypeCheck";
import { SignedMessage } from "./Types/Messages";
import { GetServerKeyFunc } from "./Types/Functions";
export class MessageVerifier {
private readonly key: KeyObject | GetServerKeyFunc;
constructor(key: GetServerKeyFunc | KeyLike, private readonly options: CryptographyOptions) {
if (isFunction(key)) {
this.key = key;
} else {
this.key = isKeyObject(key) ? key : crypto.createPrivateKey(key);
}
}
async decryptAndVerify(pubKey: KeyLike, data: SignedMessage): Promise<Buffer | null> {
const key = await this.serverKey();
const decrypted = crypto.privateDecrypt(this.encryptOptions(key), data.message);
const isVerified = crypto.verify(
this.options.hashAlgorithm,
decrypted,
this.verifyOptions(pubKey),
data.signature
);
return isVerified ? decrypted : null;
}
async encryptAndSign(pubKey: KeyLike, data: Buffer): Promise<SignedMessage> {
const key = await this.serverKey();
const message = crypto.publicEncrypt(this.encryptOptions(pubKey), data);
const signature = crypto.sign(this.options.hashAlgorithm, data, this.signOptions(key));
return { message, signature };
}
async verify(pubKey: KeyLike, data: SignedMessage): Promise<boolean> {
const key = await this.serverKey();
return crypto.verify(
this.options.hashAlgorithm,
data.message,
this.verifyOptions(pubKey),
data.signature
);
}
async sign(data: Buffer): Promise<SignedMessage> {
const key = await this.serverKey();
const signature = crypto.sign(this.options.hashAlgorithm, data, this.signOptions(key));
return { message: data, signature };
}
hash(data: BinaryLike): string {
return crypto.createHash(this.options.hashAlgorithm).update(data).digest("base64");
}
getNonce(): Buffer {
return crypto.randomBytes(this.options.nonceSize);
}
private signOptions(key: KeyLike) {
return {
key: isKeyObject(key) ? key : crypto.createPrivateKey(key),
padding: this.options.signaturePadding,
};
}
private verifyOptions(key: KeyLike) {
return {
key: isKeyObject(key) ? key : crypto.createPublicKey(key),
padding: this.options.signaturePadding,
};
}
private encryptOptions(key: KeyLike) {
return {
key,
oaepHash: this.options.hashAlgorithm,
padding: this.options.messagePadding,
};
}
private async serverKey(): Promise<KeyObject> {
if (isKeyObject(this.key)) {
return this.key;
}
const keyResult = this.key();
const keyLike = isPromise(keyResult) ? await keyResult : keyResult;
return isKeyObject(keyLike) ? keyLike : crypto.createPrivateKey(keyLike);
}
}