@virtonetwork/authenticators-substrate 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,9 @@
1
+ # Substrate Authentication for Virto Signer
2
+
3
+ A TypeScript helper that wires **substrate**-style signatures to the [@virtonetwork/signer](https://github.com/virto-network/papi-signers) stack. It exposes a single class, `Substrate`, that fulfils the `Authenticator<number>` interface used by `PassSigner`.
4
+
5
+ ## ✨ Features
6
+
7
+ * **One‑line setup** → `await new Substrate(user, { sign }).setup()`
8
+ * Deterministic `deviceId = Blake2‑256(credentialId)`
9
+ * Produces SCALE‑encoded `PassAuthenticate` objects
@@ -0,0 +1,163 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
13
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.SubstrateKey = exports.KREIVO_AUTHORITY_ID = void 0;
40
+ var signer_1 = require("@virtonetwork/signer");
41
+ var types_ts_1 = require("./types.js");
42
+ var polkadot_api_1 = require("polkadot-api");
43
+ exports.KREIVO_AUTHORITY_ID = polkadot_api_1.Binary.fromText("kreivo_p".padEnd(32, "\0"));
44
+ var SubstrateKey = /** @class */ (function () {
45
+ function SubstrateKey(userId, signer, getChallenge, addressGenerator) {
46
+ if (addressGenerator === void 0) { addressGenerator = signer_1.kreivoPassDefaultAddressGenerator; }
47
+ this.userId = userId;
48
+ this.signer = signer;
49
+ this.getChallenge = getChallenge;
50
+ this.addressGenerator = addressGenerator;
51
+ /**
52
+ * SHA‑256 hash of {@link userId}. Filled once by {@link setup} and reused
53
+ * for all WebAuthn operations.
54
+ */
55
+ this.hashedUserId = new Uint8Array(32);
56
+ }
57
+ /**
58
+ * Pre‑computes {@link hashedUserId}.
59
+ *
60
+ * Must be awaited **once** before any other interaction.
61
+ * Returns `this` for fluent chaining.
62
+ */
63
+ SubstrateKey.prototype.setup = function () {
64
+ return __awaiter(this, void 0, void 0, function () {
65
+ var _a;
66
+ return __generator(this, function (_b) {
67
+ switch (_b.label) {
68
+ case 0:
69
+ _a = this;
70
+ return [4 /*yield*/, SubstrateKey.getHashedUserId(this.userId)];
71
+ case 1:
72
+ _a.hashedUserId = _b.sent();
73
+ return [2 /*return*/, this];
74
+ }
75
+ });
76
+ });
77
+ };
78
+ SubstrateKey.getHashedUserId = function (userId) {
79
+ return __awaiter(this, void 0, void 0, function () {
80
+ var _a;
81
+ return __generator(this, function (_b) {
82
+ switch (_b.label) {
83
+ case 0:
84
+ _a = Uint8Array.bind;
85
+ return [4 /*yield*/, crypto.subtle.digest("SHA-256", new TextEncoder().encode(userId))];
86
+ case 1: return [2 /*return*/, new (_a.apply(Uint8Array, [void 0, _b.sent()]))()];
87
+ }
88
+ });
89
+ });
90
+ };
91
+ SubstrateKey.prototype.register = function (context) {
92
+ return __awaiter(this, void 0, void 0, function () {
93
+ var challenge, message, _a, _b;
94
+ var _c;
95
+ return __generator(this, function (_d) {
96
+ switch (_d.label) {
97
+ case 0: return [4 /*yield*/, this.getChallenge(context, this.addressGenerator(this.hashedUserId))];
98
+ case 1:
99
+ challenge = _d.sent();
100
+ message = types_ts_1.SignedMessage.enc({
101
+ context: context,
102
+ authority_id: exports.KREIVO_AUTHORITY_ID,
103
+ challenge: polkadot_api_1.Binary.fromBytes(challenge),
104
+ });
105
+ _c = {
106
+ message: types_ts_1.SignedMessage.dec(message),
107
+ public: polkadot_api_1.Binary.fromBytes(this.signer.publicKey)
108
+ };
109
+ _b = (_a = polkadot_api_1.Binary).fromBytes;
110
+ return [4 /*yield*/, this.signer.sign(message)];
111
+ case 2: return [2 /*return*/, (_c.signature = _b.apply(_a, [_d.sent()]),
112
+ _c)];
113
+ }
114
+ });
115
+ });
116
+ };
117
+ /**
118
+ * Signs an arbitrary challenge with the substrate key and produces a
119
+ * {@link TPassAuthenticate} payload understood by `PassSigner`.
120
+ *
121
+ * @param challenge - 32‑byte buffer supplied by the runtime.
122
+ * @param context - Block number (or any numeric context expected by the pallet).
123
+ *
124
+ * @returns SCALE‑encoded authentication payload.
125
+ * @throws Error If no credential id is available.
126
+ */
127
+ SubstrateKey.prototype.authenticate = function (context, xtc) {
128
+ return __awaiter(this, void 0, void 0, function () {
129
+ var challenge, message, _a, _b, _c, _d;
130
+ var _e, _f, _g;
131
+ return __generator(this, function (_h) {
132
+ switch (_h.label) {
133
+ case 0: return [4 /*yield*/, this.getChallenge(context, xtc)];
134
+ case 1:
135
+ challenge = _h.sent();
136
+ message = {
137
+ context: context,
138
+ authority_id: exports.KREIVO_AUTHORITY_ID,
139
+ challenge: polkadot_api_1.Binary.fromBytes(challenge),
140
+ };
141
+ _e = {
142
+ deviceId: polkadot_api_1.Binary.fromBytes(this.signer.publicKey)
143
+ };
144
+ _f = {
145
+ tag: "SubstrateKey"
146
+ };
147
+ _b = (_a = types_ts_1.KeySignature).enc;
148
+ _g = {
149
+ message: message
150
+ };
151
+ _d = (_c = polkadot_api_1.Binary).fromBytes;
152
+ return [4 /*yield*/, this.signer.sign(types_ts_1.SignedMessage.enc(message))];
153
+ case 2: return [2 /*return*/, (_e.credentials = (_f.value = _b.apply(_a, [(_g.signature = _d.apply(_c, [_h.sent()]),
154
+ _g)]),
155
+ _f),
156
+ _e)];
157
+ }
158
+ });
159
+ });
160
+ };
161
+ return SubstrateKey;
162
+ }());
163
+ exports.SubstrateKey = SubstrateKey;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.KeySignature = exports.SignedMessage = void 0;
4
+ var substrate_bindings_1 = require("@polkadot-api/substrate-bindings");
5
+ var scale_ts_1 = require("scale-ts");
6
+ exports.SignedMessage = (0, scale_ts_1.Struct)({
7
+ context: scale_ts_1.u32,
8
+ challenge: (0, substrate_bindings_1.Bin)(),
9
+ authority_id: (0, substrate_bindings_1.Bin)(32),
10
+ });
11
+ exports.KeySignature = (0, scale_ts_1.Struct)({
12
+ message: exports.SignedMessage,
13
+ signature: (0, substrate_bindings_1.Bin)(),
14
+ });
@@ -0,0 +1,36 @@
1
+ import { AddressGenerator, Authenticator, Challenger, TPassAuthenticate } from "@virtonetwork/signer";
2
+ import { SubstrateSigner, TKeyRegistration } from "./types.ts";
3
+ import { Binary } from "polkadot-api";
4
+ export declare const KREIVO_AUTHORITY_ID: Binary;
5
+ export declare class SubstrateKey implements Authenticator<number> {
6
+ readonly userId: string;
7
+ private signer;
8
+ readonly getChallenge: Challenger<number>;
9
+ readonly addressGenerator: AddressGenerator;
10
+ /**
11
+ * SHA‑256 hash of {@link userId}. Filled once by {@link setup} and reused
12
+ * for all WebAuthn operations.
13
+ */
14
+ hashedUserId: Uint8Array;
15
+ constructor(userId: string, signer: SubstrateSigner, getChallenge: Challenger<number>, addressGenerator?: AddressGenerator);
16
+ /**
17
+ * Pre‑computes {@link hashedUserId}.
18
+ *
19
+ * Must be awaited **once** before any other interaction.
20
+ * Returns `this` for fluent chaining.
21
+ */
22
+ setup(): Promise<this>;
23
+ private static getHashedUserId;
24
+ register(context: number): Promise<TKeyRegistration<number>>;
25
+ /**
26
+ * Signs an arbitrary challenge with the substrate key and produces a
27
+ * {@link TPassAuthenticate} payload understood by `PassSigner`.
28
+ *
29
+ * @param challenge - 32‑byte buffer supplied by the runtime.
30
+ * @param context - Block number (or any numeric context expected by the pallet).
31
+ *
32
+ * @returns SCALE‑encoded authentication payload.
33
+ * @throws Error If no credential id is available.
34
+ */
35
+ authenticate(context: number, xtc: Uint8Array): Promise<TPassAuthenticate>;
36
+ }
@@ -0,0 +1,75 @@
1
+ import { kreivoPassDefaultAddressGenerator, } from "@virtonetwork/signer";
2
+ import { KeySignature, SignedMessage, } from "./types.js";
3
+ import { Binary } from "polkadot-api";
4
+ export const KREIVO_AUTHORITY_ID = Binary.fromText("kreivo_p".padEnd(32, "\0"));
5
+ export class SubstrateKey {
6
+ userId;
7
+ signer;
8
+ getChallenge;
9
+ addressGenerator;
10
+ /**
11
+ * SHA‑256 hash of {@link userId}. Filled once by {@link setup} and reused
12
+ * for all WebAuthn operations.
13
+ */
14
+ hashedUserId = new Uint8Array(32);
15
+ constructor(userId, signer, getChallenge, addressGenerator = kreivoPassDefaultAddressGenerator) {
16
+ this.userId = userId;
17
+ this.signer = signer;
18
+ this.getChallenge = getChallenge;
19
+ this.addressGenerator = addressGenerator;
20
+ }
21
+ /**
22
+ * Pre‑computes {@link hashedUserId}.
23
+ *
24
+ * Must be awaited **once** before any other interaction.
25
+ * Returns `this` for fluent chaining.
26
+ */
27
+ async setup() {
28
+ this.hashedUserId = await SubstrateKey.getHashedUserId(this.userId);
29
+ return this;
30
+ }
31
+ static async getHashedUserId(userId) {
32
+ return new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(userId)));
33
+ }
34
+ async register(context) {
35
+ const challenge = await this.getChallenge(context, this.addressGenerator(this.hashedUserId));
36
+ const message = SignedMessage.enc({
37
+ context,
38
+ authority_id: KREIVO_AUTHORITY_ID,
39
+ challenge: Binary.fromBytes(challenge),
40
+ });
41
+ return {
42
+ message: SignedMessage.dec(message),
43
+ public: Binary.fromBytes(this.signer.publicKey),
44
+ signature: Binary.fromBytes(await this.signer.sign(message)),
45
+ };
46
+ }
47
+ /**
48
+ * Signs an arbitrary challenge with the substrate key and produces a
49
+ * {@link TPassAuthenticate} payload understood by `PassSigner`.
50
+ *
51
+ * @param challenge - 32‑byte buffer supplied by the runtime.
52
+ * @param context - Block number (or any numeric context expected by the pallet).
53
+ *
54
+ * @returns SCALE‑encoded authentication payload.
55
+ * @throws Error If no credential id is available.
56
+ */
57
+ async authenticate(context, xtc) {
58
+ const challenge = await this.getChallenge(context, xtc);
59
+ const message = {
60
+ context,
61
+ authority_id: KREIVO_AUTHORITY_ID,
62
+ challenge: Binary.fromBytes(challenge),
63
+ };
64
+ return {
65
+ deviceId: Binary.fromBytes(this.signer.publicKey),
66
+ credentials: {
67
+ tag: "SubstrateKey",
68
+ value: KeySignature.enc({
69
+ message: message,
70
+ signature: Binary.fromBytes(await this.signer.sign(SignedMessage.enc(message))),
71
+ }),
72
+ },
73
+ };
74
+ }
75
+ }
@@ -0,0 +1,23 @@
1
+ import { Binary, FixedSizeBinary } from "@polkadot-api/substrate-bindings";
2
+ import { Codec } from "scale-ts";
3
+ import { AuthorityId } from "@virtonetwork/signer";
4
+ export type SubstrateSigner = {
5
+ publicKey: Uint8Array;
6
+ sign: (bytes: Uint8Array) => Promise<Uint8Array> | Uint8Array;
7
+ };
8
+ export type TSignedMessage<Cx> = {
9
+ context: Cx;
10
+ challenge: Binary;
11
+ authority_id: AuthorityId;
12
+ };
13
+ export declare const SignedMessage: Codec<TSignedMessage<number>>;
14
+ export type TKeyRegistration<Cx> = {
15
+ message: TSignedMessage<Cx>;
16
+ public: FixedSizeBinary<32>;
17
+ signature: AuthorityId;
18
+ };
19
+ export type TKeySignature<Cx> = {
20
+ message: TSignedMessage<Cx>;
21
+ signature: Binary;
22
+ };
23
+ export declare const KeySignature: Codec<TKeySignature<number>>;
@@ -0,0 +1,11 @@
1
+ import { Bin } from "@polkadot-api/substrate-bindings";
2
+ import { Struct, u32 } from "scale-ts";
3
+ export const SignedMessage = Struct({
4
+ context: u32,
5
+ challenge: Bin(),
6
+ authority_id: Bin(32),
7
+ });
8
+ export const KeySignature = Struct({
9
+ message: SignedMessage,
10
+ signature: Bin(),
11
+ });
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@virtonetwork/authenticators-substrate",
3
+ "description": "An Authenticator compatible with KreivoPassSigner that uses substrate-style signatures",
4
+ "version": "1.0.0",
5
+ "type": "module",
6
+ "files": [
7
+ "dist"
8
+ ],
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/esm/index.d.ts",
12
+ "module": "./dist/esm/index.js",
13
+ "import": "./dist/esm/index.js",
14
+ "require": "./dist/cjs/index.js"
15
+ },
16
+ "./package.json": "./package.json"
17
+ },
18
+ "main": "./dist/cjs/index.js",
19
+ "module": "./dist/esm/index.js",
20
+ "browser": "./dist/esm/index.js",
21
+ "types": "./dist/esm/index.d.ts",
22
+ "scripts": {
23
+ "test": "node --loader ts-node/esm test/test.ts",
24
+ "build": "tsc && tsc -p tsconfig.cjs.json",
25
+ "prepack": "npm run build"
26
+ },
27
+ "author": "Virto Network <contact@virto.networks>",
28
+ "license": "MIT",
29
+ "keywords": [
30
+ "virto-sdk",
31
+ "signer",
32
+ "papi",
33
+ "scale",
34
+ "polkadot.js",
35
+ "polkadot"
36
+ ],
37
+ "dependencies": {
38
+ "@polkadot-labs/hdkd": "0.0.26",
39
+ "@virtonetwork/signer": "^1.2.0"
40
+ },
41
+ "devDependencies": {
42
+ "ts-node": "^10.9.2"
43
+ },
44
+ "repository": {
45
+ "url": "https://github.com/virto-network/papi-signers",
46
+ "directory": "authenticators/substrate"
47
+ },
48
+ "publishConfig": {
49
+ "registry": "https://registry.npmjs.org/",
50
+ "access": "public"
51
+ }
52
+ }