Compare commits
1 Commits
main
...
cryptograp
| Author | SHA1 | Date | |
|---|---|---|---|
| 5d582461e2 |
177
app/components/button/google-code.vue
Normal file
177
app/components/button/google-code.vue
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
useTokenClient,
|
||||||
|
type TokenClientSuccessResponse,
|
||||||
|
type TokenClientErrorResponse
|
||||||
|
} from "vue3-google-signin";
|
||||||
|
|
||||||
|
const authStore = useAuthStore();
|
||||||
|
const listStore = useListStore();
|
||||||
|
const crypto = useCryptoStore()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logique Google Drive (appDataFolder)
|
||||||
|
*/
|
||||||
|
const handleDriveKey = async (accessToken: string) => {
|
||||||
|
const fileName = 'list_master_key';
|
||||||
|
const baseUrl = 'https://www.googleapis.com/drive/v3/files';
|
||||||
|
const authHeader = { Authorization: `Bearer ${accessToken}` };
|
||||||
|
|
||||||
|
try {
|
||||||
|
const searchParams = new URLSearchParams({
|
||||||
|
q: `name = '${fileName}'`,
|
||||||
|
spaces: 'appDataFolder',
|
||||||
|
fields: 'files(id, name)'
|
||||||
|
});
|
||||||
|
|
||||||
|
const searchRes = await $fetch<any>(`${baseUrl}?${searchParams}`, { headers: authHeader });
|
||||||
|
|
||||||
|
if (searchRes.files && searchRes.files.length > 0) {
|
||||||
|
const fileId = searchRes.files[0].id;
|
||||||
|
|
||||||
|
// 1. Récupérer le contenu en tant que Blob ou ArrayBuffer pour préserver les bytes
|
||||||
|
const blob = await $fetch<Blob>(`${baseUrl}/${fileId}?alt=media`, {
|
||||||
|
headers: authHeader,
|
||||||
|
responseType: 'blob'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Convertir le Blob en Uint8Array
|
||||||
|
const arrayBuffer = await blob.arrayBuffer();
|
||||||
|
const keyUint8 = new Uint8Array(arrayBuffer);
|
||||||
|
|
||||||
|
listStore.setMasterKey(keyUint8);
|
||||||
|
console.log("Clé binaire récupérée du Drive : " + keyUint8);
|
||||||
|
} else {
|
||||||
|
// 2. Création avec le type Uint8Array
|
||||||
|
const newKey = crypto.generateMasterKey();
|
||||||
|
|
||||||
|
const metadata = {
|
||||||
|
name: fileName,
|
||||||
|
parents: ['appDataFolder']
|
||||||
|
};
|
||||||
|
|
||||||
|
const formData = new FormData();
|
||||||
|
// On spécifie explicitement 'application/octet-stream' pour la clé binaire
|
||||||
|
formData.append('metadata', new Blob([JSON.stringify(metadata)], { type: 'application/json' }));
|
||||||
|
formData.append('file', new Blob([newKey as any], { type: 'application/octet-stream' }));
|
||||||
|
|
||||||
|
await $fetch('https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: authHeader,
|
||||||
|
body: formData
|
||||||
|
});
|
||||||
|
|
||||||
|
listStore.setMasterKey(newKey);
|
||||||
|
console.log("Nouvelle clé binaire générée et stockée : " + newKey);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Erreur Google Drive:", err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Succès de l'authentification Google
|
||||||
|
*/
|
||||||
|
const handleSuccess = async (response: TokenClientSuccessResponse) => {
|
||||||
|
const accessToken = response.access_token;
|
||||||
|
console.log(response.access_token)
|
||||||
|
|
||||||
|
try {
|
||||||
|
// // A. Vérification côté WordPress (Login)
|
||||||
|
// // Note : On envoie l'accessToken ou on utilise l'ID Token si configuré.
|
||||||
|
// // Si ton API WP attendait un 'code', il faudra l'adapter pour vérifier l'access_token ou l'email.
|
||||||
|
const data = await $fetch<any>('/api-wp/auth/google', {
|
||||||
|
method: 'POST',
|
||||||
|
body: { access_token: accessToken }
|
||||||
|
});
|
||||||
|
|
||||||
|
// Stockage des infos WP
|
||||||
|
authStore.setTokenCookie(data.token);
|
||||||
|
authStore.setUserCookie(data.user);
|
||||||
|
|
||||||
|
// B. Gestion de la clé sur Google Drive (Entièrement géré par le Front)
|
||||||
|
await handleDriveKey(accessToken);
|
||||||
|
listStore.processRawData(data.lists);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Erreur login WordPress:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleError = (error: TokenClientErrorResponse) => {
|
||||||
|
console.error("Erreur Google:", error);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialisation du Token Client (Implicit Flow - Pas de secret nécessaire)
|
||||||
|
const { login } = useTokenClient({
|
||||||
|
scope: 'openid email profile https://www.googleapis.com/auth/drive.appdata',
|
||||||
|
onSuccess: handleSuccess,
|
||||||
|
onError: handleError,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<button @click="() => login()" class="g-signin-button">
|
||||||
|
<div class="g-logo-wrapper">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" class="g-logo">
|
||||||
|
<path fill="#EA4335" d="M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z"/>
|
||||||
|
<path fill="#4285F4" d="M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z"/>
|
||||||
|
<path fill="#FBBC05" d="M10.53 28.59c-.48-1.45-.76-2.99-.76-4.59s.27-3.14.76-4.59l-7.98-6.19C.92 16.46 0 20.12 0 24c0 3.88.92 7.54 2.56 10.78l7.97-6.19z"/>
|
||||||
|
<path fill="#34A853" d="M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z"/>
|
||||||
|
<path fill="none" d="M0 0h48v48H0z"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<span class="g-button-text">{{ $t('googleBtnTxt') }}</span>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.g-signin-button {
|
||||||
|
margin:auto;
|
||||||
|
margin-top:1em;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
background-color: #ffffff;
|
||||||
|
color: #757575;
|
||||||
|
height: 40px;
|
||||||
|
width: auto;
|
||||||
|
border: 1px solid #dadce0;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 0em;
|
||||||
|
font-family: 'Roboto', arial, sans-serif;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
box-shadow: 0 1px 2px 0 rgba(60,64,67,0.302), 0 1px 3px 1px rgba(60,64,67,0.149);
|
||||||
|
transition: background-color .218s, border-color .218s, box-shadow .218s;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.g-signin-button:hover {
|
||||||
|
background-color: hsl(210, 17%, 90%);
|
||||||
|
border-color: #d2d2d2;
|
||||||
|
box-shadow: 0 1px 2px 0 rgba(60,64,67,0.302), 0 1px 3px 1px rgba(60,64,67,0.149);
|
||||||
|
}
|
||||||
|
|
||||||
|
.g-signin-button:active {
|
||||||
|
background-color: #eeeeee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.g-logo-wrapper {
|
||||||
|
padding: 11px;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.g-logo {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.g-button-text {
|
||||||
|
padding: 0 12px 0 10px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
authStore.setTokenCookie(data.token);
|
authStore.setTokenCookie(data.token);
|
||||||
authStore.setUserCookie(data.user);
|
authStore.setUserCookie(data.user);
|
||||||
listStore.lists = data.lists
|
listStore.lists = data.lists
|
||||||
console.log(listStore.lists) //ok
|
//console.log(listStore.lists) //ok
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Erreur côté WordPress:", error);
|
console.error("Erreur côté WordPress:", error);
|
||||||
}
|
}
|
||||||
|
|||||||
199
app/composables/crypto.ts
Normal file
199
app/composables/crypto.ts
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
import sodium from "libsodium-wrappers"
|
||||||
|
|
||||||
|
export type KeyPair = {
|
||||||
|
publicKey: Uint8Array
|
||||||
|
privateKey: Uint8Array
|
||||||
|
}
|
||||||
|
|
||||||
|
export type EncryptedPayload = {
|
||||||
|
nonce: Uint8Array
|
||||||
|
cipher: Uint8Array
|
||||||
|
}
|
||||||
|
|
||||||
|
export type EncryptedPrivateKey = {
|
||||||
|
nonce: Uint8Array
|
||||||
|
cipher: Uint8Array
|
||||||
|
salt: Uint8Array
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useCryptoStore = () => {
|
||||||
|
|
||||||
|
let ready = false
|
||||||
|
|
||||||
|
const init = async (): Promise<void> => {
|
||||||
|
if (!ready) {
|
||||||
|
await sodium.ready
|
||||||
|
ready = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* USER KEY PAIR
|
||||||
|
*/
|
||||||
|
const generateUserKeyPair = (): KeyPair => {
|
||||||
|
|
||||||
|
const pair = sodium.crypto_box_keypair()
|
||||||
|
|
||||||
|
return {
|
||||||
|
publicKey: pair.publicKey,
|
||||||
|
privateKey: pair.privateKey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MASTER KEY
|
||||||
|
*/
|
||||||
|
const generateMasterKey = (): Uint8Array => {
|
||||||
|
return sodium.randombytes_buf(
|
||||||
|
sodium.crypto_secretbox_KEYBYTES
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ENCRYPT PRIVATE KEY
|
||||||
|
*/
|
||||||
|
const encryptPrivateKey = (
|
||||||
|
privateKey: Uint8Array,
|
||||||
|
masterKey: Uint8Array
|
||||||
|
): EncryptedPayload => {
|
||||||
|
|
||||||
|
const nonce = sodium.randombytes_buf(
|
||||||
|
sodium.crypto_secretbox_NONCEBYTES
|
||||||
|
)
|
||||||
|
|
||||||
|
const cipher = sodium.crypto_secretbox_easy(
|
||||||
|
privateKey,
|
||||||
|
nonce,
|
||||||
|
masterKey
|
||||||
|
)
|
||||||
|
|
||||||
|
return { nonce, cipher }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DECRYPT PRIVATE KEY
|
||||||
|
*/
|
||||||
|
const decryptPrivateKey = (
|
||||||
|
encrypted: EncryptedPayload,
|
||||||
|
masterKey: Uint8Array
|
||||||
|
): Uint8Array => {
|
||||||
|
|
||||||
|
return sodium.crypto_secretbox_open_easy(
|
||||||
|
encrypted.cipher,
|
||||||
|
encrypted.nonce,
|
||||||
|
masterKey
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LIST KEY
|
||||||
|
*/
|
||||||
|
const generateListKey = (): Uint8Array => {
|
||||||
|
|
||||||
|
return sodium.randombytes_buf(
|
||||||
|
sodium.crypto_aead_xchacha20poly1305_ietf_KEYBYTES
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ENCRYPT LIST DATA
|
||||||
|
*/
|
||||||
|
const encryptList = (
|
||||||
|
data: string,
|
||||||
|
listKey: Uint8Array
|
||||||
|
): EncryptedPayload => {
|
||||||
|
|
||||||
|
const nonce = sodium.randombytes_buf(
|
||||||
|
sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES
|
||||||
|
)
|
||||||
|
|
||||||
|
const cipher = sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(
|
||||||
|
sodium.from_string(data),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
nonce,
|
||||||
|
listKey
|
||||||
|
)
|
||||||
|
|
||||||
|
return { nonce, cipher }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DECRYPT LIST DATA
|
||||||
|
*/
|
||||||
|
const decryptList = (
|
||||||
|
encrypted: EncryptedPayload,
|
||||||
|
listKey: Uint8Array
|
||||||
|
): string => {
|
||||||
|
|
||||||
|
const decrypted = sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(
|
||||||
|
null,
|
||||||
|
encrypted.cipher,
|
||||||
|
null,
|
||||||
|
encrypted.nonce,
|
||||||
|
listKey
|
||||||
|
)
|
||||||
|
|
||||||
|
return sodium.to_string(decrypted)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ENCRYPT LIST KEY FOR USER
|
||||||
|
*/
|
||||||
|
const encryptListKeyForUser = (
|
||||||
|
listKey: Uint8Array,
|
||||||
|
recipientPublicKey: Uint8Array,
|
||||||
|
senderPrivateKey: Uint8Array
|
||||||
|
): EncryptedPayload => {
|
||||||
|
|
||||||
|
const nonce = sodium.randombytes_buf(
|
||||||
|
sodium.crypto_box_NONCEBYTES
|
||||||
|
)
|
||||||
|
|
||||||
|
const cipher = sodium.crypto_box_easy(
|
||||||
|
listKey,
|
||||||
|
nonce,
|
||||||
|
recipientPublicKey,
|
||||||
|
senderPrivateKey
|
||||||
|
)
|
||||||
|
|
||||||
|
return { nonce, cipher }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DECRYPT LIST KEY
|
||||||
|
*/
|
||||||
|
const decryptListKey = (
|
||||||
|
encrypted: EncryptedPayload,
|
||||||
|
senderPublicKey: Uint8Array,
|
||||||
|
recipientPrivateKey: Uint8Array
|
||||||
|
): Uint8Array => {
|
||||||
|
|
||||||
|
return sodium.crypto_box_open_easy(
|
||||||
|
encrypted.cipher,
|
||||||
|
encrypted.nonce,
|
||||||
|
senderPublicKey,
|
||||||
|
recipientPrivateKey
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
|
||||||
|
init,
|
||||||
|
|
||||||
|
generateUserKeyPair,
|
||||||
|
|
||||||
|
generateMasterKey,
|
||||||
|
|
||||||
|
encryptPrivateKey,
|
||||||
|
decryptPrivateKey,
|
||||||
|
|
||||||
|
generateListKey,
|
||||||
|
|
||||||
|
encryptList,
|
||||||
|
decryptList,
|
||||||
|
|
||||||
|
encryptListKeyForUser,
|
||||||
|
decryptListKey
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
export const useCounter = () => {
|
|
||||||
const counter = useState('counter', () => 0)
|
|
||||||
const increment = () => {
|
|
||||||
counter.value++
|
|
||||||
}
|
|
||||||
|
|
||||||
const decrement = () => {
|
|
||||||
counter.value--
|
|
||||||
}
|
|
||||||
|
|
||||||
const reset = () => {
|
|
||||||
counter.value=0
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
counter,
|
|
||||||
increment,
|
|
||||||
decrement,
|
|
||||||
reset
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -89,7 +89,7 @@ const handleFormSubmit = async() => {
|
|||||||
</ButtonBase>
|
</ButtonBase>
|
||||||
</form>
|
</form>
|
||||||
<p>{{ $t('loginGoogle') }}</p>
|
<p>{{ $t('loginGoogle') }}</p>
|
||||||
<ButtonGoogle/>
|
<ButtonGoogleCode/>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { $Fetch } from 'ofetch';
|
import type { $Fetch } from 'ofetch';
|
||||||
import type { List } from '~/types/lists'
|
import type { List, WPListEncrypted } from '~/types/lists'
|
||||||
|
|
||||||
export default class ListRepository {
|
export default class ListRepository {
|
||||||
private fetcher: $Fetch;
|
private fetcher: $Fetch;
|
||||||
@@ -17,6 +17,23 @@ export default class ListRepository {
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: data
|
body: data
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async uploadKeys(data: string) {
|
||||||
|
console.log('uploadEncryptedPrivateKey : data is')
|
||||||
|
console.log(data)
|
||||||
|
|
||||||
|
return await this.fetcher<any>('/lists/PrivKeyCipher', {
|
||||||
|
method: 'POST',
|
||||||
|
body: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async update(data: WPListEncrypted) {
|
||||||
|
const string = JSON.stringify(data);
|
||||||
|
return await this.fetcher<any>('/lists/' + data.id, {
|
||||||
|
method: 'PUT',
|
||||||
|
body: string
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,18 @@
|
|||||||
import { defineStore } from 'pinia'
|
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', {
|
export const useListStore = defineStore('lists', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
lists: [] as List[],
|
lists: [] as List[],
|
||||||
loading: false as boolean,
|
loading: false as boolean,
|
||||||
|
masterKey: null as Uint8Array | null,
|
||||||
|
publicKey: new Uint8Array() as Uint8Array,
|
||||||
|
privateKey: new Uint8Array() as Uint8Array,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
@@ -15,6 +22,9 @@ export const useListStore = defineStore('lists', {
|
|||||||
resetLists(){
|
resetLists(){
|
||||||
this.lists = []
|
this.lists = []
|
||||||
},
|
},
|
||||||
|
setMasterKey(key: Uint8Array){
|
||||||
|
this.masterKey = key
|
||||||
|
},
|
||||||
|
|
||||||
async fetchLists() {
|
async fetchLists() {
|
||||||
// On récupère notre plugin API injecté
|
// On récupère notre plugin API injecté
|
||||||
@@ -33,8 +43,108 @@ export const useListStore = defineStore('lists', {
|
|||||||
} finally {
|
} finally {
|
||||||
this.loading = false;
|
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) {
|
// async updateList(id, title, content) {
|
||||||
// const config = useRuntimeConfig();
|
// const config = useRuntimeConfig();
|
||||||
|
|
||||||
@@ -57,6 +167,26 @@ export const useListStore = defineStore('lists', {
|
|||||||
// console.error("Erreur lors de la récupération des listes:", error);
|
// 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'
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
@@ -1,11 +1,24 @@
|
|||||||
|
export interface WPListEncrypted {
|
||||||
|
id: string;
|
||||||
|
user_id: string;
|
||||||
|
key_cipher: string; // La "data" est cachée là-dedans sous forme de string chiffrée
|
||||||
|
key_nonce: string;
|
||||||
|
content_cipher: string;
|
||||||
|
content_nonce: string;
|
||||||
|
created_at: number | null; // Souvent gardé en clair pour le tri côté front
|
||||||
|
updated_at: number | null;
|
||||||
|
}
|
||||||
|
|
||||||
export interface List {
|
export interface List {
|
||||||
id: number;
|
id: number;
|
||||||
|
user_id: number;
|
||||||
|
aesKey: Uint8Array;
|
||||||
list_title: string;
|
list_title: string;
|
||||||
list_type: string;
|
list_type: string;
|
||||||
encrypted_content: string; // JSON string
|
content: string; // JSON string
|
||||||
is_open: boolean;
|
is_open: boolean;
|
||||||
created_at: string;
|
created_at: number;
|
||||||
updated_at: string;
|
updated_at: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface taskItem {
|
export interface taskItem {
|
||||||
|
|||||||
16
package-lock.json
generated
16
package-lock.json
generated
@@ -15,6 +15,7 @@
|
|||||||
"@nuxtjs/i18n": "^10.2.1",
|
"@nuxtjs/i18n": "^10.2.1",
|
||||||
"@nuxtjs/tailwindcss": "^6.14.0",
|
"@nuxtjs/tailwindcss": "^6.14.0",
|
||||||
"@pinia/nuxt": "^0.11.3",
|
"@pinia/nuxt": "^0.11.3",
|
||||||
|
"libsodium-wrappers": "^0.8.2",
|
||||||
"nuxt": "^4.2.2",
|
"nuxt": "^4.2.2",
|
||||||
"nuxt-vue3-google-signin": "^0.0.13",
|
"nuxt-vue3-google-signin": "^0.0.13",
|
||||||
"pinia": "^3.0.4",
|
"pinia": "^3.0.4",
|
||||||
@@ -8736,6 +8737,21 @@
|
|||||||
"node": ">= 0.8.0"
|
"node": ">= 0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/libsodium": {
|
||||||
|
"version": "0.8.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.8.2.tgz",
|
||||||
|
"integrity": "sha512-TsnGYMoZtpweT+kR+lOv5TVsnJ/9U0FZOsLFzFOMWmxqOAYXjX3fsrPAW+i1LthgDKXJnI9A8dWEanT1tnJKIw==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
|
"node_modules/libsodium-wrappers": {
|
||||||
|
"version": "0.8.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/libsodium-wrappers/-/libsodium-wrappers-0.8.2.tgz",
|
||||||
|
"integrity": "sha512-VFLmfxkxo+U9q60tjcnSomQBRx2UzlRjKWJqvB4K1pUqsMQg4cu3QXA2nrcsj9A1qRsnJBbi2Ozx1hsiDoCkhw==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"libsodium": "^0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/lilconfig": {
|
"node_modules/lilconfig": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
"@nuxtjs/i18n": "^10.2.1",
|
"@nuxtjs/i18n": "^10.2.1",
|
||||||
"@nuxtjs/tailwindcss": "^6.14.0",
|
"@nuxtjs/tailwindcss": "^6.14.0",
|
||||||
"@pinia/nuxt": "^0.11.3",
|
"@pinia/nuxt": "^0.11.3",
|
||||||
|
"libsodium-wrappers": "^0.8.2",
|
||||||
"nuxt": "^4.2.2",
|
"nuxt": "^4.2.2",
|
||||||
"nuxt-vue3-google-signin": "^0.0.13",
|
"nuxt-vue3-google-signin": "^0.0.13",
|
||||||
"pinia": "^3.0.4",
|
"pinia": "^3.0.4",
|
||||||
|
|||||||
Reference in New Issue
Block a user