The AI trust layer between agents: identity, agreements, safeguards, and outcome intelligence for A2A.

Viewing files at commit b506ef3834f7Commit detailsView latest

packages/persistence/src/relay.ts 69 lines · 2.3 KB · typescript
1
import {
2
  createCipheriv,
3
  createDecipheriv,
4
  createHash,
5
  createHmac,
6
  randomBytes,
7
  timingSafeEqual,
8
} from 'node:crypto';
9
10
export type GatewayGrant = {
11
  interactionId: string;
12
  senderAgentId: string;
13
  recipientAgentId: string;
14
  expiresAt: number;
15
};
16
17
const keyFromSecret = (secret: string) => createHash('sha256').update(secret).digest();
18
19
export function encryptGatewayPayload(secret: string, payload: unknown) {
20
  const iv = randomBytes(12);
21
  const cipher = createCipheriv('aes-256-gcm', keyFromSecret(secret), iv);
22
  const ciphertext = Buffer.concat([
23
    cipher.update(JSON.stringify(payload), 'utf8'),
24
    cipher.final(),
25
  ]);
26
  return {
27
    ciphertext: ciphertext.toString('base64url'),
28
    iv: iv.toString('base64url'),
29
    authTag: cipher.getAuthTag().toString('base64url'),
30
  };
31
}
32
33
export function decryptGatewayPayload(
34
  secret: string,
35
  encrypted: { ciphertext: string; iv: string; authTag: string },
36
): unknown {
37
  const decipher = createDecipheriv(
38
    'aes-256-gcm',
39
    keyFromSecret(secret),
40
    Buffer.from(encrypted.iv, 'base64url'),
41
  );
42
  decipher.setAuthTag(Buffer.from(encrypted.authTag, 'base64url'));
43
  return JSON.parse(
44
    Buffer.concat([
45
      decipher.update(Buffer.from(encrypted.ciphertext, 'base64url')),
46
      decipher.final(),
47
    ]).toString('utf8'),
48
  );
49
}
50
51
export function issueGatewayGrant(secret: string, grant: GatewayGrant): string {
52
  const payload = Buffer.from(JSON.stringify(grant)).toString('base64url');
53
  const signature = createHmac('sha256', secret).update(payload).digest('base64url');
54
  return `${payload}.${signature}`;
55
}
56
57
export function verifyGatewayGrant(secret: string, token: string): GatewayGrant {
58
  const [payload, provided] = token.split('.');
59
  if (!payload || !provided) throw new Error('Invalid gateway token');
60
  const expected = createHmac('sha256', secret).update(payload).digest();
61
  const actual = Buffer.from(provided, 'base64url');
62
  if (actual.length !== expected.length || !timingSafeEqual(actual, expected))
63
    throw new Error('Invalid gateway token');
64
  const grant = JSON.parse(Buffer.from(payload, 'base64url').toString('utf8')) as GatewayGrant;
65
  if (grant.expiresAt <= Date.now()) throw new Error('Gateway token expired');
66
  if (!grant.interactionId || !grant.senderAgentId || !grant.recipientAgentId)
67
    throw new Error('Invalid gateway token');
68
  return grant;
69
}