address comments

This commit is contained in:
Sasha 2023-11-30 02:03:27 +01:00
parent 63a92cf825
commit ce693cfdf1
No known key found for this signature in database
11 changed files with 1110 additions and 934 deletions

View File

@ -1,36 +1,16 @@
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
## Waku dependencies
- @waku/interfaces
- @waku/message-encryption
- @waku/sdk
- @waku/utils
## Getting Started
First, run the development server:
## Description
Exchange encrypted or plain notes by link.
This example shows how symmetric encryption can be used to encrypt only part of Waku message.
## How to run
```bash
npm run dev
npm start
# or
yarn dev
# or
pnpm dev
# or
bun dev
yarn start
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.

File diff suppressed because it is too large Load Diff

View File

@ -8,11 +8,10 @@
"lint": "next lint"
},
"dependencies": {
"@waku/interfaces": "^0.0.20",
"@waku/message-encryption": "^0.0.23",
"@waku/sdk": "^0.0.21",
"@waku/utils": "^0.0.13",
"ethereum-cryptography": "^2.1.2",
"@waku/interfaces": "0.0.21-7eb3375.0",
"@waku/message-encryption": "0.0.24-7eb3375.0",
"@waku/sdk": "0.0.22-7eb3375.0",
"@waku/utils": "0.0.14-7eb3375.0",
"next": "14.0.2",
"react": "^18",
"react-dom": "^18",

View File

@ -21,10 +21,8 @@
align-items: center;
}
.password-value {
.to-encrypt {
font-size: 1rem;
height: 2rem;
width: 250px;
padding: 5px;
}

View File

@ -2,7 +2,7 @@
import React from "react";
import { Inter } from "next/font/google";
import { WakuProvider } from "@/app/WakuProvider";
import { WakuProvider } from "@/components/WakuProvider";
import "./globals.css";
const inter = Inter({ subsets: ["latin"] });

View File

@ -7,7 +7,7 @@ import { notes } from "@/services/notes";
export default function Create() {
const router = useRouter();
const { note, onNoteChange } = useEditNote();
const { password, onPasswordChange } = usePassword();
const { toEncrypt, onEncryptChange } = useEncryptedState();
const onSave = async () => {
if (!note) {
@ -15,10 +15,12 @@ export default function Create() {
}
try {
const id = await notes.createNote(note, password);
router.push(`/view/${id}`);
const { id, password } = await notes.createNote(note, toEncrypt);
const passwordParam = password ? `?password=${password}` : "";
router.push(`/view/${id}${passwordParam}`);
} catch (error) {
console.log("Failed to create a note:", error);
console.error("Failed to create a note:", error);
}
};
@ -28,13 +30,17 @@ export default function Create() {
Your record will be stored for couple of days. Markdown is supported.
</p>
<div className="create-header">
<input
className="password-value"
type="password"
onChange={onPasswordChange}
placeholder="Optional password"
autoComplete="off"
/>
<div>
<input
type="checkbox"
id="isEncrypted"
name="isEncrypted"
onChange={onEncryptChange}
/>
<label htmlFor="isEncrypted" className="to-encrypt">
Private (only those that have link will read the note)
</label>
</div>
<button onClick={onSave} className="save-note">
Save note
</button>
@ -61,15 +67,15 @@ const useEditNote = () => {
};
};
const usePassword = () => {
const [password, setPassword] = React.useState<string>();
const useEncryptedState = () => {
const [toEncrypt, setToEncrypt] = React.useState<string>();
const onPasswordChange = (event: React.FormEvent<HTMLInputElement>) => {
setPassword(event?.currentTarget?.value);
const onEncryptChange = (event: React.FormEvent<HTMLSelectElement>) => {
setToEncrypt(event?.currentTarget?.value);
};
return {
password,
onPasswordChange,
toEncrypt,
onEncryptChange,
};
};

View File

@ -5,7 +5,7 @@ import Markdown from "react-markdown";
import { useRouter } from "next/navigation";
import { useNoteURL } from "@/hooks/useNoteURL";
import { notes } from "@/services/notes";
import { Loading } from "../Loading";
import { Loading } from "@/components/Loading";
const View = () => {
const router = useRouter();

View File

@ -3,7 +3,7 @@
import React from "react";
import { waku, Waku, WakuEvents } from "@/services/waku";
import { WakuStatus } from "@/const";
import { Loading } from "./Loading";
import { Loading } from "@/components/Loading";
type WakuContextProps = {
status: WakuStatus;

View File

@ -1,12 +1,13 @@
"use client";
import { usePathname } from "next/navigation";
import { usePathname, useSearchParams } from "next/navigation";
export const useNoteURL = (): undefined | string => {
const pathname = usePathname();
const urlParams = new URLSearchParams(window.location.search);
const params = useSearchParams();
const segments = pathname.split("/");
const viewIndex = segments.indexOf("view");
const password = urlParams.get("password");
const password = params.get("password");
return {
password,

View File

@ -2,9 +2,10 @@
import { waku } from "@/services/waku";
import { CONTENT_TOPIC } from "@/const";
import { encrypt, decrypt } from "ethereum-cryptography/aes";
import { getRandomBytes } from "ethereum-cryptography/random";
import { pbkdf2 } from "ethereum-cryptography/pbkdf2";
import {
symmetric,
generateSymmetricKey,
} from "@waku/message-encryption/crypto";
import {
createDecoder,
createEncoder,
@ -21,14 +22,12 @@ import { bytesToHex, hexToBytes } from "@waku/utils/bytes";
type Note = {
id: string;
content: string;
kdf:
| undefined
| {
iv: string;
dklen: number;
c: number;
salt: string;
};
iv: string;
};
type NoteResult = {
id: string;
password?: string;
};
export class Notes {
@ -42,16 +41,23 @@ export class Notes {
this.encoder = createEncoder({ contentTopic: CONTENT_TOPIC });
}
public async createNote(content: string, password?: string): Promise<string> {
const note = password
? await this.encryptNote(content, password)
: { id: generateRandomString(), content, kdf: undefined };
public async createNote(
content: string,
toEncrypt?: boolean
): Promise<NoteResult> {
const symmetricKey = toEncrypt ? generateSymmetricKey() : undefined;
const note = toEncrypt
? await this.encryptNote(content, symmetricKey)
: { id: generateRandomString(), content, iv: undefined };
await waku.send(this.encoder, {
payload: utf8ToBytes(JSON.stringify(note)),
});
return note.id;
return {
id: note.id,
password: symmetricKey ? bytesToHex(symmetricKey) : undefined,
};
}
public async readNote(
@ -74,7 +80,7 @@ export class Notes {
}
});
if (!message?.kdf) {
if (!message?.iv) {
return message?.content;
}
@ -100,48 +106,36 @@ export class Notes {
});
}
private async encryptNote(content: string, password: string): Promise<Note> {
const iv = await getRandomBytes(16);
const salt = await getRandomBytes(32);
const c = 131072;
const dklen = 16;
const kdf = await pbkdf2(
utf8ToBytes(password.normalize("NFKC")),
salt,
c,
dklen,
"sha256"
private async encryptNote(
content: string,
symmetricKey: Uint8Array
): Promise<Note> {
const iv = symmetric.generateIv();
const encryptedContent = await symmetric.encrypt(
iv,
symmetricKey,
utf8ToBytes(content)
);
const encryptedContent = await encrypt(utf8ToBytes(content), kdf, iv);
return {
id: generateRandomString(),
content: bytesToHex(encryptedContent),
kdf: {
c,
dklen,
iv: bytesToHex(iv),
salt: bytesToHex(salt),
},
iv: bytesToHex(iv),
};
}
private async decryptNote(note: Note, password: string): Promise<string> {
if (!note?.kdf) {
throw Error("Failed to decrypt a note, no kdf params found.");
if (!note?.iv) {
throw Error("Failed to decrypt a note, no IV params found.");
}
const iv = hexToBytes(note.kdf.iv);
const salt = hexToBytes(note.kdf.salt);
const kdf = await pbkdf2(
utf8ToBytes(password.normalize("NFKC")),
salt,
note.kdf.c,
note.kdf.dklen,
"sha256"
const iv = hexToBytes(note.iv);
const symmetricKey = hexToBytes(password);
const decryptedContent = await symmetric.decrypt(
iv,
symmetricKey,
hexToBytes(note.content)
);
const decryptedContent = await decrypt(hexToBytes(note.content), kdf, iv);
return bytesToUtf8(decryptedContent);
}