@novasamatech/host-papp 0.5.2 → 0.5.3-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/dist/crypto.d.ts +0 -5
- package/dist/crypto.js +6 -47
- package/dist/sso/auth/attestationService.d.ts +1 -1
- package/dist/sso/auth/attestationService.js +1 -1
- package/dist/sso/auth/impl.js +5 -10
- package/dist/sso/auth/scale/handshake.d.ts +4 -1
- package/dist/sso/auth/scale/handshake.js +3 -3
- package/dist/sso/sessionManager/scale/signPayloadRequest.js +12 -12
- package/dist/sso/sessionManager/userSession.js +18 -38
- package/dist/sso/ssoSessionProver.js +6 -25
- package/package.json +6 -4
package/dist/crypto.d.ts
CHANGED
|
@@ -11,11 +11,6 @@ export declare const SsPubKey: Codec<SsPublicKey>;
|
|
|
11
11
|
export declare const EncrPubKey: Codec<EncrPublicKey>;
|
|
12
12
|
export declare function stringToBytes(str: string): Uint8Array<ArrayBuffer>;
|
|
13
13
|
export declare function bytesToString(bytes: Uint8Array): string;
|
|
14
|
-
export declare function createSsSecret(entropy: Uint8Array): SsSecret;
|
|
15
|
-
export declare function createSsDerivation(secret: SsSecret, derivation: string): SsSecret;
|
|
16
|
-
export declare function getSsPub(secret: SsSecret): SsPublicKey;
|
|
17
|
-
export declare function signWithSsSecret(secret: SsSecret, message: Uint8Array): Uint8Array<ArrayBufferLike>;
|
|
18
|
-
export declare function verifyWithSsSecret(message: Uint8Array, signature: Uint8Array, publicKey: Uint8Array): boolean;
|
|
19
14
|
export type DerivedSr25519Account = {
|
|
20
15
|
secret: SsSecret;
|
|
21
16
|
publicKey: SsPublicKey;
|
package/dist/crypto.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { p256 } from '@noble/curves/nist.js';
|
|
2
|
+
import { createSr25519Secret, deriveSr25519PublicKey, signWithSr25519Secret, verifySr25519Signature, } from '@novasamatech/statement-store';
|
|
2
3
|
import { entropyToMiniSecret, mnemonicToEntropy } from '@polkadot-labs/hdkd-helpers';
|
|
3
|
-
import {
|
|
4
|
-
import { Bytes, str } from 'scale-ts';
|
|
4
|
+
import { Bytes } from 'scale-ts';
|
|
5
5
|
// schemas
|
|
6
6
|
export function BrandedBytesCodec(length) {
|
|
7
7
|
return Bytes(length);
|
|
@@ -17,57 +17,16 @@ export function stringToBytes(str) {
|
|
|
17
17
|
export function bytesToString(bytes) {
|
|
18
18
|
return textDecoder.decode(bytes);
|
|
19
19
|
}
|
|
20
|
-
function parseDerivations(derivationsStr) {
|
|
21
|
-
const DERIVATION_RE = /(\/{1,2})([^/]+)/g;
|
|
22
|
-
const derivations = [];
|
|
23
|
-
for (const [, type, code] of derivationsStr.matchAll(DERIVATION_RE)) {
|
|
24
|
-
if (code) {
|
|
25
|
-
derivations.push([type === '//' ? 'hard' : 'soft', code]);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
return derivations;
|
|
29
|
-
}
|
|
30
|
-
function createChainCode(derivation) {
|
|
31
|
-
const chainCode = new Uint8Array(32);
|
|
32
|
-
chainCode.set(str.enc(derivation));
|
|
33
|
-
return chainCode;
|
|
34
|
-
}
|
|
35
|
-
// statement store key pair
|
|
36
|
-
export function createSsSecret(entropy) {
|
|
37
|
-
const miniSecret = entropyToMiniSecret(entropy);
|
|
38
|
-
return sr25519SecretFromSeed(miniSecret);
|
|
39
|
-
}
|
|
40
|
-
export function createSsDerivation(secret, derivation) {
|
|
41
|
-
const derivations = parseDerivations(derivation);
|
|
42
|
-
return derivations.reduce((secret, [type, derivation]) => {
|
|
43
|
-
const chainCode = createChainCode(derivation);
|
|
44
|
-
switch (type) {
|
|
45
|
-
case 'hard':
|
|
46
|
-
return sr25519HDKD.secretHard(secret, chainCode);
|
|
47
|
-
case 'soft':
|
|
48
|
-
return sr25519HDKD.secretSoft(secret, chainCode);
|
|
49
|
-
}
|
|
50
|
-
}, secret);
|
|
51
|
-
}
|
|
52
|
-
export function getSsPub(secret) {
|
|
53
|
-
return sr25519GetPublicKey(secret);
|
|
54
|
-
}
|
|
55
|
-
export function signWithSsSecret(secret, message) {
|
|
56
|
-
return sr25519Sign(secret, message);
|
|
57
|
-
}
|
|
58
|
-
export function verifyWithSsSecret(message, signature, publicKey) {
|
|
59
|
-
return sr25519Verify(message, signature, publicKey);
|
|
60
|
-
}
|
|
61
20
|
export function deriveSr25519Account(mnemonic, derivation) {
|
|
62
21
|
const entropy = mnemonicToEntropy(mnemonic);
|
|
63
|
-
const secret =
|
|
64
|
-
const publicKey =
|
|
22
|
+
const secret = createSr25519Secret(entropy, derivation);
|
|
23
|
+
const publicKey = deriveSr25519PublicKey(secret);
|
|
65
24
|
return {
|
|
66
25
|
secret,
|
|
67
26
|
publicKey,
|
|
68
27
|
entropy,
|
|
69
|
-
sign: message =>
|
|
70
|
-
verify: (message, signature) =>
|
|
28
|
+
sign: message => signWithSr25519Secret(secret, message),
|
|
29
|
+
verify: (message, signature) => verifySr25519Signature(message, signature, publicKey),
|
|
71
30
|
};
|
|
72
31
|
}
|
|
73
32
|
// encryption key pair
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { LazyClient } from '@novasamatech/statement-store';
|
|
2
2
|
import type { ResultAsync } from 'neverthrow';
|
|
3
3
|
import type { DerivedSr25519Account } from '../../crypto.js';
|
|
4
|
-
export declare function
|
|
4
|
+
export declare function createSudoAliceVerifier(): DerivedSr25519Account;
|
|
5
5
|
export declare const createAttestationService: (lazyClient: LazyClient) => {
|
|
6
6
|
claimUsername(): string;
|
|
7
7
|
grantVerifierAllowance(verifier: DerivedSr25519Account): ResultAsync<void, Error>;
|
|
@@ -10,7 +10,7 @@ import { member_from_entropy, sign } from 'verifiablejs/bundler';
|
|
|
10
10
|
import { deriveSr25519Account, getEncrPub, stringToBytes } from '../../crypto.js';
|
|
11
11
|
import { toError } from '../../helpers/utils.js';
|
|
12
12
|
const accountId = AccountId();
|
|
13
|
-
export function
|
|
13
|
+
export function createSudoAliceVerifier() {
|
|
14
14
|
return deriveSr25519Account('bottom drive obey lake curtain smoke basket hold race lonely fit walk', '//Alice');
|
|
15
15
|
}
|
|
16
16
|
export const createAttestationService = (lazyClient) => {
|
package/dist/sso/auth/impl.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { enumValue } from '@novasamatech/scale';
|
|
1
2
|
import { createAccountId, createEncryption, createLocalSessionAccount, createRemoteSessionAccount, khash, } from '@novasamatech/statement-store';
|
|
2
3
|
import { mergeUint8, toHex } from '@polkadot-api/utils';
|
|
3
4
|
import { generateMnemonic } from '@polkadot-labs/hdkd-helpers';
|
|
@@ -7,7 +8,7 @@ import { AbortError } from '../../helpers/abortError.js';
|
|
|
7
8
|
import { createState, readonly } from '../../helpers/state.js';
|
|
8
9
|
import { toError } from '../../helpers/utils.js';
|
|
9
10
|
import { createStoredUserSession } from '../userSessionRepository.js';
|
|
10
|
-
import {
|
|
11
|
+
import { createAttestationService, createSudoAliceVerifier } from './attestationService.js';
|
|
11
12
|
import { HandshakeData, HandshakeResponsePayload, HandshakeResponseSensitiveData } from './scale/handshake.js';
|
|
12
13
|
export function createAuth({ metadata, statementStore, ssoSessionRepository, userSecretRepository, lazyClient, }) {
|
|
13
14
|
const attestationStatus = createState({ step: 'none' });
|
|
@@ -16,7 +17,7 @@ export function createAuth({ metadata, statementStore, ssoSessionRepository, use
|
|
|
16
17
|
let abort = null;
|
|
17
18
|
function attestAccount(account, signal) {
|
|
18
19
|
const attestationService = createAttestationService(lazyClient);
|
|
19
|
-
const verifier =
|
|
20
|
+
const verifier = createSudoAliceVerifier();
|
|
20
21
|
const username = attestationService.claimUsername();
|
|
21
22
|
attestationStatus.write({ step: 'attestation', username });
|
|
22
23
|
return attestationService
|
|
@@ -115,18 +116,12 @@ export function createAuth({ metadata, statementStore, ssoSessionRepository, use
|
|
|
115
116
|
return authModule;
|
|
116
117
|
}
|
|
117
118
|
const createHandshakeTopic = fromThrowable((account, encrPublicKey) => khash(account.accountId, mergeUint8([encrPublicKey, stringToBytes('topic')])), toError);
|
|
118
|
-
const createHandshakePayloadV1 = fromThrowable(({ encrPublicKey, ssPublicKey, metadata, }) => HandshakeData.enc(
|
|
119
|
-
tag: 'v1',
|
|
120
|
-
value: [ssPublicKey, encrPublicKey, metadata],
|
|
121
|
-
}), toError);
|
|
119
|
+
const createHandshakePayloadV1 = fromThrowable(({ encrPublicKey, ssPublicKey, metadata, }) => HandshakeData.enc(enumValue('v1', [ssPublicKey, encrPublicKey, metadata])), toError);
|
|
122
120
|
function parseHandshakePayload(payload) {
|
|
123
121
|
const decoded = HandshakeResponsePayload.dec(payload);
|
|
124
122
|
switch (decoded.tag) {
|
|
125
123
|
case 'v1':
|
|
126
|
-
return
|
|
127
|
-
encrypted: decoded.value[0],
|
|
128
|
-
tmpKey: decoded.value[1],
|
|
129
|
-
};
|
|
124
|
+
return decoded.value;
|
|
130
125
|
default:
|
|
131
126
|
throw new Error('Unsupported handshake payload version');
|
|
132
127
|
}
|
|
@@ -4,6 +4,9 @@ export declare const HandshakeData: import("scale-ts").Codec<{
|
|
|
4
4
|
}>;
|
|
5
5
|
export declare const HandshakeResponsePayload: import("scale-ts").Codec<{
|
|
6
6
|
tag: "v1";
|
|
7
|
-
value:
|
|
7
|
+
value: {
|
|
8
|
+
encrypted: Uint8Array<ArrayBufferLike>;
|
|
9
|
+
tmpKey: Uint8Array<ArrayBufferLike>;
|
|
10
|
+
};
|
|
8
11
|
}>;
|
|
9
12
|
export declare const HandshakeResponseSensitiveData: import("scale-ts").Codec<[Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>]>;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Enum } from '@novasamatech/scale';
|
|
2
|
+
import { Bytes, Struct, Tuple, str } from 'scale-ts';
|
|
2
3
|
import { EncrPubKey, SsPubKey } from '../../../crypto.js';
|
|
3
4
|
export const HandshakeData = Enum({
|
|
4
5
|
v1: Tuple(SsPubKey, EncrPubKey, str),
|
|
5
6
|
});
|
|
6
7
|
export const HandshakeResponsePayload = Enum({
|
|
7
|
-
|
|
8
|
-
v1: Tuple(Bytes(), Bytes(65)),
|
|
8
|
+
v1: Struct({ encrypted: Bytes(), tmpKey: Bytes(65) }),
|
|
9
9
|
});
|
|
10
10
|
export const HandshakeResponseSensitiveData = Tuple(Bytes(65), Bytes(32));
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
+
import { Hex } from '@novasamatech/scale';
|
|
1
2
|
import { Option, Struct, Vector, bool, str, u32 } from 'scale-ts';
|
|
2
|
-
import { hexCodec } from './hex.js';
|
|
3
3
|
export const SignPayloadRequestCodec = Struct({
|
|
4
4
|
address: str,
|
|
5
|
-
blockHash:
|
|
6
|
-
blockNumber:
|
|
7
|
-
era:
|
|
8
|
-
genesisHash:
|
|
9
|
-
method:
|
|
10
|
-
nonce:
|
|
11
|
-
specVersion:
|
|
12
|
-
tip:
|
|
13
|
-
transactionVersion:
|
|
5
|
+
blockHash: Hex(),
|
|
6
|
+
blockNumber: Hex(),
|
|
7
|
+
era: Hex(),
|
|
8
|
+
genesisHash: Hex(),
|
|
9
|
+
method: Hex(),
|
|
10
|
+
nonce: Hex(),
|
|
11
|
+
specVersion: Hex(),
|
|
12
|
+
tip: Hex(),
|
|
13
|
+
transactionVersion: Hex(),
|
|
14
14
|
signedExtensions: Vector(str),
|
|
15
15
|
version: u32,
|
|
16
|
-
assetId: Option(
|
|
17
|
-
metadataHash: Option(
|
|
16
|
+
assetId: Option(Hex()),
|
|
17
|
+
metadataHash: Option(Hex()),
|
|
18
18
|
mode: Option(u32),
|
|
19
19
|
withSignedTransaction: Option(bool),
|
|
20
20
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { enumValue } from '@novasamatech/scale';
|
|
1
2
|
import { createSession } from '@novasamatech/statement-store';
|
|
2
3
|
import { fieldListView } from '@novasamatech/storage-adapter';
|
|
3
4
|
import { AccountId } from '@polkadot-api/substrate-bindings';
|
|
@@ -31,39 +32,23 @@ export function createUserSession({ userSession, statementStore, encryption, sto
|
|
|
31
32
|
const messageId = nanoid();
|
|
32
33
|
const request = session.request(RemoteMessageCodec, {
|
|
33
34
|
messageId,
|
|
34
|
-
data:
|
|
35
|
-
tag: 'v1',
|
|
36
|
-
value: {
|
|
37
|
-
tag: 'SignRequest',
|
|
38
|
-
value: payload,
|
|
39
|
-
},
|
|
40
|
-
},
|
|
35
|
+
data: enumValue('v1', enumValue('SignRequest', payload)),
|
|
41
36
|
});
|
|
42
37
|
const responseFilter = (message) => {
|
|
43
|
-
|
|
38
|
+
if (message.data.tag === 'v1' &&
|
|
44
39
|
message.data.value.tag === 'SignResponse' &&
|
|
45
|
-
message.data.value.value.respondingTo === messageId)
|
|
40
|
+
message.data.value.value.respondingTo === messageId) {
|
|
41
|
+
return message.data.value.value.payload;
|
|
42
|
+
}
|
|
46
43
|
};
|
|
47
44
|
return request
|
|
48
45
|
.andThen(() => session.waitForRequestMessage(RemoteMessageCodec, responseFilter))
|
|
49
46
|
.andThen(message => {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (data.value.value.payload.success) {
|
|
56
|
-
return ok(data.value.value.payload.value);
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
return err(new Error(data.value.value.payload.value));
|
|
60
|
-
}
|
|
61
|
-
default:
|
|
62
|
-
return err(new Error(`Incorrect sign response: ${data.value.tag}`));
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
default:
|
|
66
|
-
return err(new Error(`Unsupported message version ${data.tag}`));
|
|
47
|
+
if (message.success) {
|
|
48
|
+
return ok(message.value);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
return err(new Error(message.value));
|
|
67
52
|
}
|
|
68
53
|
});
|
|
69
54
|
},
|
|
@@ -71,13 +56,7 @@ export function createUserSession({ userSession, statementStore, encryption, sto
|
|
|
71
56
|
return session
|
|
72
57
|
.submitRequestMessage(RemoteMessageCodec, {
|
|
73
58
|
messageId: nanoid(),
|
|
74
|
-
data:
|
|
75
|
-
tag: 'v1',
|
|
76
|
-
value: {
|
|
77
|
-
tag: 'Disconnected',
|
|
78
|
-
value: undefined,
|
|
79
|
-
},
|
|
80
|
-
},
|
|
59
|
+
data: enumValue('v1', enumValue('Disconnected', undefined)),
|
|
81
60
|
})
|
|
82
61
|
.map(() => undefined);
|
|
83
62
|
},
|
|
@@ -85,17 +64,18 @@ export function createUserSession({ userSession, statementStore, encryption, sto
|
|
|
85
64
|
return session.subscribe(RemoteMessageCodec, messages => {
|
|
86
65
|
processedMessages.read().andThen(processed => {
|
|
87
66
|
const results = messages.map(message => {
|
|
88
|
-
if (message.type === 'request') {
|
|
89
|
-
const
|
|
67
|
+
if (message.type === 'request' && message.payload.status === 'parsed') {
|
|
68
|
+
const payload = message.payload;
|
|
69
|
+
const isMessageProcessed = processed.includes(payload.value.messageId);
|
|
90
70
|
if (isMessageProcessed) {
|
|
91
71
|
return okAsync({ processed: false });
|
|
92
72
|
}
|
|
93
|
-
return callback(
|
|
73
|
+
return callback(payload.value)
|
|
94
74
|
.orTee(error => {
|
|
95
|
-
console.error('Error while processing sso
|
|
75
|
+
console.error('Error while processing sso message:', error);
|
|
96
76
|
})
|
|
97
77
|
.orElse(() => okAsync(false))
|
|
98
|
-
.map(processed => (processed ? { processed, message:
|
|
78
|
+
.map(processed => (processed ? { processed, message: payload.value } : { processed }));
|
|
99
79
|
}
|
|
100
80
|
return okAsync({ processed: false });
|
|
101
81
|
});
|
|
@@ -1,35 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { err,
|
|
3
|
-
import { compact } from 'scale-ts';
|
|
4
|
-
import { getSsPub, signWithSsSecret, verifyWithSsSecret } from '../crypto.js';
|
|
5
|
-
import { toError } from '../helpers/utils.js';
|
|
6
|
-
const verify = fromThrowable(verifyWithSsSecret, toError);
|
|
1
|
+
import { createSr25519Prover } from '@novasamatech/statement-store';
|
|
2
|
+
import { err, ok } from 'neverthrow';
|
|
7
3
|
export function createSsoStatementProver(userSession, userSecretRepository) {
|
|
8
|
-
const
|
|
4
|
+
const prover = userSecretRepository
|
|
9
5
|
.read(userSession.id)
|
|
10
6
|
.andThen(secrets => (secrets ? ok(secrets) : err(new Error(`Secrets for session ${userSession.id} not found.`))))
|
|
11
|
-
.map(x => x.ssSecret);
|
|
7
|
+
.map(x => createSr25519Prover(x.ssSecret));
|
|
12
8
|
return {
|
|
13
9
|
generateMessageProof(statement) {
|
|
14
|
-
return
|
|
15
|
-
const signer = getStatementSigner(getSsPub(secret), 'sr25519', data => signWithSsSecret(secret, data));
|
|
16
|
-
return signer.sign(statement);
|
|
17
|
-
});
|
|
10
|
+
return prover.andThen(prover => prover.generateMessageProof(statement));
|
|
18
11
|
},
|
|
19
12
|
verifyMessageProof(statement) {
|
|
20
|
-
|
|
21
|
-
if (!proof) {
|
|
22
|
-
// TODO should we pass check when proof is not presented?
|
|
23
|
-
return okAsync(true);
|
|
24
|
-
}
|
|
25
|
-
const encoded = statementCodec.enc(unsigned);
|
|
26
|
-
const compactLen = compact.enc(compact.dec(encoded)).length;
|
|
27
|
-
switch (proof.type) {
|
|
28
|
-
case 'sr25519':
|
|
29
|
-
return verify(encoded.slice(compactLen), proof.value.signature.asBytes(), proof.value.signer.asBytes()).asyncAndThen(x => okAsync(x));
|
|
30
|
-
default:
|
|
31
|
-
return errAsync(new Error(`Proof type ${proof.type} is not supported.`));
|
|
32
|
-
}
|
|
13
|
+
return prover.andThen(prover => prover.verifyMessageProof(statement));
|
|
33
14
|
},
|
|
34
15
|
};
|
|
35
16
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@novasamatech/host-papp",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.5.
|
|
4
|
+
"version": "0.5.3-0",
|
|
5
5
|
"description": "Polkadot app integration",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"repository": {
|
|
@@ -28,9 +28,11 @@
|
|
|
28
28
|
"@noble/ciphers": "2.1.1",
|
|
29
29
|
"@noble/curves": "2.0.1",
|
|
30
30
|
"@noble/hashes": "2.0.1",
|
|
31
|
-
"@novasamatech/host-api": "0.5.
|
|
32
|
-
"@novasamatech/
|
|
33
|
-
"@novasamatech/
|
|
31
|
+
"@novasamatech/host-api": "0.5.3-0",
|
|
32
|
+
"@novasamatech/scale": "0.5.3-0",
|
|
33
|
+
"@novasamatech/statement-store": "0.5.3-0",
|
|
34
|
+
"@novasamatech/storage-adapter": "0.5.3-0",
|
|
35
|
+
"@polkadot-api/utils": "0.2.0",
|
|
34
36
|
"@polkadot-api/substrate-bindings": "^0.16.5",
|
|
35
37
|
"@polkadot-labs/hdkd-helpers": "^0.0.27",
|
|
36
38
|
"@scure/sr25519": "1.0.0",
|