Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

213 support for symmetric key within encryptedthing and decryptedthing #215

Closed
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 28 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

<br>
<p align="center">
<img width="140" src="./docs/peerbit-logo.png" alt="Peerbit icon Icon">
Expand Down Expand Up @@ -26,48 +25,67 @@ Your database schema can remain very simple but still utilize P2P networks, auto

## Optimized for performance

Peerbit is performant, so performant in fact you can use it for [streaming video](https://stream.dao.xyz) by having peers subscribing to database updates. In a low latency setting, you can achieve around 1000 replications a second and have a thoughput of 100 MB/s.
Peerbit is performant, so performant in fact you can use it for [streaming video](https://stream.dao.xyz) by having peers subscribing to database updates. In a low latency setting, you can achieve around 1000 replications a second and have a thoughput of 100 MB/s.

![Dogestream](/docs/videostream.gif)


## Other examples

### [Chat room](https://github.com/dao-xyz/peerbit-examples/tree/master/packages/one-chat-room/)

[<img src="https://github.com/dao-xyz/peerbit-examples/blob/master/packages/one-chat-room/demo.gif" width="600" />](https://github.com/dao-xyz/peerbit-examples/tree/master/packages/one-chat-room/)

### [Lobby + chat rooms](https://github.com/dao-xyz/peerbit-examples/tree/master/packages/many-chat-rooms/)

[<img src="https://github.com/dao-xyz/peerbit-examples/blob/master/packages/many-chat-rooms/demo.gif" width="600" />](https://github.com/dao-xyz/peerbit-examples/tree/master/packages/many-chat-rooms/)

### [Sync files](https://github.com/dao-xyz/peerbit-examples/tree/master/packages/file-share/)

#### [React app](https://github.com/dao-xyz/peerbit-examples/tree/master/packages/file-share/)

[<img src="https://github.com/dao-xyz/peerbit-examples/blob/master/packages/file-share/demo-frontend.gif" width="600" />](https://github.com/dao-xyz/peerbit-examples/tree/master/packages/file-share/)

#### [CLI](https://github.com/dao-xyz/peerbit-examples/tree/master/packages/file-share/)
[<img src="https://github.com/dao-xyz/peerbit-examples/blob/master/packages/file-share/demo-cli.gif" width="600" />](https://github.com/dao-xyz/peerbit-examples/tree/master/packages/file-share/)


[<img src="https://github.com/dao-xyz/peerbit-examples/blob/master/packages/file-share/demo-cli.gif" width="600" />](https://github.com/dao-xyz/peerbit-examples/tree/master/packages/file-share/)

### [Collaborative machine learning](https://github.com/dao-xyz/peerbit-examples/tree/master/packages/collaborative-learning/)
[<img src="https://github.com/dao-xyz/peerbit-examples/blob/master/packages/collaborative-learning/demo.gif" width="600" />](https://github.com/dao-xyz/peerbit-examples/tree/master/packages/collaborative-learning/)


[<img src="https://github.com/dao-xyz/peerbit-examples/blob/master/packages/collaborative-learning/demo.gif" width="600" />](https://github.com/dao-xyz/peerbit-examples/tree/master/packages/collaborative-learning/)

## Get Started

1. Install Peerbit by following the simple setup instructions in our [Installation Guide](https://peerbit.org/#/getting-started).

2. Dive into our comprehensive [Documentation](https://peerbit.org/#/modules/client/) or checkout the [Example repository](https://github.com/dao-xyz/peerbit-examples) to explore the powerful features and learn how to leverage Peerbit to its fullest potential.
2. Dive into our comprehensive [Documentation](https://peerbit.org/#/modules/client/) or checkout the [Example repository](https://github.com/dao-xyz/peerbit-examples) to explore the powerful features and learn how to leverage Peerbit to its fullest potential.

3. Join us on [Matrix](https://matrix.to/#/#peerbit:matrix.org) to connect, share ideas, and collaborate with like-minded individuals.

## Contribute

Peerbit is an open-source project, and we welcome contributions from developers like you! Feel free to contribute code, report issues, and submit feature requests. Together, let's shape the future of Peerbit.

IMPORTANT: Peerbit uses yarn.

1. Check yarn version: `yarn -v` should print 1.something
2. Install: `yarn`
3. Build: `yarn build`
4. Run tests: `yarn test`

You might possibly need to CMD + Shift + P and then enter to restart the typescript server after the build step.

To create a new package, follow the following steps:

1. Clone the time folder within /packages/utils/time to the desired destination and rename it
2. Update the package.json `name`, `description`, `version` fields
3. Possibly add other depencencies to the package.json `dependencies` field (like `@peerbit/crypto`)
4. Delete contents in CHANGELOG.md
5. Update the root package.json `workspaces.packages` field
6. Update root lerna.json `workspaces.packages` field
7. run yarn once in root

We recommend running tests with the VS Code integration though: https://marketplace.visualstudio.com/items?itemName=firsttris.vscode-jest-runner

## Let's Get Coding!

[peerbit.org](https://peerbit.org)


3 changes: 2 additions & 1 deletion lerna.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@
"packages/utils/uint8arrays",
"packages/utils/cache",
"packages/utils/time",
"packages/utils/logger"
"packages/utils/logger",
"packages/utils/keychain"
],
"version": "independent",
"npmClient": "yarn"
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@
"packages/utils/uint8arrays",
"packages/utils/time",
"packages/utils/cache",
"packages/utils/logger"
"packages/utils/logger",
"packages/utils/keychain"
]
},
"engines": {
Expand Down
81 changes: 79 additions & 2 deletions packages/transport/stream/src/__tests__/stream.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -814,6 +814,73 @@ describe("streams", function () {
});

it("through relay if fails", async () => {
const dialFn =
streams[0].stream.components.connectionManager.openConnection.bind(
streams[0].stream.components.connectionManager
);

let directlyDialded = false;
const filteredDial = (address: PeerId | Multiaddr | Multiaddr[]) => {
if (
isPeerId(address) &&
address.toString() === streams[3].stream.peerIdStr
) {
throw new Error("Mock fail"); // don't allow connect directly
}

let addresses: Multiaddr[] = Array.isArray(address)
? address
: [address as Multiaddr];
for (const a of addresses) {
if (
!a.protoNames().includes("p2p-circuit") &&
a.toString().includes(streams[3].stream.peerIdStr)
) {
throw new Error("Mock fail"); // don't allow connect directly
}
}
addresses = addresses.map((x) =>
x.protoNames().includes("p2p-circuit")
? multiaddr(x.toString().replace("/webrtc/", "/"))
: x
); // TODO use webrtc in node

directlyDialded = true;
return dialFn(addresses);
};

streams[0].stream.components.connectionManager.openConnection =
filteredDial;
expect(streams[0].stream.peers.size).toEqual(1);
await streams[0].stream.publish(data, {
to: [streams[3].stream.components.peerId]
});
await waitFor(() => streams[3].received.length === 1);
await waitForResolved(() => expect(directlyDialded).toBeTrue());
});

it("tries multiple relays", async () => {
await session.connect([[session.peers[1], session.peers[3]]]);
await waitForPeerStreams(streams[1].stream, streams[3].stream);

/*
┌───┐
│ 0 │
└┬─┬┘
│┌▽┐
││1│
│└┬┘
┌▽┐│
│2││
└┬┘│
┌▽─▽─┐
│ 3 │
└────┘

*/

const dialedCircuitRelayAddresses: Set<string> = new Set();

const dialFn =
streams[0].stream.components.connectionManager.openConnection.bind(
streams[0].stream.components.connectionManager
Expand All @@ -837,22 +904,32 @@ describe("streams", function () {
throw new Error("Mock fail"); // don't allow connect directly
}
}
const q = 123;
addresses
.filter((x) => x.protoNames().includes("p2p-circuit"))
.forEach((x) => {
dialedCircuitRelayAddresses.add(x.toString());
});
addresses = addresses.map((x) =>
x.protoCodes().includes(281)
x.protoNames().includes("p2p-circuit")
? multiaddr(x.toString().replace("/webrtc/", "/"))
: x
); // TODO use webrtc in node

if (dialedCircuitRelayAddresses.size === 1) {
throw new Error("Mock fail"); // only succeed with the dial once we have tried two unique addresses (both neighbors)
}
return dialFn(addresses);
};

streams[0].stream.components.connectionManager.openConnection =
filteredDial;

expect(streams[0].stream.peers.size).toEqual(1);
await streams[0].stream.publish(data, {
to: [streams[3].stream.components.peerId]
});
await waitFor(() => streams[3].received.length === 1);
expect(dialedCircuitRelayAddresses.size).toEqual(2);
});
});

Expand Down
90 changes: 43 additions & 47 deletions packages/transport/stream/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1412,7 +1412,7 @@ export abstract class DirectStream<
) {
// Dont await this even if it is async since this method can fail
// and might take some time to run
this.maybeConnectDirectly(path).catch((e) => {
this.maybeConnectDirectly(to).catch((e) => {
logger.error(
"Failed to request direct connection: " + e.message
);
Expand Down Expand Up @@ -1499,13 +1499,7 @@ export abstract class DirectStream<
}
}

async maybeConnectDirectly(path: string[]) {
if (path.length < 3) {
return;
}

const toHash = path[path.length - 1];

async maybeConnectDirectly(toHash: string) {
if (this.peers.has(toHash)) {
return; // TODO, is this expected, or are we to dial more addresses?
}
Expand All @@ -1527,49 +1521,51 @@ export abstract class DirectStream<
}

// Connect through a closer relay that maybe does holepunch for us
const nextToHash = path[path.length - 2];
const routeKey = nextToHash + toHash;
if (!this.recentDials.has(routeKey)) {
this.recentDials.add(routeKey);
const to = this.peerKeyHashToPublicKey.get(toHash)! as Ed25519PublicKey;
const toPeerId = await to.toPeerId();
const addrs = this.multiaddrsMap.get(path[path.length - 2]);
if (addrs && addrs.length > 0) {
const addressesToDial = addrs.sort((a, b) => {
if (a.includes("/wss/")) {
if (b.includes("/wss/")) {
return 0;
}
return -1;
}
if (a.includes("/ws/")) {
if (b.includes("/ws/")) {
return 0;
const neighbours = this.routes.graph.neighbors(toHash);
outer: for (const neighbour of neighbours) {
const routeKey = neighbour + toHash;
if (!this.recentDials.has(routeKey)) {
this.recentDials.add(routeKey);
const to = this.peerKeyHashToPublicKey.get(toHash)! as Ed25519PublicKey;
const toPeerId = await to.toPeerId();
const addrs = this.multiaddrsMap.get(neighbour);
if (addrs && addrs.length > 0) {
const addressesToDial = addrs.sort((a, b) => {
if (a.includes("/wss/")) {
if (b.includes("/wss/")) {
return 0;
}
return -1;
}
if (b.includes("/wss/")) {
return 1;
if (a.includes("/ws/")) {
if (b.includes("/ws/")) {
return 0;
}
if (b.includes("/wss/")) {
return 1;
}
return -1;
}
return -1;
}
return 0;
});
return 0;
});

for (const addr of addressesToDial) {
const circuitAddress = multiaddr(
addr + "/p2p-circuit/webrtc/p2p/" + toPeerId.toString()
);
try {
await this.components.connectionManager.openConnection(
circuitAddress
);
return;
} catch (error: any) {
logger.error(
"Failed to connect directly to: " +
circuitAddress.toString() +
". " +
error?.message
for (const addr of addressesToDial) {
const circuitAddress = multiaddr(
addr + "/p2p-circuit/webrtc/p2p/" + toPeerId.toString()
);
try {
await this.components.connectionManager.openConnection(
circuitAddress
);
break outer; // We succeeded! that means we dont have to try anymore
} catch (error: any) {
logger.warn(
"Failed to connect directly to: " +
circuitAddress.toString() +
". " +
error?.message
);
}
}
}
}
Expand Down
37 changes: 20 additions & 17 deletions packages/utils/crypto/src/__tests__/encryption.test.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,11 @@
import {
DecryptedThing,
X25519Keypair,
Keychain,
PublicSignKey
createDecrypterFromKeyResolver,
createLocalEncryptProvider
} from "../index.js";

describe("encryption", function () {
const keychain = (keypair: X25519Keypair): Keychain => {
return {
exportById: async (id: Uint8Array) => undefined,
exportByKey: async <T extends PublicSignKey, Q>(publicKey: T) =>
publicKey.equals(keypair.publicKey) ? (keypair as Q) : undefined,
import: (keypair: any, id: Uint8Array) => {
throw new Error("No implemented+");
}
};
};
it("encrypt", async () => {
const senderKey = await X25519Keypair.create();
const receiverKey1 = await X25519Keypair.create();
Expand All @@ -26,14 +16,27 @@ describe("encryption", function () {
data
});

const receiver1Config = keychain(receiverKey1);
const receiver2Config = keychain(receiverKey2);
const receiver1Config = createDecrypterFromKeyResolver(
() => receiverKey1 as any
);
const receiver2Config = createDecrypterFromKeyResolver(
() => receiverKey2 as any
);

const encrypted = await decrypted.encrypt(
senderKey,
receiverKey1.publicKey,
receiverKey2.publicKey
createLocalEncryptProvider(new Uint8Array([1, 2, 3])),
{
receiverPublicKeys: [receiverKey1.publicKey, receiverKey2.publicKey]
}
);

/* const encrypted = await decrypted.encrypt(
createLocalEncryptProvider(new Uint8Array(32)),
{
type: 'symmetric'
},
); */

encrypted._decrypted = undefined;

const decryptedFromEncrypted1 = await encrypted.decrypt(receiver1Config);
Expand Down
Loading
Loading