Skip to content

Commit

Permalink
feat: implemented ClaimNetworkAccess and ClaimNetworkAuthority in
Browse files Browse the repository at this point in the history
preparation for network segregation

wip
  • Loading branch information
amydevs committed Sep 2, 2024
1 parent fad5e05 commit b37307a
Show file tree
Hide file tree
Showing 10 changed files with 90 additions and 86 deletions.
5 changes: 5 additions & 0 deletions src/PolykeyAgent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import * as workersUtils from './workers/utils';
import * as clientMiddleware from './client/middleware';
import clientServerManifest from './client/handlers';
import agentServerManifest from './nodes/agent/handlers';

/**
* Optional configuration for `PolykeyAgent`.
*/
Expand All @@ -61,6 +62,7 @@ type PolykeyAgentOptions = {
clientServicePort: number;
agentServiceHost: string;
agentServicePort: number;
network: string;
seedNodes: SeedNodes;
workers: number;
ipv6Only: boolean;
Expand Down Expand Up @@ -160,6 +162,7 @@ class PolykeyAgent {
agentServiceHost: config.defaultsUser.agentServiceHost,
agentServicePort: config.defaultsUser.agentServicePort,
seedNodes: config.defaultsUser.seedNodes,
network: config.defaultsUser.network,
workers: config.defaultsUser.workers,
ipv6Only: config.defaultsUser.ipv6Only,
keys: {
Expand Down Expand Up @@ -687,6 +690,7 @@ class PolykeyAgent {
groups: Array<string>;
port: number;
};
network: string;
seedNodes: SeedNodes;
}>;
workers?: number;
Expand All @@ -705,6 +709,7 @@ class PolykeyAgent {
groups: config.defaultsSystem.mdnsGroups,
port: config.defaultsSystem.mdnsPort,
},
network: config.defaultsUser.network,
seedNodes: config.defaultsUser.seedNodes,
});
// Register event handlers
Expand Down
2 changes: 1 addition & 1 deletion src/bootstrap/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import * as utils from '../utils';
import * as errors from '../errors';

/**
* Bootstraps the Node Path
* Bootstraps the Node Path`
*/
async function bootstrapState({
// Required parameters
Expand Down
17 changes: 15 additions & 2 deletions src/claims/payloads/claimNetworkAccess.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Claim, SignedClaim } from '../types';
import type { NodeIdEncoded } from '../../ids/types';
import type { SignedTokenEncoded } from '../../tokens/types';
import * as tokensSchema from '../../tokens/schemas';
import * as ids from '../../ids';
import * as claimsUtils from '../utils';
import * as tokensUtils from '../../tokens/utils';
Expand All @@ -14,7 +15,8 @@ interface ClaimNetworkAccess extends Claim {
typ: 'ClaimNetworkAccess';
iss: NodeIdEncoded;
sub: NodeIdEncoded;
signedClaimNetworkAuthorityEncoded: SignedTokenEncoded;
network: string;
signedClaimNetworkAuthorityEncoded?: SignedTokenEncoded;
}

function assertClaimNetworkAccess(
Expand Down Expand Up @@ -45,7 +47,18 @@ function assertClaimNetworkAccess(
);
}
if (
claimNetworkAccess['signedClaimNetworkAuthorityEncoded'] == null
claimNetworkAccess['network'] == null ||
typeof claimNetworkAccess['network'] !== 'string'
) {
throw new validationErrors.ErrorParse(
'`network` property must be a string',
);
}
if (
claimNetworkAccess['signedClaimNetworkAuthorityEncoded'] != null &&
!tokensSchema.validateSignedTokenEncoded(
claimNetworkAccess['signedClaimNetworkAuthorityEncoded'],
)
) {
throw new validationErrors.ErrorParse(
'`signedClaimNetworkAuthorityEncoded` property must be an encoded signed token',
Expand Down
2 changes: 1 addition & 1 deletion src/claims/payloads/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export * from './claimLinkIdentity';
export * from './claimLinkNode';
export * from './claimNetworkNode';
export * from './claimNetworkAccess';
6 changes: 6 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,12 @@ const config = {
*/
agentServiceHost: '::',
agentServicePort: 0,
/**
* Hostname of network to connect to.
*
* This is defaulted to 'mainnet.polykey.com'.
*/
network: 'mainnet.polykey.com',
/**
* Seed nodes.
*
Expand Down
1 change: 0 additions & 1 deletion src/nodes/NodeConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import * as nodesUtils from '../nodes/utils';
import { never } from '../utils';
import config from '../config';
import * as networkUtils from '../network/utils';
import * as keysUtils from '../keys/utils';

type AgentClientManifest = typeof agentClientManifest;

Expand Down
73 changes: 25 additions & 48 deletions src/nodes/NodeManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ import * as nodesEvents from './events';
import * as nodesErrors from './errors';
import * as agentErrors from './agent/errors';
import NodeConnectionQueue from './NodeConnectionQueue';
import { assertClaimNetworkAuthority } from '../claims/payloads/claimNetworkAuthority';
import { assertClaimNetworkAccess } from '../claims/payloads/claimNetworkAccess';
import Token from '../tokens/Token';
import * as keysUtils from '../keys/utils';
import * as tasksErrors from '../tasks/errors';
Expand All @@ -58,8 +60,6 @@ import * as claimsErrors from '../claims/errors';
import * as utils from '../utils/utils';
import config from '../config';
import * as networkUtils from '../network/utils';
import { ClaimNetworkAccess, assertClaimNetworkAccess } from '../claims/payloads/claimNetworkAccess';
import { ClaimNetworkAuthority, assertClaimNetworkAuthority } from '@/claims/payloads/claimNetworkAuthority';

const abortEphemeralTaskReason = Symbol('abort ephemeral task reason');
const abortSingletonTaskReason = Symbol('abort singleton task reason');
Expand Down Expand Up @@ -263,43 +263,6 @@ class NodeManager {
cause: new AggregateError(failedConnectionErrors),
},
);
} else {
// Wip: We should ideally take the fastest connection and use it here for node signing.
const conn = successfulConnections[0].value;
await this.sigchain.addClaim(
{
typ: 'ClaimNetworkNode',
iss: nodesUtils.encodeNodeId(conn.nodeId),
sub: nodesUtils.encodeNodeId(this.keyRing.getNodeId()),
},
undefined,
async (token) => {
const halfSignedClaim = token.toSigned();
const halfSignedClaimEncoded =
claimsUtils.generateSignedClaim(halfSignedClaim);
const receivedClaim =
await conn.rpcClient.methods.nodesClaimNetworkSign({
signedTokenEncoded: halfSignedClaimEncoded,
});
const signedClaim = claimsUtils.parseSignedClaim(
receivedClaim.signedTokenEncoded,
);
const fullySignedToken = Token.fromSigned(signedClaim);
// Check that the signatures are correct
const targetNodePublicKey = keysUtils.publicKeyFromNodeId(
conn.nodeId,
);
if (
!fullySignedToken.verifyWithPublicKey(
this.keyRing.keyPair.publicKey,
) ||
!fullySignedToken.verifyWithPublicKey(targetNodePublicKey)
) {
throw new claimsErrors.ErrorDoublySignedClaimVerificationFailed();
}
return fullySignedToken;
},
);
}
if (ctx.signal.aborted) return;

Expand Down Expand Up @@ -1575,7 +1538,18 @@ class NodeManager {
) {
throw new claimsErrors.ErrorDoublySignedClaimVerificationFailed();
}
const authorityToken = Token.fromEncoded(token.payload.signedClaimNetworkAuthorityEncoded);
if (
token.payload.network === 'testnet.polykey.com' ||
token.payload.network === 'mainnet.polykey.com'
) {
return { success: true };
}
if (token.payload.signedClaimNetworkAuthorityEncoded == null) {
throw new claimsErrors.ErrorDoublySignedClaimVerificationFailed();
}
const authorityToken = Token.fromEncoded(
token.payload.signedClaimNetworkAuthorityEncoded,
);
// Verify if the token is signed
if (
token.payload.iss !== authorityToken.payload.sub ||
Expand All @@ -1597,23 +1571,26 @@ class NodeManager {
for await (const [_, claim] of this.sigchain.getSignedClaims({})) {
try {
assertClaimNetworkAccess(claim.payload);
}
catch {
} catch {
continue;
}
const tokenNetworkAuthority = Token.fromEncoded(claim.payload.signedClaimNetworkAuthorityEncoded);
if (claim.payload.signedClaimNetworkAuthorityEncoded == null) {
throw new claimsErrors.ErrorDoublySignedClaimVerificationFailed();
}
const tokenNetworkAuthority = Token.fromEncoded(
claim.payload.signedClaimNetworkAuthorityEncoded,
);
try {
assertClaimNetworkAuthority(tokenNetworkAuthority.payload);
}
catch {
} catch {
continue;
}
// No need to check if local claims are correctly signed by an Network Authority.
if (
authorityToken.verifyWithPublicKey(
keysUtils.publicKeyFromNodeId(
nodesUtils.decodeNodeId(claim.payload.iss)!,
)
),
)
) {
success = true;
Expand All @@ -1627,7 +1604,7 @@ class NodeManager {

return {
success: true,
}
};
}

/**
Expand Down Expand Up @@ -1687,7 +1664,7 @@ class NodeManager {
);
}

// need to await node connection verification, if fail, need to reject connection.
// Need to await node connection verification, if fail, need to reject connection.

// When adding a node we need to handle 3 cases
// 1. The node already exists. We need to update it's last updated field
Expand Down
2 changes: 1 addition & 1 deletion src/nodes/agent/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class ErrorNodesConnectionSignalRelayVerificationFailed<

class ErrorNodesClaimNetworkVerificationFailed<T> extends ErrorAgent<T> {
static description = 'Failed to verify claim network message';
exitCode = sysexits.UNAVAILABLE
exitCode = sysexits.UNAVAILABLE;
}

export {
Expand Down
10 changes: 4 additions & 6 deletions src/nodes/agent/handlers/NodesClaimNetworkVerify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,10 @@ import type {
AgentRPCResponseResult,
} from '../types';
import type NodeManager from '../../../nodes/NodeManager';
import type { Host, Port } from '../../../network/types';
import type { JSONValue } from '../../../types';
import { UnaryHandler } from '@matrixai/rpc';
import * as x509 from '@peculiar/x509';
import * as agentErrors from '../errors';
import * as agentUtils from '../utils';
import { never } from '../../../utils';
import * as keysUtils from '../../../keys/utils';
import * as ids from '../../../ids';

class NodesClaimNetworkVerify extends UnaryHandler<
{
Expand All @@ -30,7 +25,10 @@ class NodesClaimNetworkVerify extends UnaryHandler<
if (requestingNodeId == null) {
throw new agentErrors.ErrorAgentNodeIdMissing();
}
return this.container.nodeManager.handleVerifyClaimNetwork(requestingNodeId, input);
return this.container.nodeManager.handleVerifyClaimNetwork(
requestingNodeId,
input,
);
};
}

Expand Down
Loading

0 comments on commit b37307a

Please sign in to comment.