address comments
This commit is contained in:
parent
63a92cf825
commit
ce693cfdf1
|
@ -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
|
@ -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",
|
||||
|
|
|
@ -21,10 +21,8 @@
|
|||
align-items: center;
|
||||
}
|
||||
|
||||
.password-value {
|
||||
.to-encrypt {
|
||||
font-size: 1rem;
|
||||
height: 2rem;
|
||||
width: 250px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
|
|
|
@ -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"] });
|
||||
|
|
|
@ -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">
|
||||
<div>
|
||||
<input
|
||||
className="password-value"
|
||||
type="password"
|
||||
onChange={onPasswordChange}
|
||||
placeholder="Optional password"
|
||||
autoComplete="off"
|
||||
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,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
|
||||
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),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue