Création d'un composable pour la gestion de la crypto, mise en place de l'initialisation de la première liste et sauvegarde de celle-ci. TODO NEXT : Récupération de la liste en la déchiffrant, mise en place d'une masterKey dérivée du mot de passe pour les utilisateurs non google, basculement vers la page des listes après connexion google etc.

This commit is contained in:
2026-03-15 08:15:42 +01:00
parent d9d84634e8
commit 5d582461e2
10 changed files with 562 additions and 29 deletions

View File

@@ -1,11 +1,18 @@
import { defineStore } from 'pinia'
import type { List } from '~/types/lists'
import type { List, WPListEncrypted } from '~/types/lists'
import {useCryptoStore} from '~/composables/crypto'
import sodium from "libsodium-wrappers"
const crypto = useCryptoStore()
export const useListStore = defineStore('lists', {
state: () => ({
lists: [] as List[],
loading: false as boolean,
masterKey: null as Uint8Array | null,
publicKey: new Uint8Array() as Uint8Array,
privateKey: new Uint8Array() as Uint8Array,
}),
actions: {
@@ -15,8 +22,11 @@ export const useListStore = defineStore('lists', {
resetLists(){
this.lists = []
},
setMasterKey(key: Uint8Array){
this.masterKey = key
},
async fetchLists() {
async fetchLists() {
// On récupère notre plugin API injecté
const { $api } = useNuxtApp();
this.loading = true;
@@ -33,7 +43,107 @@ export const useListStore = defineStore('lists', {
} finally {
this.loading = false;
}
}
},
async processRawData(rawData: [WPListEncrypted]){
const { $api } = useNuxtApp();
await crypto.init()
if (rawData && rawData.length > 0) {
const item = rawData[0];
if (item && this.masterKey){
if (this.isWPListEncrypted(item)) {
if (item.content_cipher === "initialize-me"){
/* 1- Création de la paire de clés (TODO : à déporter ailleurs (Key Store ?)) */
const { publicKey, privateKey } = crypto.generateUserKeyPair()
this.publicKey = publicKey;
this.privateKey = privateKey;
// On les envoie au BO //
/* Chiffrage de la private avec la master : */
const privKeyCipher = crypto.encryptPrivateKey(this.privateKey, this.masterKey)
/* empaquetage */
const payload = {
'user_id': item.user_id, // pour vérification au BO
'private_key_cipher': privKeyCipher,
'public_key': publicKey,
}
/* Et on envoie ! */
await $api.lists.uploadKeys(JSON.stringify(payload))
// On crée la liste //
/* D'abord la clé AES de la liste */
const key = crypto.generateListKey();
/* Puis la liste */
const user_id = Number(item.user_id)
const data: List = {
id: Number(item.id),
user_id: user_id,
aesKey: key,
list_title: 'THE VERY première liste',
list_type: 'basic',
content: '{[\'vide\']}',
is_open: true,
created_at: Date.now(),
updated_at: Date.now(),
}
this.lists[0] = data
/* Et on envoie au BO */
this.syncData(data, this.publicKey, this.privateKey)
}
else{
console.log('coucou : ' + rawData);
}
}
}
}
},
async syncData(decryptedData: List, userPublicKey: Uint8Array, userPrivateKey: Uint8Array) {
const { $api } = useNuxtApp();
const aesKey = decryptedData.aesKey;
await crypto.init();
// 1. On prépare les données (en excluant la clé elle-même du contenu)
const { aesKey: _, ...pureData } = decryptedData;
const stringData = JSON.stringify(pureData);
// 2. Chiffrement du contenu par la clé AES (XChaCha20)
const encryptedContent = crypto.encryptList(stringData, aesKey);
// 3. Chiffrement de la clé AES par la clé Publique (Asymétrique)
// On utilise ici ta fonction de l'étape 11
const encryptedKeyPayload = crypto.encryptListKeyForUser(
aesKey,
userPublicKey,
userPrivateKey
);
// 4. On empaquette le tout pour le Back-Office
const dataToBO: WPListEncrypted = {
id: decryptedData.id.toString(),
user_id: decryptedData.user_id.toString(),
// La clé AES verrouillée pour l'utilisateur
key_cipher: sodium.to_base64(encryptedKeyPayload.cipher),
key_nonce: sodium.to_base64(encryptedKeyPayload.nonce),
// Le contenu de la liste
content_cipher: sodium.to_base64(encryptedContent.cipher),
content_nonce: sodium.to_base64(encryptedContent.nonce),
created_at: null,
updated_at: null,
};
await $api.lists.update(dataToBO)
},
// async updateList(id, title, content) {
// const config = useRuntimeConfig();
@@ -57,6 +167,26 @@ export const useListStore = defineStore('lists', {
// console.error("Erreur lors de la récupération des listes:", error);
// }
// }
isWPListEncrypted(obj: any): obj is WPListEncrypted {
console.log(obj !== null)
console.log(typeof obj)
console.log(typeof obj.id)
console.log(typeof obj.key_cipher )
console.log(typeof obj.key_nonce )
console.log(typeof obj.content_cipher)
console.log(typeof obj.content_nonce)
return (
obj !== null &&
typeof obj === 'object' &&
typeof obj.id === 'string' &&
typeof obj.key_cipher === 'string' &&
typeof obj.key_nonce === 'string' &&
typeof obj.content_cipher === 'string' &&
typeof obj.content_nonce === 'string'
);
}
}
})