Skip to content

Commit

Permalink
Merge pull request #312 from PeculiarVentures/always-authenticate
Browse files Browse the repository at this point in the history
Implement Event Handling for AlwaysAuthenticate Keys with `operation-pin` Event
  • Loading branch information
microshine authored Oct 11, 2024
2 parents 60945da + 846b505 commit e67a34b
Show file tree
Hide file tree
Showing 9 changed files with 798 additions and 613 deletions.
12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,14 @@
"homepage": "https://github.com/PeculiarVentures/webcrypto-local#readme",
"devDependencies": {
"2key-ratchet": "^1.0.18",
"@babel/core": "^7.24.6",
"@babel/core": "^7.25.8",
"@babel/plugin-proposal-class-properties": "^7.18.6",
"@babel/plugin-proposal-object-rest-spread": "^7.20.7",
"@babel/preset-env": "^7.24.6",
"@babel/preset-env": "^7.25.8",
"@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-node-resolve": "^15.2.3",
"@types/long": "^5.0.0",
"@types/mocha": "^10.0.8",
"@types/mocha": "^10.0.9",
"@types/node": "^20.14.2",
"@types/ws": "^8.5.10",
"colors": "^1.4.0",
Expand All @@ -73,14 +73,14 @@
"ts-node": "^10.9.2",
"tsconfig-paths": "^4.2.0",
"tslint": "^6.1.3",
"typedoc": "^0.26.7",
"typedoc": "^0.26.8",
"typedoc-plugin-lerna-packages": "^0.3.1",
"typescript": "^5.6.2"
"typescript": "^5.6.3"
},
"resolutions": {
"pkcs11js": "2.1.6",
"@peculiar/asn1-schema": "^2.1.7",
"tsprotobuf": "^1.0.19"
},
"dependencies": {}
}
}
2 changes: 1 addition & 1 deletion packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
"idb": "^2",
"pvtsutils": "^1.3.5",
"tslib": "^2.7.0",
"webcrypto-core": "^1.8.0",
"webcrypto-core": "^1.8.1",
"ws": "^8.17.0"
}
}
6 changes: 3 additions & 3 deletions packages/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,18 @@
"@peculiar/asn1-x509": "^2.3.13",
"@peculiar/json-schema": "^1.1.12",
"@peculiar/webcrypto": "1.0.22",
"@peculiar/x509": "^1.12.1",
"@peculiar/x509": "^1.12.3",
"@types/pvutils": "^1.0.4",
"@webcrypto-local/cards": "^1.10.5",
"@webcrypto-local/core": "^1.10.6",
"@webcrypto-local/proto": "^1.10.6",
"graphene-pk11": "^2.3.6",
"node-webcrypto-p11": "^2.6.5",
"node-webcrypto-p11": "^2.6.7",
"pcsclite": "^1.0.1",
"pvtsutils": "^1.3.5",
"pvutils": "^1.1.3",
"tslib": "^2.7.0",
"webcrypto-core": "^1.8.0",
"webcrypto-core": "^1.8.1",
"ws": "^8.17.0"
},
"resolutions": {
Expand Down
38 changes: 19 additions & 19 deletions packages/server/src/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,25 +165,25 @@ export class LocalProvider extends core.EventLogEmitter {
if (fs.existsSync(prov.lib)) {
prov.slots = prov.slots || [0];
for (const slot of prov.slots) {
try {
const crypto = new Pkcs11Crypto({
library: prov.lib,
libraryParameters: prov.libraryParameters,
slot,
readWrite: true,
});
if (prov.config) {
this.log("info", "Use ConfigTemplateBuilder", prov.config);
crypto.templateBuilder = new ConfigTemplateBuilder(prov.config);
} else {
this.log("info", "Use default TemplateBuilder");
}
this.addProvider(crypto, {
name: prov.name,
});
} catch (err) {
this.emit("error", new WebCryptoLocalError(WebCryptoLocalError.CODE.PROVIDER_INIT, `Provider:open Cannot load PKCS#11 library by path ${prov.lib}. ${stringifyError(err)}`));
try {
const crypto = new Pkcs11Crypto({
library: prov.lib,
libraryParameters: prov.libraryParameters,
slot,
readWrite: true,
});
if (prov.config) {
this.log("info", "Use ConfigTemplateBuilder", prov.config);
crypto.templateBuilder = new ConfigTemplateBuilder(prov.config);
} else {
this.log("info", "Use default TemplateBuilder");
}
this.addProvider(crypto, {
name: prov.name,
});
} catch (err) {
this.emit("error", new WebCryptoLocalError(WebCryptoLocalError.CODE.PROVIDER_INIT, `Provider:open Cannot load PKCS#11 library by path ${prov.lib}. ${stringifyError(err)}`));
}
}
} else {
this.log("info", `File ${prov.lib} does not exist`, { action: "open" });
Expand Down Expand Up @@ -264,7 +264,7 @@ export class LocalProvider extends core.EventLogEmitter {
return crypto;
}

protected async onTokenInsert(card: Card) {
protected async onTokenInsert(card: Card) {
this.log("info", "Token was added to the reader", {
reader: card.reader,
name: card.name,
Expand Down
21 changes: 4 additions & 17 deletions packages/server/src/services/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,10 @@ import { MemoryStorage } from "../memory_storage";
import { PCSCCard } from "../pcsc";
import { IProviderConfig, LocalProvider } from "../provider";
import { CryptoService } from "./crypto";
import { Service } from "./service";
import { Service, ServiceNotifyEvent, ServiceNotifyEventHandler } from "./service";

import { WebCryptoLocalError } from "../error";

export interface ProviderNotifyEvent {
type: string;
resolve: () => void;
reject: (error: Error) => void;
}

export type ProviderNotifyEventHandler = (e: ProviderNotifyEvent) => void;

export class ProviderService extends Service<LocalProvider> {

public source = "provider-service";
Expand All @@ -37,29 +29,28 @@ export class ProviderService extends Service<LocalProvider> {
//#region Connect events
this.object.on("token_new", this.onTokenNew.bind(this));
this.object.on("token", this.onToken.bind(this));
crypto.on("notify", this.onNotify.bind(this));
//#endregion
}

//#region Events

public emit(event: "notify", e: ProviderNotifyEvent): boolean;
public emit(event: "notify", e: ServiceNotifyEvent): boolean;
public emit(event: "token_new", e: PCSCCard): boolean;
public emit(event: "error", error: Error): boolean;
public emit(event: "info", level: core.LogLevel, source: string, message: string, data?: core.LogData): boolean;
public emit(event: string, ...args: any[]): boolean {
return super.emit(event, ...args);
}

public on(event: "notify", cb: ProviderNotifyEventHandler): this;
public on(event: "notify", cb: ServiceNotifyEventHandler): this;
public on(event: "token_new", cb: (e: PCSCCard) => void): this;
public on(event: "error", cb: (error: Error) => void): this;
public on(event: "info", cb: core.LogHandler): this;
public on(event: string, cb: (...args: any[]) => void): this {
return super.on(event, cb);
}

public once(event: "notify", cb: ProviderNotifyEventHandler): this;
public once(event: "notify", cb: ServiceNotifyEventHandler): this;
public once(event: "token_new", cb: (e: PCSCCard) => void): this;
public once(event: "error", cb: (error: Error) => void): this;
public once(event: "info", cb: core.LogHandler): this;
Expand Down Expand Up @@ -112,10 +103,6 @@ export class ProviderService extends Service<LocalProvider> {
}
}

protected onNotify(e: ProviderNotifyEvent) {
this.emit("notify", e);
}

protected async onMessage(session: Session, action: proto.ActionProto) {
const result = new proto.ResultProto(action);
switch (action.action) {
Expand Down
17 changes: 17 additions & 0 deletions packages/server/src/services/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ import { Crypto, CryptoKey, Pkcs11KeyAlgorithm } from "node-webcrypto-p11";

import { Server, Session } from "../connection";

export interface ServiceNotifyEvent {
type: string;
resolve: (...args: any[]) => void;
reject: (error: Error) => void;
}

export type ServiceNotifyEventHandler = (e: ServiceNotifyEvent) => void;

/**
* Base class for services
* NOTE: Each object must have info, error events
Expand Down Expand Up @@ -47,6 +55,9 @@ export abstract class Service<T extends core.EventLogEmitter> extends core.Event
})
.on("error", (error: Error) => {
this.emit("error", error);
})
.on("notify", (e: ServiceNotifyEvent) => {
this.emit("notify", e);
});
}
//#endregion
Expand All @@ -64,23 +75,29 @@ export abstract class Service<T extends core.EventLogEmitter> extends core.Event
})
.on("error", (error: Error) => {
this.emit("error", error);
})
.on("notify", (e: ServiceNotifyEvent) => {
this.emit("notify", e);
});
}

public emit(event: "notify", e: ServiceNotifyEvent): boolean;
public emit(event: "error", error: Error): boolean;
public emit(event: "info", level: core.LogLevel, source: string, message: string, data?: core.LogData): boolean;
public emit(event: string, ...args: any[]): boolean;
public emit(event: string, ...args: any[]): boolean {
return super.emit(event, ...args);
}

public on(event: "notify", cb: ServiceNotifyEventHandler): this;
public on(event: "error", cb: (error: Error) => void): this;
public on(event: "info", cb: core.LogHandler): this;
public on(event: string, cb: (...args: any[]) => void): this;
public on(event: string, cb: (...args: any[]) => void): this {
return super.on(event, cb);
}

public once(event: "notify", cb: ServiceNotifyEventHandler): this;
public once(event: "error", cb: (error: Error) => void): this;
public once(event: "info", cb: core.LogHandler): this;
public once(event: string, cb: (...args: any[]) => void): this;
Expand Down
37 changes: 35 additions & 2 deletions packages/server/src/services/subtle.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
import * as proto from "@webcrypto-local/proto";
import { CryptoKey } from "node-webcrypto-p11";
import { AlwaysAuthenticateHandle, AlwaysAuthenticateHandleResult, CryptoKey } from "node-webcrypto-p11";
import { Convert } from "pvtsutils";
import { ObjectProto } from "tsprotobuf";

import { Server, Session } from "../connection";
import { ServiceCryptoItem } from "../crypto_item";
import { CryptoService } from "./crypto";
import { Service } from "./service";
import { Service, ServiceNotifyEvent } from "./service";

import { WebCryptoLocalError } from "../error";

export interface SubtleAlwaysAuthenticateEvent extends ServiceNotifyEvent {
type: "operation-pin";
origin: string;
label: string;
keyLabel: string;
operation: string;
resolve: (pin: AlwaysAuthenticateHandleResult) => void;
reject: (error: Error) => void;
}

export class SubtleService extends Service<CryptoService> {

constructor(server: Server, crypto: CryptoService) {
Expand Down Expand Up @@ -39,6 +49,27 @@ export class SubtleService extends Service<CryptoService> {
return this.object.object.memoryStorage;
}

private handleAlwaysAuthenticate(session: Session): AlwaysAuthenticateHandle {
return (key, crypto, operation) => {
const promise = new Promise<AlwaysAuthenticateHandleResult>((resolve, reject) => {
const keyLabel = key instanceof CryptoKey ? key.algorithm.label : key.algorithm.name;

const event: SubtleAlwaysAuthenticateEvent = {
type: "operation-pin",
origin: session.origin + ":" + session.port,
label: crypto.session.slot.getToken().label,
keyLabel,
operation,
resolve,
reject,
};

this.emit("notify", event);
});
return promise;
};
}

protected async onMessage(session: Session, action: proto.ActionProto) {
const result = new proto.ResultProto(action);
switch (action.action) {
Expand Down Expand Up @@ -100,6 +131,7 @@ export class SubtleService extends Service<CryptoService> {
const params = await proto.SignActionProto.importProto(action);

const crypto = await this.getCrypto(params.providerID);
crypto.onAlwaysAuthenticate = this.handleAlwaysAuthenticate(session);
const key = this.getMemoryStorage().item(params.key.id).item as CryptoKey;
this.log("info", "sign", {
crypto: this.logCrypto(crypto),
Expand Down Expand Up @@ -147,6 +179,7 @@ export class SubtleService extends Service<CryptoService> {
const params = await proto.DecryptActionProto.importProto(action);

const crypto = await this.getCrypto(params.providerID);
crypto.onAlwaysAuthenticate = this.handleAlwaysAuthenticate(session);
const key = this.getMemoryStorage().item(params.key.id).item as CryptoKey;
this.log("info", "decrypt", {
crypto: this.logCrypto(crypto),
Expand Down
15 changes: 9 additions & 6 deletions scripts/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,17 @@ async function main() {
p.resolve(true);
break;
}
case "operation-pin":
case "pin": {
const { origin, label } = p;
if (p.type === "pin") {
console.log("PIN:", { origin, label });
} else {
const { keyLabel, operation } = p;
console.log("Operation PIN:", { origin, label, keyLabel, operation });
}
// auto PIN for all token's
switch (p.label) {
case "SafeNet U:39sp85MY":
p.resolve("39sp85MY");
break;
case "My slot 0":
p.resolve("12345");
break;
Expand All @@ -131,13 +136,11 @@ async function main() {
default:
p.resolve("123456");
break;
// throw new Error("Unknown token");
}
throw new Error("Oops");
break;
}
default:
throw new Error("Unknown type of notify");
p.reject(new Error(`Unknown notification type: ${p.type}`));
}
})
.on("close", (e: any) => {
Expand Down
Loading

0 comments on commit e67a34b

Please sign in to comment.