Skip to content

Commit

Permalink
address comments
Browse files Browse the repository at this point in the history
  • Loading branch information
weboko committed Nov 30, 2023
1 parent 63a92cf commit ce693cf
Show file tree
Hide file tree
Showing 11 changed files with 1,111 additions and 935 deletions.
42 changes: 11 additions & 31 deletions examples/flush-notes/README.md
Original file line number Diff line number Diff line change
@@ -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
# or
yarn dev
npm start
# 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.
1,850 changes: 1,024 additions & 826 deletions examples/flush-notes/package-lock.json

Large diffs are not rendered by default.

9 changes: 4 additions & 5 deletions examples/flush-notes/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
4 changes: 1 addition & 3 deletions examples/flush-notes/src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,8 @@
align-items: center;
}

.password-value {
.to-encrypt {
font-size: 1rem;
height: 2rem;
width: 250px;
padding: 5px;
}

Expand Down
2 changes: 1 addition & 1 deletion examples/flush-notes/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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"] });
Expand Down
40 changes: 23 additions & 17 deletions examples/flush-notes/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,20 @@ 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) {
return;
}

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);
}
};

Expand All @@ -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>
Expand All @@ -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,
};
};
2 changes: 1 addition & 1 deletion examples/flush-notes/src/app/view/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
7 changes: 4 additions & 3 deletions examples/flush-notes/src/hooks/useNoteURL.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand Down
88 changes: 41 additions & 47 deletions examples/flush-notes/src/services/notes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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 {
Expand All @@ -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(
Expand All @@ -74,7 +80,7 @@ export class Notes {
}
});

if (!message?.kdf) {
if (!message?.iv) {
return message?.content;
}

Expand All @@ -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);
}
Expand Down

0 comments on commit ce693cf

Please sign in to comment.