@novasamatech/host-papp 0.5.0-8 → 0.5.0-9
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/dist/adapters/identity/rpc.d.ts +3 -3
- package/dist/components/sso/index.d.ts +36 -0
- package/dist/components/sso/index.js +150 -0
- package/dist/components/sso/scale/handshake.d.ts +9 -0
- package/dist/components/sso/scale/handshake.js +10 -0
- package/dist/components/sso/types.d.ts +15 -0
- package/dist/components/sso/types.js +1 -0
- package/dist/components/user/index.d.ts +3 -3
- package/dist/components/user/userSessionStorage.d.ts +3 -3
- package/dist/components/user/userSessionStorage.js +1 -1
- package/dist/crypto.d.ts +23 -0
- package/dist/crypto.js +51 -0
- package/dist/helpers/state.d.ts +16 -0
- package/dist/helpers/state.js +51 -0
- package/dist/helpers/zipWith.d.ts +4 -0
- package/dist/helpers/zipWith.js +11 -0
- package/dist/identity/impl.d.ts +6 -0
- package/dist/identity/impl.js +68 -0
- package/dist/identity/rpcAdapter.d.ts +3 -0
- package/dist/identity/rpcAdapter.js +38 -0
- package/dist/identity/types.d.ts +22 -0
- package/dist/identity/types.js +1 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.js +1 -7
- package/dist/modules/crypto.d.ts +4 -8
- package/dist/modules/crypto.js +10 -43
- package/dist/modules/secretStorage.d.ts +12 -9
- package/dist/modules/secretStorage.js +36 -32
- package/dist/modules/session/helpers.d.ts +1 -1
- package/dist/modules/session/helpers.js +2 -2
- package/dist/modules/session/session.js +18 -14
- package/dist/modules/statementStore.d.ts +2 -2
- package/dist/modules/statementStore.js +4 -2
- package/dist/modules/storageView.d.ts +6 -6
- package/dist/modules/transport/crypto.js +5 -3
- package/dist/modules/transport/transport.d.ts +17 -2
- package/dist/modules/transport/transport.js +15 -5
- package/dist/papp.d.ts +24 -13
- package/dist/papp.js +18 -37
- package/dist/sso/auth/impl.d.ts +48 -0
- package/dist/sso/auth/impl.js +148 -0
- package/dist/sso/auth/scale/handshake.d.ts +9 -0
- package/dist/sso/auth/scale/handshake.js +10 -0
- package/dist/sso/auth/types.d.ts +15 -0
- package/dist/sso/auth/types.js +1 -0
- package/dist/sso/session/impl.d.ts +23 -0
- package/dist/sso/session/impl.js +57 -0
- package/dist/sso/session/scale/remoteMessage.d.ts +10 -0
- package/dist/sso/session/scale/remoteMessage.js +13 -0
- package/dist/sso/session/sessionManager.d.ts +23 -0
- package/dist/sso/session/sessionManager.js +58 -0
- package/dist/sso/session/ssoSession.d.ts +8 -0
- package/dist/sso/session/ssoSession.js +5 -0
- package/dist/sso/session/ssoSessionStorage.d.ts +21 -0
- package/dist/sso/session/ssoSessionStorage.js +20 -0
- package/dist/sso/session/types.d.ts +6 -0
- package/dist/sso/session/types.js +1 -0
- package/dist/sso/session/userSessionStorage.d.ts +21 -0
- package/dist/sso/session/userSessionStorage.js +20 -0
- package/dist/sso/sessionManager/impl.d.ts +22 -0
- package/dist/sso/sessionManager/impl.js +71 -0
- package/dist/sso/sessionManager/repository/ssoSessionRepository.d.ts +22 -0
- package/dist/sso/sessionManager/repository/ssoSessionRepository.js +27 -0
- package/dist/sso/sessionManager/scale/remoteMessage.d.ts +23 -0
- package/dist/sso/sessionManager/scale/remoteMessage.js +14 -0
- package/dist/sso/sessionManager/ssoSession.d.ts +23 -0
- package/dist/sso/sessionManager/ssoSession.js +69 -0
- package/dist/sso/sessionManager/ssoSessionProver.d.ts +4 -0
- package/dist/sso/sessionManager/ssoSessionProver.js +35 -0
- package/dist/sso/sessionManager/types.d.ts +6 -0
- package/dist/sso/sessionManager/types.js +1 -0
- package/dist/sso/sessionManager/userSession.d.ts +24 -0
- package/dist/sso/sessionManager/userSession.js +75 -0
- package/dist/sso/ssoSessionProver.d.ts +4 -0
- package/dist/sso/ssoSessionProver.js +35 -0
- package/dist/sso/ssoSessionRepository.d.ts +18 -0
- package/dist/sso/ssoSessionRepository.js +27 -0
- package/dist/sso/userSecretRepository.d.ts +16 -0
- package/dist/sso/userSecretRepository.js +44 -0
- package/dist/sso/userSessionRepository.d.ts +18 -0
- package/dist/sso/userSessionRepository.js +27 -0
- package/package.json +5 -3
package/dist/modules/crypto.js
CHANGED
|
@@ -1,17 +1,13 @@
|
|
|
1
|
-
import { gcm } from '@noble/ciphers/aes.js';
|
|
2
1
|
import { p256 } from '@noble/curves/nist.js';
|
|
3
|
-
import { blake2b } from '@noble/hashes/blake2.js';
|
|
4
|
-
import { hkdf } from '@noble/hashes/hkdf.js';
|
|
5
|
-
import { sha256 } from '@noble/hashes/sha2.js';
|
|
6
2
|
import { randomBytes } from '@noble/hashes/utils.js';
|
|
7
|
-
import { HDKD as sr25519HDKD, getPublicKey as sr25519GetPublicKey, secretFromSeed as sr25519SecretFromSeed, sign as sr25519Sign, } from '@scure/sr25519';
|
|
3
|
+
import { HDKD as sr25519HDKD, getPublicKey as sr25519GetPublicKey, secretFromSeed as sr25519SecretFromSeed, sign as sr25519Sign, verify as sr25519Verify, } from '@scure/sr25519';
|
|
8
4
|
import { Bytes, str, u32 } from 'scale-ts';
|
|
9
5
|
// schemas
|
|
10
|
-
function
|
|
6
|
+
export function BrandedBytesCodec(length) {
|
|
11
7
|
return Bytes(length);
|
|
12
8
|
}
|
|
13
|
-
export const SsPubKey =
|
|
14
|
-
export const EncrPubKey =
|
|
9
|
+
export const SsPubKey = BrandedBytesCodec(32);
|
|
10
|
+
export const EncrPubKey = BrandedBytesCodec(65);
|
|
15
11
|
// helpers
|
|
16
12
|
const textEncoder = new TextEncoder();
|
|
17
13
|
const textDecoder = new TextDecoder();
|
|
@@ -21,19 +17,9 @@ export function stringToBytes(str) {
|
|
|
21
17
|
export function bytesToString(bytes) {
|
|
22
18
|
return textDecoder.decode(bytes);
|
|
23
19
|
}
|
|
24
|
-
export function mergeBytes(...bytes) {
|
|
25
|
-
const len = bytes.reduce((l, b) => l + b.length, 0);
|
|
26
|
-
const merged = new Uint8Array(len);
|
|
27
|
-
let offset = 0;
|
|
28
|
-
for (const arr of bytes) {
|
|
29
|
-
merged.set(arr, offset);
|
|
30
|
-
offset += arr.length;
|
|
31
|
-
}
|
|
32
|
-
return merged;
|
|
33
|
-
}
|
|
34
20
|
// statement store key pair
|
|
35
21
|
export const SS_SECRET_SEED_SIZE = 32;
|
|
36
|
-
export function createSsSecret(seed) {
|
|
22
|
+
export function createSsSecret(seed = randomBytes(SS_SECRET_SEED_SIZE)) {
|
|
37
23
|
return sr25519SecretFromSeed(seed);
|
|
38
24
|
}
|
|
39
25
|
export function createSsHardDerivation(secret, derivation) {
|
|
@@ -47,38 +33,19 @@ export function getSsPub(secret) {
|
|
|
47
33
|
export function signWithSsSecret(secret, message) {
|
|
48
34
|
return sr25519Sign(secret, message);
|
|
49
35
|
}
|
|
36
|
+
export function verifyWithSsSecret(message, signature, publicKey) {
|
|
37
|
+
return sr25519Verify(message, signature, publicKey);
|
|
38
|
+
}
|
|
50
39
|
// encryption key pair
|
|
51
40
|
export const ENCR_SECRET_SEED_SIZE = 48;
|
|
52
|
-
export function createEncrSecret(seed) {
|
|
41
|
+
export function createEncrSecret(seed = randomBytes(ENCR_SECRET_SEED_SIZE)) {
|
|
53
42
|
const { secretKey } = p256.keygen(seed);
|
|
54
43
|
return secretKey;
|
|
55
44
|
}
|
|
56
45
|
export function getEncrPub(secret) {
|
|
57
46
|
return p256.getPublicKey(secret, false);
|
|
58
47
|
}
|
|
59
|
-
// helpers
|
|
60
|
-
export function createRandomSeed(suffix, size) {
|
|
61
|
-
return blake2b(mergeBytes(randomBytes(128), stringToBytes(suffix)), { dkLen: size });
|
|
62
|
-
}
|
|
63
|
-
export function createStableSeed(value, size) {
|
|
64
|
-
return blake2b(stringToBytes(value), { dkLen: size });
|
|
65
|
-
}
|
|
66
|
-
export function khash(secret, message) {
|
|
67
|
-
return blake2b(message, { dkLen: 256 / 8, key: secret });
|
|
68
|
-
}
|
|
69
48
|
export function createSharedSecret(secret, publicKey) {
|
|
49
|
+
// slicing first byte: @noble/curves adds y offset at the start
|
|
70
50
|
return p256.getSharedSecret(secret, publicKey).slice(1, 33);
|
|
71
51
|
}
|
|
72
|
-
export function encrypt(secret, cipherText) {
|
|
73
|
-
const nonce = randomBytes(12);
|
|
74
|
-
const aesKey = hkdf(sha256, secret, new Uint8Array(), new Uint8Array(), 32);
|
|
75
|
-
const aes = gcm(aesKey, nonce);
|
|
76
|
-
return aes.encrypt(mergeBytes(nonce, cipherText));
|
|
77
|
-
}
|
|
78
|
-
export function decrypt(secret, message) {
|
|
79
|
-
const nonce = message.slice(0, 12);
|
|
80
|
-
const cipherText = message.slice(12);
|
|
81
|
-
const aesKey = hkdf(sha256, secret, new Uint8Array(), new Uint8Array(), 32);
|
|
82
|
-
const aes = gcm(aesKey, nonce);
|
|
83
|
-
return aes.decrypt(cipherText);
|
|
84
|
-
}
|
|
@@ -1,12 +1,15 @@
|
|
|
1
|
+
import type { StorageAdapter } from '@novasamatech/storage-adapter';
|
|
1
2
|
import type { ResultAsync } from 'neverthrow';
|
|
2
|
-
import type {
|
|
3
|
+
import type { CodecType } from 'scale-ts';
|
|
3
4
|
import type { EncrSecret, SsSecret } from './crypto.js';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
type UserSecrets = CodecType<typeof UserSecretsCodec>;
|
|
6
|
+
declare const UserSecretsCodec: import("scale-ts").Codec<{
|
|
7
|
+
SsSecret: SsSecret;
|
|
8
|
+
EncrSecret: EncrSecret;
|
|
9
|
+
}>;
|
|
10
|
+
export declare function createSecretStorage(salt: string, storage: StorageAdapter): {
|
|
11
|
+
read(sessionId: string): ResultAsync<UserSecrets | null, Error>;
|
|
12
|
+
write(sessionId: string, value: UserSecrets): ResultAsync<void, Error>;
|
|
13
|
+
clear(sessionId: string): ResultAsync<void, Error>;
|
|
11
14
|
};
|
|
12
|
-
export
|
|
15
|
+
export {};
|
|
@@ -1,40 +1,44 @@
|
|
|
1
1
|
import { gcm } from '@noble/ciphers/aes.js';
|
|
2
2
|
import { blake2b } from '@noble/hashes/blake2.js';
|
|
3
3
|
import { fromHex, toHex } from '@polkadot-api/utils';
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
import { fromThrowable } from 'neverthrow';
|
|
5
|
+
import { Struct } from 'scale-ts';
|
|
6
|
+
import { toError } from '../helpers/utils.js';
|
|
7
|
+
import { BrandedBytesCodec, stringToBytes } from './crypto.js';
|
|
8
|
+
const UserSecretsCodec = Struct({
|
|
9
|
+
SsSecret: BrandedBytesCodec(),
|
|
10
|
+
EncrSecret: BrandedBytesCodec(),
|
|
11
|
+
});
|
|
12
|
+
export function createSecretStorage(salt, storage) {
|
|
13
|
+
const baseKey = 'UserSecrets';
|
|
14
|
+
const encode = fromThrowable(UserSecretsCodec.enc, toError);
|
|
15
|
+
const decode = fromThrowable((value) => (value ? UserSecretsCodec.dec(value) : null), toError);
|
|
16
|
+
const encrypt = fromThrowable((value) => {
|
|
17
|
+
const aes = getAes(salt);
|
|
18
|
+
return toHex(aes.encrypt(value));
|
|
19
|
+
}, toError);
|
|
20
|
+
const decrypt = fromThrowable((value) => {
|
|
21
|
+
if (value === null) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
const aes = getAes(salt);
|
|
25
|
+
return aes.decrypt(fromHex(value));
|
|
26
|
+
}, toError);
|
|
8
27
|
return {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
28
|
+
read(sessionId) {
|
|
29
|
+
return storage.read(createKey(baseKey, sessionId)).andThen(decrypt).andThen(decode);
|
|
30
|
+
},
|
|
31
|
+
write(sessionId, value) {
|
|
32
|
+
return encode(value)
|
|
33
|
+
.andThen(encrypt)
|
|
34
|
+
.asyncAndThen(value => storage.write(createKey(baseKey, sessionId), value));
|
|
35
|
+
},
|
|
36
|
+
clear(sessionId) {
|
|
37
|
+
return storage.clear(createKey(baseKey, sessionId));
|
|
38
|
+
},
|
|
15
39
|
};
|
|
16
40
|
}
|
|
17
41
|
const createKey = (key, context) => `${key}_${context}`;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
return storage.read(createKey(baseKey, context)).map(value => {
|
|
21
|
-
if (value) {
|
|
22
|
-
const aes = getAes(appId);
|
|
23
|
-
return aes.decrypt(fromHex(value));
|
|
24
|
-
}
|
|
25
|
-
else {
|
|
26
|
-
return null;
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
},
|
|
30
|
-
write(context, value) {
|
|
31
|
-
const aes = getAes(appId);
|
|
32
|
-
return storage.write(createKey(baseKey, context), toHex(aes.encrypt(value)));
|
|
33
|
-
},
|
|
34
|
-
clear(context) {
|
|
35
|
-
return storage.clear(createKey(baseKey, context));
|
|
36
|
-
},
|
|
37
|
-
});
|
|
38
|
-
function getAes(appId) {
|
|
39
|
-
return gcm(blake2b(stringToBytes(appId), { dkLen: 16 }), blake2b(stringToBytes('nonce'), { dkLen: 32 }));
|
|
42
|
+
function getAes(salt) {
|
|
43
|
+
return gcm(blake2b(stringToBytes(salt), { dkLen: 16 }), blake2b(stringToBytes('nonce'), { dkLen: 32 }));
|
|
40
44
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Account } from './types.js';
|
|
2
|
-
export declare function createSessionId(accountA: Account, accountB: Account): Uint8Array<ArrayBufferLike>;
|
|
2
|
+
export declare function createSessionId(sharedSecret: Uint8Array, accountA: Account, accountB: Account): Uint8Array<ArrayBufferLike>;
|
|
3
3
|
export declare function createRequestChannel(session: Uint8Array): Uint8Array<ArrayBufferLike>;
|
|
4
4
|
export declare function createResponseChannel(session: Uint8Array): Uint8Array<ArrayBufferLike>;
|
|
5
5
|
export declare function createAccount(accountId: Uint8Array, publicKey: Uint8Array, pin?: string): Account;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { mergeUint8 } from '@polkadot-api/utils';
|
|
2
2
|
import { khash, stringToBytes } from '../crypto.js';
|
|
3
|
-
export function createSessionId(accountA, accountB) {
|
|
3
|
+
export function createSessionId(sharedSecret, accountA, accountB) {
|
|
4
4
|
const sessionPrefix = stringToBytes('session');
|
|
5
5
|
const pinSeparator = stringToBytes('/');
|
|
6
6
|
function makePin(pin) {
|
|
@@ -12,7 +12,7 @@ export function createSessionId(accountA, accountB) {
|
|
|
12
12
|
makePin(accountA.pin),
|
|
13
13
|
makePin(accountB.pin),
|
|
14
14
|
]);
|
|
15
|
-
return khash(
|
|
15
|
+
return khash(sharedSecret, mergeUint8(sessionPrefix, accountSessionParams));
|
|
16
16
|
}
|
|
17
17
|
export function createRequestChannel(session) {
|
|
18
18
|
return khash(session, stringToBytes('request'));
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import { toHex } from '@polkadot-api/utils';
|
|
1
2
|
import { okAsync } from 'neverthrow';
|
|
2
|
-
import { toHex } from 'polkadot-api/utils';
|
|
3
3
|
import { storageListView } from '../storageView.js';
|
|
4
4
|
import { createSessionId } from './helpers.js';
|
|
5
5
|
export function createSession({ ownAccount, peerAccount, transport, storage, codec, }) {
|
|
6
|
-
// const ownSession = createSessionId(ownAccount, peerAccount);
|
|
7
|
-
const peerSession = createSessionId(peerAccount, ownAccount);
|
|
6
|
+
// const ownSession = createSessionId(peerAccount.publicKey, ownAccount, peerAccount);
|
|
7
|
+
const peerSession = createSessionId(peerAccount.publicKey, peerAccount, ownAccount);
|
|
8
8
|
const processedStorage = storageListView({
|
|
9
9
|
key: `ProcessesMessages_${toHex(peerSession)}`,
|
|
10
10
|
storage,
|
|
@@ -15,19 +15,23 @@ export function createSession({ ownAccount, peerAccount, transport, storage, cod
|
|
|
15
15
|
let subscriptions = [];
|
|
16
16
|
return {
|
|
17
17
|
subscribe(callback) {
|
|
18
|
-
const unsub = transport.
|
|
19
|
-
processedStorage.read().
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if (processed) {
|
|
25
|
-
return processedStorage.mutate(p => p.concat(message.requestId));
|
|
26
|
-
}
|
|
27
|
-
else {
|
|
18
|
+
const unsub = transport.subscribe({ ownAccount, peerAccount, codec }, async (messages) => {
|
|
19
|
+
processedStorage.read().map(processed => {
|
|
20
|
+
for (const message of messages) {
|
|
21
|
+
if (message.type === 'response')
|
|
22
|
+
continue;
|
|
23
|
+
if (processed.includes(message.requestId)) {
|
|
28
24
|
return okAsync();
|
|
29
25
|
}
|
|
30
|
-
|
|
26
|
+
callback(message.data).andThen(processed => {
|
|
27
|
+
if (processed) {
|
|
28
|
+
return processedStorage.mutate(p => p.concat(message.requestId));
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
return okAsync();
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
31
35
|
});
|
|
32
36
|
});
|
|
33
37
|
subscriptions.push(unsub);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { UserSession } from '../
|
|
1
|
+
import type { UserSession } from '../sso/session/ssoSessionStorage.js';
|
|
2
2
|
import type { SsSecret } from './crypto.js';
|
|
3
3
|
import type { Account } from './session/types.js';
|
|
4
4
|
export declare function createUserSession(hostAccount: Account, peerAccount: Account): UserSession;
|
|
@@ -8,5 +8,5 @@ type StatementPayload = {
|
|
|
8
8
|
topics: Uint8Array[];
|
|
9
9
|
data: Uint8Array;
|
|
10
10
|
};
|
|
11
|
-
export declare function createStatement(secret: SsSecret, payload: StatementPayload):
|
|
11
|
+
export declare function createStatement(secret: SsSecret, payload: StatementPayload): import("neverthrow").ResultAsync<import("@polkadot-api/sdk-statement").SignedStatement, Error>;
|
|
12
12
|
export {};
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { getStatementSigner } from '@polkadot-api/sdk-statement';
|
|
2
2
|
import { Binary } from '@polkadot-api/substrate-bindings';
|
|
3
3
|
import { nanoid } from 'nanoid';
|
|
4
|
+
import { fromPromise } from 'neverthrow';
|
|
5
|
+
import { toError } from '../helpers/utils.js';
|
|
4
6
|
import { getSsPub, signWithSsSecret } from './crypto.js';
|
|
5
7
|
export function createUserSession(hostAccount, peerAccount) {
|
|
6
8
|
return {
|
|
@@ -11,10 +13,10 @@ export function createUserSession(hostAccount, peerAccount) {
|
|
|
11
13
|
}
|
|
12
14
|
export function createStatement(secret, payload) {
|
|
13
15
|
const signer = getStatementSigner(getSsPub(secret), 'sr25519', data => signWithSsSecret(secret, data));
|
|
14
|
-
return signer.sign({
|
|
16
|
+
return fromPromise(signer.sign({
|
|
15
17
|
priority: payload.priority,
|
|
16
18
|
channel: Binary.fromBytes(payload.channel),
|
|
17
19
|
topics: payload.topics.map(Binary.fromBytes),
|
|
18
20
|
data: Binary.fromBytes(payload.data),
|
|
19
|
-
});
|
|
21
|
+
}), toError);
|
|
20
22
|
}
|
|
@@ -9,17 +9,17 @@ type Params<T> = {
|
|
|
9
9
|
to(value: T): string | null;
|
|
10
10
|
};
|
|
11
11
|
export declare function storageView<T>({ storage, initial, key, from, to, autosync }: Params<T>): {
|
|
12
|
-
read():
|
|
13
|
-
write(value: T):
|
|
14
|
-
clear():
|
|
12
|
+
read(): any;
|
|
13
|
+
write(value: T): any;
|
|
14
|
+
clear(): any;
|
|
15
15
|
subscribe(fn: (value: T) => void): () => void;
|
|
16
16
|
};
|
|
17
17
|
export declare function storageListView<T>(params: Params<T[]>): {
|
|
18
18
|
add(value: T): ResultAsync<T, Error>;
|
|
19
19
|
mutate(fn: (value: T[]) => T[]): ResultAsync<T[], Error>;
|
|
20
|
-
read():
|
|
21
|
-
write(value: T[]):
|
|
22
|
-
clear():
|
|
20
|
+
read(): any;
|
|
21
|
+
write(value: T[]): any;
|
|
22
|
+
clear(): any;
|
|
23
23
|
subscribe(fn: (value: T[]) => void): () => void;
|
|
24
24
|
};
|
|
25
25
|
export {};
|
|
@@ -2,12 +2,14 @@ import { gcm } from '@noble/ciphers/aes.js';
|
|
|
2
2
|
import { hkdf } from '@noble/hashes/hkdf.js';
|
|
3
3
|
import { sha256 } from '@noble/hashes/sha2.js';
|
|
4
4
|
import { randomBytes } from '@noble/hashes/utils.js';
|
|
5
|
-
import {
|
|
5
|
+
import { mergeBytes } from '../crypto.js';
|
|
6
6
|
export function encrypt(secret, cipherText) {
|
|
7
7
|
const nonce = randomBytes(12);
|
|
8
|
-
const
|
|
8
|
+
const salt = new Uint8Array(); // secure enough since P256 random keys provide enough entropy
|
|
9
|
+
const info = new Uint8Array(); // no need to introduce any context
|
|
10
|
+
const aesKey = hkdf(sha256, secret, salt, info, 32);
|
|
9
11
|
const aes = gcm(aesKey, nonce);
|
|
10
|
-
return aes.encrypt(
|
|
12
|
+
return aes.encrypt(mergeBytes(nonce, cipherText));
|
|
11
13
|
}
|
|
12
14
|
export function decrypt(secret, message) {
|
|
13
15
|
const nonce = message.slice(0, 12);
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { Statement } from '@polkadot-api/sdk-statement';
|
|
2
|
+
import type { ResultAsync } from 'neverthrow';
|
|
2
3
|
import type { Codec } from 'scale-ts';
|
|
3
4
|
import type { StatementAdapter } from '../../adapters/statement/types.js';
|
|
4
5
|
import type { Callback } from '../../types.js';
|
|
6
|
+
import type { SsSecret } from '../crypto.js';
|
|
5
7
|
import type { Account } from '../session/types.js';
|
|
6
8
|
import type { TransportError } from './codec.js';
|
|
7
9
|
export type Transport = ReturnType<typeof createTransport>;
|
|
@@ -21,7 +23,20 @@ type Params = {
|
|
|
21
23
|
};
|
|
22
24
|
export declare function createTransport({ adapter }: Params): {
|
|
23
25
|
subscribeSession(sessionId: Uint8Array, callback: Callback<Statement[]>): VoidFunction;
|
|
24
|
-
subscribe<T>(ownAccount
|
|
25
|
-
|
|
26
|
+
subscribe<T>({ ownAccount, peerAccount, codec, }: {
|
|
27
|
+
ownAccount: Account;
|
|
28
|
+
peerAccount: Account;
|
|
29
|
+
codec: Codec<T>;
|
|
30
|
+
}, callback: Callback<Message<T>[]>): VoidFunction;
|
|
31
|
+
handleRequest<T>(params: {
|
|
32
|
+
ownAccount: Account;
|
|
33
|
+
peerAccount: Account;
|
|
34
|
+
codec: Codec<T>;
|
|
35
|
+
}, callback: Callback<RequestMessage<T>>): VoidFunction;
|
|
36
|
+
submitRequest({ ownAccount, peerAccount, secret }: {
|
|
37
|
+
ownAccount: Account;
|
|
38
|
+
peerAccount: Account;
|
|
39
|
+
secret: SsSecret;
|
|
40
|
+
}, message: Uint8Array): ResultAsync<void, Error>;
|
|
26
41
|
};
|
|
27
42
|
export {};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Result, fromThrowable, ok } from 'neverthrow';
|
|
2
2
|
import { nonNullable, toError } from '../../helpers/utils.js';
|
|
3
3
|
import { decrypt } from '../crypto.js';
|
|
4
|
-
import { createSessionId } from '../session/helpers.js';
|
|
4
|
+
import { createRequestChannel, createSessionId } from '../session/helpers.js';
|
|
5
|
+
import { createStatement } from '../statementStore.js';
|
|
5
6
|
import { StatementData } from './codec.js';
|
|
6
7
|
const decryptResults = fromThrowable(decrypt, toError);
|
|
7
8
|
function mapMessage(statementData) {
|
|
@@ -27,8 +28,8 @@ export function createTransport({ adapter }) {
|
|
|
27
28
|
subscribeSession(sessionId, callback) {
|
|
28
29
|
return adapter.subscribeStatements([sessionId], callback);
|
|
29
30
|
},
|
|
30
|
-
subscribe(ownAccount, peerAccount, codec, callback) {
|
|
31
|
-
const sessionId = createSessionId(peerAccount, ownAccount);
|
|
31
|
+
subscribe({ ownAccount, peerAccount, codec, }, callback) {
|
|
32
|
+
const sessionId = createSessionId(peerAccount.publicKey, peerAccount, ownAccount);
|
|
32
33
|
const statementDataCodec = StatementData(codec);
|
|
33
34
|
return adapter.subscribeStatements([sessionId], statements => {
|
|
34
35
|
Result.combine(statements.map(statement => {
|
|
@@ -46,11 +47,20 @@ export function createTransport({ adapter }) {
|
|
|
46
47
|
});
|
|
47
48
|
});
|
|
48
49
|
},
|
|
49
|
-
handleRequest(
|
|
50
|
-
return transport.subscribe(
|
|
50
|
+
handleRequest(params, callback) {
|
|
51
|
+
return transport.subscribe(params, messages => {
|
|
51
52
|
messages.filter(m => m.type === 'request').forEach(callback);
|
|
52
53
|
});
|
|
53
54
|
},
|
|
55
|
+
submitRequest({ ownAccount, peerAccount, secret }, message) {
|
|
56
|
+
const sessionId = createSessionId(peerAccount.publicKey, ownAccount, peerAccount);
|
|
57
|
+
return createStatement(secret, {
|
|
58
|
+
channel: createRequestChannel(sessionId),
|
|
59
|
+
priority: 0,
|
|
60
|
+
topics: [sessionId],
|
|
61
|
+
data: message,
|
|
62
|
+
}).andThen(adapter.submitStatement);
|
|
63
|
+
},
|
|
54
64
|
};
|
|
55
65
|
return transport;
|
|
56
66
|
}
|
package/dist/papp.d.ts
CHANGED
|
@@ -1,26 +1,37 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
4
|
-
import type {
|
|
5
|
-
import type {
|
|
6
|
-
import type { UserSessionsComponent } from './components/user/index.js';
|
|
1
|
+
import type { LazyClient, StatementStoreAdapter } from '@novasamatech/statement-store';
|
|
2
|
+
import type { StorageAdapter } from '@novasamatech/storage-adapter';
|
|
3
|
+
import type { IdentityAdapter, IdentityRepository } from './identity/types.js';
|
|
4
|
+
import type { AuthComponent } from './sso/auth/impl.js';
|
|
5
|
+
import type { SsoSessionManager } from './sso/sessionManager/impl.js';
|
|
7
6
|
export type PappAdapter = {
|
|
8
7
|
sso: AuthComponent;
|
|
9
|
-
sessions:
|
|
10
|
-
identity:
|
|
11
|
-
getIdentity(accountId: string): ResultAsync<Identity | null, Error>;
|
|
12
|
-
getIdentities(accounts: string[]): ResultAsync<Record<string, Identity | null>, Error>;
|
|
13
|
-
};
|
|
8
|
+
sessions: SsoSessionManager;
|
|
9
|
+
identity: IdentityRepository;
|
|
14
10
|
};
|
|
15
11
|
type Adapters = {
|
|
16
|
-
|
|
12
|
+
statementStore: StatementStoreAdapter;
|
|
17
13
|
identities: IdentityAdapter;
|
|
18
14
|
storage: StorageAdapter;
|
|
15
|
+
lazyClient: LazyClient;
|
|
19
16
|
};
|
|
20
17
|
type Params = {
|
|
18
|
+
/**
|
|
19
|
+
* Host app Id.
|
|
20
|
+
* CAUTION! This value should be stable.
|
|
21
|
+
*/
|
|
21
22
|
appId: string;
|
|
23
|
+
/**
|
|
24
|
+
* URL for additional metadata that will be displayed during pairing process.
|
|
25
|
+
* Content of provided json shound be
|
|
26
|
+
* ```ts
|
|
27
|
+
* interface Metadata {
|
|
28
|
+
* name: string;
|
|
29
|
+
* icon: string; // url for icon. Icon should be a rasterized image with min size 256x256 px.
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
22
33
|
metadata: string;
|
|
23
|
-
adapters?: Adapters
|
|
34
|
+
adapters?: Partial<Adapters>;
|
|
24
35
|
};
|
|
25
36
|
export declare function createPappAdapter({ appId, metadata, adapters }: Params): PappAdapter;
|
|
26
37
|
export {};
|
package/dist/papp.js
CHANGED
|
@@ -1,42 +1,23 @@
|
|
|
1
|
+
import { createLazyClient, createPapiStatementStoreAdapter } from '@novasamatech/statement-store';
|
|
2
|
+
import { createLocalStorageAdapter } from '@novasamatech/storage-adapter';
|
|
1
3
|
import { getWsProvider } from '@polkadot-api/ws-provider';
|
|
2
|
-
import { createIdentityRpcAdapter } from './adapters/identity/rpc.js';
|
|
3
|
-
import { createPapiLazyClient } from './adapters/lazyClient/papi.js';
|
|
4
|
-
import { createPapiStatementAdapter } from './adapters/statement/rpc.js';
|
|
5
|
-
import { createLocalStorageAdapter } from './adapters/storage/localStorage.js';
|
|
6
|
-
import { createAuthComponent } from './components/auth/index.js';
|
|
7
|
-
import { createUserSessionsComponent } from './components/user/index.js';
|
|
8
|
-
import { createUserSessionStorage } from './components/user/userSessionStorage.js';
|
|
9
4
|
import { SS_PROD_ENDPOINTS } from './constants.js';
|
|
10
|
-
import {
|
|
5
|
+
import { createIdentityRepository } from './identity/impl.js';
|
|
6
|
+
import { createIdentityRpcAdapter } from './identity/rpcAdapter.js';
|
|
7
|
+
import { createAuth } from './sso/auth/impl.js';
|
|
8
|
+
import { createSsoSessionManager } from './sso/sessionManager/impl.js';
|
|
9
|
+
import { createUserSecretRepository } from './sso/userSecretRepository.js';
|
|
10
|
+
import { createUserSessionRepository } from './sso/userSessionRepository.js';
|
|
11
11
|
export function createPappAdapter({ appId, metadata, adapters }) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
storage = createLocalStorageAdapter(appId);
|
|
23
|
-
identities = createIdentityRpcAdapter(lazyPapiAdapter, storage);
|
|
24
|
-
statements = createPapiStatementAdapter(lazyPapiAdapter);
|
|
25
|
-
}
|
|
26
|
-
const transport = createTransport({ adapter: statements });
|
|
27
|
-
const userSessionStorage = createUserSessionStorage({ storage });
|
|
28
|
-
const identityComponent = {
|
|
29
|
-
getIdentity(accountId) {
|
|
30
|
-
return identities.readIdentities([accountId]).map(map => map[accountId] ?? null);
|
|
31
|
-
},
|
|
32
|
-
getIdentities(accounts) {
|
|
33
|
-
return identities.readIdentities(accounts);
|
|
34
|
-
},
|
|
12
|
+
const lazyClient = adapters?.lazyClient ?? createLazyClient(getWsProvider(SS_PROD_ENDPOINTS));
|
|
13
|
+
const statementStore = adapters?.statementStore ?? createPapiStatementStoreAdapter(lazyClient);
|
|
14
|
+
const identities = adapters?.identities ?? createIdentityRpcAdapter(lazyClient);
|
|
15
|
+
const storage = adapters?.storage ?? createLocalStorageAdapter(appId);
|
|
16
|
+
const ssoSessionRepository = createUserSessionRepository(storage);
|
|
17
|
+
const userSecretRepository = createUserSecretRepository(appId, storage);
|
|
18
|
+
return {
|
|
19
|
+
sso: createAuth({ metadata, statementStore, ssoSessionRepository, userSecretRepository }),
|
|
20
|
+
sessions: createSsoSessionManager({ storage, statementStore, ssoSessionRepository, userSecretRepository }),
|
|
21
|
+
identity: createIdentityRepository({ adapter: identities, storage }),
|
|
35
22
|
};
|
|
36
|
-
const papp = {
|
|
37
|
-
sso: createAuthComponent({ appId, metadata, transport, userSessionStorage }),
|
|
38
|
-
sessions: createUserSessionsComponent({ transport, storage, userSessionStorage }),
|
|
39
|
-
identity: identityComponent,
|
|
40
|
-
};
|
|
41
|
-
return papp;
|
|
42
23
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { StatementStoreAdapter } from '@novasamatech/statement-store';
|
|
2
|
+
import { ResultAsync } from 'neverthrow';
|
|
3
|
+
import type { UserSecretRepository } from '../userSecretRepository.js';
|
|
4
|
+
import type { StoredUserSession, UserSessionRepository } from '../userSessionRepository.js';
|
|
5
|
+
export type AuthComponent = ReturnType<typeof createAuth>;
|
|
6
|
+
type Params = {
|
|
7
|
+
metadata: string;
|
|
8
|
+
statementStore: StatementStoreAdapter;
|
|
9
|
+
ssoSessionRepository: UserSessionRepository;
|
|
10
|
+
userSecretRepository: UserSecretRepository;
|
|
11
|
+
};
|
|
12
|
+
export declare function createAuth({ metadata, statementStore, ssoSessionRepository, userSecretRepository }: Params): {
|
|
13
|
+
status: {
|
|
14
|
+
read: () => {
|
|
15
|
+
step: "none";
|
|
16
|
+
} | {
|
|
17
|
+
step: "initial";
|
|
18
|
+
} | {
|
|
19
|
+
step: "pairing";
|
|
20
|
+
payload: string;
|
|
21
|
+
} | {
|
|
22
|
+
step: "error";
|
|
23
|
+
message: string;
|
|
24
|
+
} | {
|
|
25
|
+
step: "finished";
|
|
26
|
+
session: StoredUserSession;
|
|
27
|
+
};
|
|
28
|
+
subscribe: (fn: (value: {
|
|
29
|
+
step: "none";
|
|
30
|
+
} | {
|
|
31
|
+
step: "initial";
|
|
32
|
+
} | {
|
|
33
|
+
step: "pairing";
|
|
34
|
+
payload: string;
|
|
35
|
+
} | {
|
|
36
|
+
step: "error";
|
|
37
|
+
message: string;
|
|
38
|
+
} | {
|
|
39
|
+
step: "finished";
|
|
40
|
+
session: StoredUserSession;
|
|
41
|
+
}) => void) => () => void;
|
|
42
|
+
onFirstSubscribe: (callback: VoidFunction) => import("nanoevents").Unsubscribe;
|
|
43
|
+
onLastUnsubscribe: (callback: VoidFunction) => import("nanoevents").Unsubscribe;
|
|
44
|
+
};
|
|
45
|
+
authenticate(): ResultAsync<StoredUserSession | null, Error>;
|
|
46
|
+
abortAuthentication(): void;
|
|
47
|
+
};
|
|
48
|
+
export {};
|