@qidcloud/sdk 1.1.1 → 1.2.1

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/src/QidCloud.ts DELETED
@@ -1,171 +0,0 @@
1
- import axios, { AxiosInstance } from 'axios';
2
- import * as PQC from './crypto/pqc';
3
- import UnifiedStorage, { IStorage } from './storage';
4
- import { generateUUID, toBase64, hex2buf, fromBase64 } from './utils';
5
-
6
- export interface QidConfig {
7
- baseUrl?: string; // Optional, defaults to production backend
8
- apiKey: string;
9
- storage?: IStorage;
10
- }
11
-
12
- const DEFAULT_BACKEND_URL = 'https://qgate.onrender.com';
13
-
14
- export interface UserProfile {
15
- userId: string;
16
- username: string;
17
- role?: string;
18
- plan?: string;
19
- accountLimits?: {
20
- maxProjects: number;
21
- maxUsersTotal: number;
22
- };
23
- }
24
-
25
- export class QidCloud {
26
- private api: AxiosInstance;
27
- private storage: IStorage;
28
- private config: QidConfig;
29
-
30
- constructor(config: QidConfig) {
31
- this.config = {
32
- ...config,
33
- baseUrl: config.baseUrl || DEFAULT_BACKEND_URL
34
- };
35
- this.storage = config.storage || UnifiedStorage;
36
- this.api = axios.create({
37
- baseURL: this.config.baseUrl,
38
- headers: {
39
- 'Content-Type': 'application/json',
40
- 'X-API-KEY': config.apiKey
41
- }
42
- });
43
-
44
- // Attach token interceptor
45
- this.api.interceptors.request.use(async (req) => {
46
- const token = await this.storage.getItem('qgate_token');
47
- if (token) {
48
- req.headers.Authorization = `Bearer ${token} `;
49
- }
50
- return req;
51
- });
52
- }
53
-
54
- async hasIdentity(): Promise<boolean> {
55
- const seed = await this.storage.getItem('qgate_pqc_seed');
56
- return !!seed;
57
- }
58
-
59
- async register(username: string, tenantId: string = 'default'): Promise<UserProfile> {
60
- // 1. Generate Seed
61
- const seedHex = await PQC.generateDilithiumSeed();
62
-
63
- // 2. Persist Locally
64
- await this.storage.setItem('qgate_pqc_seed', seedHex);
65
-
66
- // 3. Derive Keys
67
- const keys = await PQC.getKeysFromSeed(seedHex);
68
-
69
- const initResp = await this.api.post('/api/register/initiate', { tenantId });
70
- const { regSessionId, regNonce } = initResp.data;
71
-
72
- // 4. Sign Nonce
73
- // Backend sends nonce as Base64
74
- const nonceBytes = fromBase64(regNonce);
75
- const signature = keys.sign(nonceBytes);
76
- const signatureBase64 = toBase64(signature);
77
-
78
- // 5. Complete Registration
79
- const completeResp = await this.api.post('/api/register/complete', {
80
- regSessionId,
81
- did: generateUUID(), // Device ID
82
- username,
83
- attestation: {
84
- format: 'web-sdk',
85
- signature: signatureBase64,
86
- nonce: regNonce, // Echo back
87
- publicKey: toBase64(keys.publicKey)
88
- }
89
- });
90
-
91
- const { user, token } = completeResp.data;
92
-
93
- // 6. Store Session
94
- if (token) {
95
- await this.storage.setItem('qgate_token', token);
96
- }
97
- if (user && user.userId) {
98
- await this.storage.setItem('qgate_user_id', user.userId);
99
- return user;
100
- }
101
-
102
- return user || { userId: 'unknown', username };
103
- }
104
-
105
- /**
106
- * Login / Authenticate
107
- * Uses stored PQC keys to sign a challenge from the server
108
- */
109
- async login(): Promise<string> {
110
- if (!(await this.hasIdentity())) {
111
- throw new Error('NO_IDENTITY: Device not registered. Call register() first.');
112
- }
113
-
114
- const seedHex = await this.storage.getItem('qgate_pqc_seed');
115
- if (!seedHex) {
116
- throw new Error('NO_IDENTITY_SEED: Seed not found. Re-register.');
117
- }
118
- const keys = await PQC.getKeysFromSeed(seedHex);
119
-
120
- // 1. Initiate Auth Challenge
121
- let userId = await this.storage.getItem('qgate_user_id');
122
-
123
- if (!userId) {
124
- // Try to find user from profile if logged in? No, we are logging in.
125
- throw new Error('MISSING_USER_ID: Cannot look up user. Re-register.');
126
- }
127
-
128
- const initResp = await this.api.post('/api/initiate', {
129
- regUserId: userId,
130
- clientHint: 'web-sdk'
131
- });
132
-
133
- const { sessionId, nonce } = initResp.data;
134
-
135
- // 2. Sign Challenge
136
- // Backend sends nonce as Base64
137
- const nonceBytes = fromBase64(nonce);
138
- const signature = keys.sign(nonceBytes);
139
- const signatureBase64 = toBase64(signature);
140
-
141
- // 3. Verify
142
- const verifyResp = await this.api.post('/api/verify', {
143
- sessionId,
144
- signature: signatureBase64
145
- });
146
-
147
- const { token } = verifyResp.data;
148
- if (token) {
149
- await this.storage.setItem('qgate_token', token);
150
- return token;
151
- }
152
-
153
- throw new Error('Authentication failed');
154
- }
155
-
156
- async logout(): Promise<void> {
157
- try {
158
- // Attempt to notify server
159
- await this.api.post('/api/logout');
160
- } catch (e) {
161
- console.warn('Logout API call failed', e);
162
- }
163
- await this.storage.removeItem('qgate_token');
164
- // Optional: Keep identity keys? Usually yes.
165
- }
166
-
167
- async getProfile(): Promise<UserProfile> {
168
- const resp = await this.api.get('/api/me');
169
- return resp.data;
170
- }
171
- }
package/src/crypto/pqc.ts DELETED
@@ -1,79 +0,0 @@
1
- // PQC Crypto Utility for Q-Gate SDK
2
- import { DilithiumLevel, DilithiumKeyPair } from '@asanrom/dilithium';
3
- import { Kyber1024 } from 'crystals-kyber-js';
4
- import { hex2buf } from '../utils';
5
-
6
- export async function generateKyberKeyPair() {
7
- // Hardcoded sizes for Kyber1024
8
- const PK_SIZE = 1568;
9
- const SK_SIZE = 3168;
10
-
11
- // Assume Kyber1024 is a class we can instantiate or it has static methods.
12
- // Based on library patterns, if static props were missing, maybe it's an instance.
13
- const kyber = new Kyber1024();
14
- // Try async generation first
15
- try {
16
- // @ts-ignore
17
- if (kyber.generateKeyPair) {
18
- // @ts-ignore
19
- const [pk, sk] = await kyber.generateKeyPair();
20
- return { publicKey: pk, privateKey: sk };
21
- }
22
- } catch (e) { }
23
-
24
- // Fallback to buffer passing
25
- const pk = new Uint8Array(PK_SIZE);
26
- const sk = new Uint8Array(SK_SIZE);
27
- // @ts-ignore
28
- kyber.keyPair(pk, sk);
29
-
30
- return {
31
- publicKey: pk,
32
- privateKey: sk
33
- };
34
- }
35
-
36
- export async function decapsulateSecret(privateKey: Uint8Array, cipherText: Uint8Array) {
37
- const kyber = new Kyber1024();
38
- // @ts-ignore
39
- if (kyber.decap) {
40
- // @ts-ignore
41
- return await kyber.decap(cipherText, privateKey);
42
- }
43
-
44
- // Fallback
45
- const SS_SIZE = 32;
46
- const sharedSecret = new Uint8Array(SS_SIZE);
47
- // @ts-ignore
48
- kyber.decaps(sharedSecret, cipherText, privateKey);
49
- return sharedSecret;
50
- }
51
-
52
- export async function generateDilithiumSeed(): Promise<string> {
53
- const seed = new Uint8Array(32);
54
- if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
55
- crypto.getRandomValues(seed);
56
- } else {
57
- for (let i = 0; i < 32; i++) seed[i] = Math.floor(Math.random() * 256);
58
- }
59
- return Array.prototype.map.call(seed, (x: number) => ('00' + x.toString(16)).slice(-2)).join('');
60
- }
61
-
62
- export async function getKeysFromSeed(seedHex: string) {
63
- const level = DilithiumLevel.get(3);
64
- const seed = hex2buf(seedHex);
65
-
66
- const kp = DilithiumKeyPair.generate(level, seed);
67
- return {
68
- publicKey: kp.getPublicKey().getBytes(),
69
- privateKey: kp.getPrivateKey().getBytes(),
70
- // Helper to sign and return Uint8Array
71
- sign: (msg: Uint8Array) => kp.sign(msg).getBytes()
72
- };
73
- }
74
-
75
- export async function signMessage(seedHex: string, message: Uint8Array | string) {
76
- const keys = await getKeysFromSeed(seedHex);
77
- const msgBytes = typeof message === 'string' ? new TextEncoder().encode(message) : message;
78
- return keys.sign(msgBytes);
79
- }
@@ -1,49 +0,0 @@
1
- export interface IStorage {
2
- getItem(key: string): Promise<string | null>;
3
- setItem(key: string, value: string): Promise<void>;
4
- removeItem(key: string): Promise<void>;
5
- }
6
-
7
- export class MemoryStorage implements IStorage {
8
- private storage: Map<string, string> = new Map();
9
-
10
- async getItem(key: string): Promise<string | null> {
11
- return this.storage.get(key) || null;
12
- }
13
-
14
- async setItem(key: string, value: string): Promise<void> {
15
- this.storage.set(key, value);
16
- }
17
-
18
- async removeItem(key: string): Promise<void> {
19
- this.storage.delete(key);
20
- }
21
- }
22
-
23
- export class LocalStorageAdapter implements IStorage {
24
- async getItem(key: string): Promise<string | null> {
25
- if (typeof window !== 'undefined' && window.localStorage) {
26
- return window.localStorage.getItem(key);
27
- }
28
- return null;
29
- }
30
-
31
- async setItem(key: string, value: string): Promise<void> {
32
- if (typeof window !== 'undefined' && window.localStorage) {
33
- window.localStorage.setItem(key, value);
34
- }
35
- }
36
-
37
- async removeItem(key: string): Promise<void> {
38
- if (typeof window !== 'undefined' && window.localStorage) {
39
- window.localStorage.removeItem(key);
40
- }
41
- }
42
- }
43
-
44
- // Default export: Auto-detect
45
- const storage = typeof window !== 'undefined' && window.localStorage
46
- ? new LocalStorageAdapter()
47
- : new MemoryStorage();
48
-
49
- export default storage;
package/src/utils.ts DELETED
@@ -1,48 +0,0 @@
1
- export function generateUUID(): string {
2
- if (typeof crypto !== 'undefined' && crypto.randomUUID) {
3
- return crypto.randomUUID();
4
- }
5
- // Fallback for environments without randomUUID
6
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
7
- var r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
8
- return v.toString(16);
9
- });
10
- }
11
-
12
- export function buf2hex(buffer: Uint8Array): string {
13
- return Array.prototype.map.call(new Uint8Array(buffer), (x: number) => ('00' + x.toString(16)).slice(-2)).join('');
14
- }
15
-
16
- export function hex2buf(hex: string): Uint8Array {
17
- if (!hex) return new Uint8Array(0);
18
- const bytes = new Uint8Array(Math.ceil(hex.length / 2));
19
- for (let i = 0; i < bytes.length; i++) bytes[i] = parseInt(hex.substr(i * 2, 2), 16);
20
- return bytes;
21
- }
22
-
23
- export function toBase64(bytes: Uint8Array): string {
24
- if (typeof Buffer !== 'undefined') {
25
- return Buffer.from(bytes).toString('base64');
26
- }
27
- // Browser fallback
28
- let binary = '';
29
- const len = bytes.byteLength;
30
- for (let i = 0; i < len; i++) {
31
- binary += String.fromCharCode(bytes[i]);
32
- }
33
- return window.btoa(binary);
34
- }
35
-
36
- export function fromBase64(base64: string): Uint8Array {
37
- if (typeof Buffer !== 'undefined') {
38
- return new Uint8Array(Buffer.from(base64, 'base64'));
39
- }
40
- // Browser fallback
41
- const binary_string = window.atob(base64);
42
- const len = binary_string.length;
43
- const bytes = new Uint8Array(len);
44
- for (let i = 0; i < len; i++) {
45
- bytes[i] = binary_string.charCodeAt(i);
46
- }
47
- return bytes;
48
- }
package/verify_sdk.ts DELETED
@@ -1,53 +0,0 @@
1
- import { QGateClient } from './src';
2
- import * as PQC from './src/crypto/pqc';
3
-
4
- async function verify() {
5
- console.log('--- QGate SDK Verification ---');
6
-
7
- // 1. Verify Exports
8
- if (!QGateClient) {
9
- throw new Error('QGateClient export missing');
10
- }
11
- console.log('✅ QGateClient exported');
12
-
13
- // 2. Verify PQC Generation (Dilithium)
14
- console.log('Testing Dilithium Seed Generation...');
15
- const seed = await PQC.generateDilithiumSeed();
16
- console.log('Generated Seed:', seed);
17
- if (!seed || seed.length !== 64) { // 32 bytes hex
18
- throw new Error('Invalid seed generated');
19
- }
20
- console.log('✅ Dilithium Seed Generated');
21
-
22
- // 3. Verify Key Derivation
23
- console.log('Deriving Keys...');
24
- const keys = await PQC.getKeysFromSeed(seed);
25
- if (!keys.publicKey || !keys.privateKey) {
26
- throw new Error('Failed to derive keys');
27
- }
28
- console.log('✅ Keys Derived');
29
-
30
- // 4. Verify Signing
31
- console.log('Testing Signing...');
32
- const msg = "Hello QGate";
33
- const sig = keys.sign(new TextEncoder().encode(msg));
34
- console.log('Signature Length:', sig.length);
35
- if (sig.length === 0) {
36
- throw new Error('Signature failed');
37
- }
38
- console.log('✅ Signing Success'); // Verification requires Dilithium library verify which we didn't export in PQC yet but sign works.
39
-
40
- // 5. Verify Client Instantiation
41
- const client = new QGateClient({
42
- baseUrl: 'http://localhost:3000',
43
- apiKey: 'test-key'
44
- });
45
- console.log('✅ Client Instantiated');
46
-
47
- console.log('--- SDK VERIFIED ---');
48
- }
49
-
50
- verify().catch(err => {
51
- console.error('❌ VERIFICATION FAILED:', err);
52
- process.exit(1);
53
- });