aap-agent-core 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +92 -0
- package/crypto.js +130 -0
- package/identity.js +144 -0
- package/index.js +34 -0
- package/package.json +29 -0
package/README.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# @aap/core
|
|
2
|
+
|
|
3
|
+
Core cryptographic utilities for Agent Attestation Protocol.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @aap/core
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### Cryptographic Functions
|
|
14
|
+
|
|
15
|
+
```javascript
|
|
16
|
+
import { generateKeyPair, sign, verify, generateNonce, derivePublicId } from '@aap/core';
|
|
17
|
+
|
|
18
|
+
// Generate a new key pair
|
|
19
|
+
const { publicKey, privateKey } = generateKeyPair();
|
|
20
|
+
|
|
21
|
+
// Derive public ID
|
|
22
|
+
const publicId = derivePublicId(publicKey);
|
|
23
|
+
|
|
24
|
+
// Sign data
|
|
25
|
+
const signature = sign('Hello, World!', privateKey);
|
|
26
|
+
|
|
27
|
+
// Verify signature
|
|
28
|
+
const isValid = verify('Hello, World!', signature, publicKey);
|
|
29
|
+
|
|
30
|
+
// Generate nonce
|
|
31
|
+
const nonce = generateNonce(); // 32-char hex string
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Identity Management
|
|
35
|
+
|
|
36
|
+
```javascript
|
|
37
|
+
import { Identity } from '@aap/core';
|
|
38
|
+
|
|
39
|
+
// Create identity manager
|
|
40
|
+
const identity = new Identity({
|
|
41
|
+
storagePath: '/path/to/identity.json' // optional
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Initialize (loads or generates)
|
|
45
|
+
const publicInfo = identity.init();
|
|
46
|
+
console.log(publicInfo.publicId);
|
|
47
|
+
|
|
48
|
+
// Sign with identity
|
|
49
|
+
const signature = identity.sign('data to sign');
|
|
50
|
+
|
|
51
|
+
// Get public info (safe to share)
|
|
52
|
+
const pub = identity.getPublic();
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Constants
|
|
56
|
+
|
|
57
|
+
```javascript
|
|
58
|
+
import {
|
|
59
|
+
PROTOCOL_VERSION,
|
|
60
|
+
DEFAULT_CHALLENGE_EXPIRY_MS,
|
|
61
|
+
DEFAULT_MAX_RESPONSE_TIME_MS,
|
|
62
|
+
CHALLENGE_TYPES
|
|
63
|
+
} from '@aap/core';
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## API Reference
|
|
67
|
+
|
|
68
|
+
### Crypto Functions
|
|
69
|
+
|
|
70
|
+
| Function | Description |
|
|
71
|
+
|----------|-------------|
|
|
72
|
+
| `generateKeyPair()` | Generate secp256k1 key pair |
|
|
73
|
+
| `derivePublicId(publicKey)` | Derive 20-char hex ID from public key |
|
|
74
|
+
| `sign(data, privateKey)` | Sign data, return Base64 signature |
|
|
75
|
+
| `verify(data, signature, publicKey)` | Verify signature |
|
|
76
|
+
| `generateNonce(bytes?)` | Generate random hex nonce |
|
|
77
|
+
| `safeCompare(a, b)` | Timing-safe string comparison |
|
|
78
|
+
| `createProofData({...})` | Create canonical proof JSON |
|
|
79
|
+
|
|
80
|
+
### Identity Class
|
|
81
|
+
|
|
82
|
+
| Method | Description |
|
|
83
|
+
|--------|-------------|
|
|
84
|
+
| `init()` | Initialize identity (load/generate) |
|
|
85
|
+
| `getPublic()` | Get public identity info |
|
|
86
|
+
| `sign(data)` | Sign data with identity key |
|
|
87
|
+
| `exists()` | Check if identity file exists |
|
|
88
|
+
| `Identity.verify(...)` | Static: verify any signature |
|
|
89
|
+
|
|
90
|
+
## License
|
|
91
|
+
|
|
92
|
+
MIT
|
package/crypto.js
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @aap/core - Cryptographic utilities
|
|
3
|
+
*
|
|
4
|
+
* Core cryptographic functions for Agent Attestation Protocol.
|
|
5
|
+
* Uses secp256k1 (same as Bitcoin/Ethereum) for compatibility.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
generateKeyPairSync,
|
|
10
|
+
createSign,
|
|
11
|
+
createVerify,
|
|
12
|
+
createHash,
|
|
13
|
+
randomBytes,
|
|
14
|
+
timingSafeEqual
|
|
15
|
+
} from 'node:crypto';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Generate a new secp256k1 key pair
|
|
19
|
+
* @returns {Object} { publicKey, privateKey } in PEM format
|
|
20
|
+
*/
|
|
21
|
+
export function generateKeyPair() {
|
|
22
|
+
const { publicKey, privateKey } = generateKeyPairSync('ec', {
|
|
23
|
+
namedCurve: 'secp256k1',
|
|
24
|
+
publicKeyEncoding: {
|
|
25
|
+
type: 'spki',
|
|
26
|
+
format: 'pem'
|
|
27
|
+
},
|
|
28
|
+
privateKeyEncoding: {
|
|
29
|
+
type: 'pkcs8',
|
|
30
|
+
format: 'pem'
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
return { publicKey, privateKey };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Derive a short public ID from a public key
|
|
39
|
+
* @param {string} publicKey - PEM-encoded public key
|
|
40
|
+
* @returns {string} 20-character hex identifier
|
|
41
|
+
*/
|
|
42
|
+
export function derivePublicId(publicKey) {
|
|
43
|
+
const hash = createHash('sha256').update(publicKey).digest('hex');
|
|
44
|
+
return hash.slice(0, 20);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Sign data with a private key
|
|
49
|
+
* @param {string} data - Data to sign
|
|
50
|
+
* @param {string} privateKey - PEM-encoded private key
|
|
51
|
+
* @returns {string} Base64-encoded signature
|
|
52
|
+
*/
|
|
53
|
+
export function sign(data, privateKey) {
|
|
54
|
+
const signer = createSign('SHA256');
|
|
55
|
+
signer.update(data);
|
|
56
|
+
signer.end();
|
|
57
|
+
return signer.sign(privateKey, 'base64');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Verify a signature
|
|
62
|
+
* @param {string} data - Original data
|
|
63
|
+
* @param {string} signature - Base64-encoded signature
|
|
64
|
+
* @param {string} publicKey - PEM-encoded public key
|
|
65
|
+
* @returns {boolean} True if valid
|
|
66
|
+
*/
|
|
67
|
+
export function verify(data, signature, publicKey) {
|
|
68
|
+
try {
|
|
69
|
+
const verifier = createVerify('SHA256');
|
|
70
|
+
verifier.update(data);
|
|
71
|
+
verifier.end();
|
|
72
|
+
return verifier.verify(publicKey, signature, 'base64');
|
|
73
|
+
} catch (error) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Generate a cryptographically secure nonce
|
|
80
|
+
* @param {number} [bytes=16] - Number of random bytes
|
|
81
|
+
* @returns {string} Hex-encoded nonce
|
|
82
|
+
*/
|
|
83
|
+
export function generateNonce(bytes = 16) {
|
|
84
|
+
return randomBytes(bytes).toString('hex');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Timing-safe comparison of two strings
|
|
89
|
+
* Prevents timing attacks on token comparison
|
|
90
|
+
* @param {string} a - First string
|
|
91
|
+
* @param {string} b - Second string
|
|
92
|
+
* @returns {boolean} True if equal
|
|
93
|
+
*/
|
|
94
|
+
export function safeCompare(a, b) {
|
|
95
|
+
if (typeof a !== 'string' || typeof b !== 'string') {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const bufA = Buffer.from(a);
|
|
100
|
+
const bufB = Buffer.from(b);
|
|
101
|
+
|
|
102
|
+
if (bufA.length !== bufB.length) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return timingSafeEqual(bufA, bufB);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Create the canonical proof data object for signing/verification
|
|
111
|
+
* @param {Object} params
|
|
112
|
+
* @param {string} params.nonce - Challenge nonce
|
|
113
|
+
* @param {string} params.solution - Agent's solution
|
|
114
|
+
* @param {string} params.publicId - Agent's public ID
|
|
115
|
+
* @param {number} params.timestamp - Unix timestamp in ms
|
|
116
|
+
* @returns {string} JSON string for signing
|
|
117
|
+
*/
|
|
118
|
+
export function createProofData({ nonce, solution, publicId, timestamp }) {
|
|
119
|
+
return JSON.stringify({ nonce, solution, publicId, timestamp });
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export default {
|
|
123
|
+
generateKeyPair,
|
|
124
|
+
derivePublicId,
|
|
125
|
+
sign,
|
|
126
|
+
verify,
|
|
127
|
+
generateNonce,
|
|
128
|
+
safeCompare,
|
|
129
|
+
createProofData
|
|
130
|
+
};
|
package/identity.js
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @aap/core - Identity Management
|
|
3
|
+
*
|
|
4
|
+
* Manages agent identity (key generation, storage, signing).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
8
|
+
import { homedir } from 'node:os';
|
|
9
|
+
import { join, dirname } from 'node:path';
|
|
10
|
+
import { generateKeyPair, derivePublicId, sign as cryptoSign, verify as cryptoVerify } from './crypto.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Identity manager class
|
|
14
|
+
*/
|
|
15
|
+
export class Identity {
|
|
16
|
+
constructor(options = {}) {
|
|
17
|
+
this.storagePath = options.storagePath || join(homedir(), '.aap', 'identity.json');
|
|
18
|
+
this.identity = null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Initialize identity (load existing or generate new)
|
|
23
|
+
* @returns {Object} Public identity info
|
|
24
|
+
*/
|
|
25
|
+
init() {
|
|
26
|
+
if (this.identity) {
|
|
27
|
+
return this.getPublic();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Ensure directory exists
|
|
31
|
+
const dir = dirname(this.storagePath);
|
|
32
|
+
if (!existsSync(dir)) {
|
|
33
|
+
mkdirSync(dir, { recursive: true });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Load existing identity
|
|
37
|
+
if (existsSync(this.storagePath)) {
|
|
38
|
+
try {
|
|
39
|
+
const data = readFileSync(this.storagePath, 'utf8');
|
|
40
|
+
this.identity = JSON.parse(data);
|
|
41
|
+
return this.getPublic();
|
|
42
|
+
} catch (error) {
|
|
43
|
+
console.error('[AAP] Failed to load identity:', error.message);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Generate new identity
|
|
48
|
+
const { publicKey, privateKey } = generateKeyPair();
|
|
49
|
+
const publicId = derivePublicId(publicKey);
|
|
50
|
+
|
|
51
|
+
this.identity = {
|
|
52
|
+
publicKey,
|
|
53
|
+
privateKey,
|
|
54
|
+
publicId,
|
|
55
|
+
createdAt: new Date().toISOString(),
|
|
56
|
+
protocol: 'AAP',
|
|
57
|
+
version: '1.0.0'
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// Save to file with secure permissions
|
|
61
|
+
writeFileSync(this.storagePath, JSON.stringify(this.identity, null, 2), {
|
|
62
|
+
mode: 0o600
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
return this.getPublic();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Get public identity (safe to share)
|
|
70
|
+
* @returns {Object} Public identity without private key
|
|
71
|
+
*/
|
|
72
|
+
getPublic() {
|
|
73
|
+
if (!this.identity) {
|
|
74
|
+
this.init();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
publicKey: this.identity.publicKey,
|
|
79
|
+
publicId: this.identity.publicId,
|
|
80
|
+
createdAt: this.identity.createdAt,
|
|
81
|
+
protocol: this.identity.protocol,
|
|
82
|
+
version: this.identity.version
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Sign data with the identity's private key
|
|
88
|
+
* @param {string} data - Data to sign
|
|
89
|
+
* @returns {string} Base64-encoded signature
|
|
90
|
+
*/
|
|
91
|
+
sign(data) {
|
|
92
|
+
if (!this.identity) {
|
|
93
|
+
this.init();
|
|
94
|
+
}
|
|
95
|
+
return cryptoSign(data, this.identity.privateKey);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Verify a signature (static method for verifying others)
|
|
100
|
+
* @param {string} data - Original data
|
|
101
|
+
* @param {string} signature - Signature to verify
|
|
102
|
+
* @param {string} publicKey - Public key of signer
|
|
103
|
+
* @returns {boolean} True if valid
|
|
104
|
+
*/
|
|
105
|
+
static verify(data, signature, publicKey) {
|
|
106
|
+
return cryptoVerify(data, signature, publicKey);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Check if identity exists
|
|
111
|
+
* @returns {boolean}
|
|
112
|
+
*/
|
|
113
|
+
exists() {
|
|
114
|
+
return existsSync(this.storagePath);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Delete identity (for testing)
|
|
119
|
+
*/
|
|
120
|
+
delete() {
|
|
121
|
+
if (existsSync(this.storagePath)) {
|
|
122
|
+
const { unlinkSync } = require('node:fs');
|
|
123
|
+
unlinkSync(this.storagePath);
|
|
124
|
+
}
|
|
125
|
+
this.identity = null;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Default instance
|
|
130
|
+
let defaultIdentity = null;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Get or create the default identity instance
|
|
134
|
+
* @param {Object} [options] - Options for new instance
|
|
135
|
+
* @returns {Identity}
|
|
136
|
+
*/
|
|
137
|
+
export function getDefaultIdentity(options) {
|
|
138
|
+
if (!defaultIdentity) {
|
|
139
|
+
defaultIdentity = new Identity(options);
|
|
140
|
+
}
|
|
141
|
+
return defaultIdentity;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export default { Identity, getDefaultIdentity };
|
package/index.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @aap/core
|
|
3
|
+
*
|
|
4
|
+
* Core utilities for Agent Attestation Protocol.
|
|
5
|
+
* Provides cryptographic primitives and identity management.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export * from './crypto.js';
|
|
9
|
+
export * from './identity.js';
|
|
10
|
+
|
|
11
|
+
// Re-export defaults
|
|
12
|
+
import crypto from './crypto.js';
|
|
13
|
+
import identity from './identity.js';
|
|
14
|
+
|
|
15
|
+
export { crypto, identity };
|
|
16
|
+
|
|
17
|
+
// Protocol constants
|
|
18
|
+
export const PROTOCOL_VERSION = '1.0.0';
|
|
19
|
+
export const DEFAULT_CHALLENGE_EXPIRY_MS = 30000;
|
|
20
|
+
export const DEFAULT_MAX_RESPONSE_TIME_MS = 1500;
|
|
21
|
+
export const NONCE_BYTES = 16;
|
|
22
|
+
|
|
23
|
+
// Challenge types
|
|
24
|
+
export const CHALLENGE_TYPES = ['poem', 'math', 'reverse', 'wordplay', 'description'];
|
|
25
|
+
|
|
26
|
+
export default {
|
|
27
|
+
...crypto,
|
|
28
|
+
...identity,
|
|
29
|
+
PROTOCOL_VERSION,
|
|
30
|
+
DEFAULT_CHALLENGE_EXPIRY_MS,
|
|
31
|
+
DEFAULT_MAX_RESPONSE_TIME_MS,
|
|
32
|
+
NONCE_BYTES,
|
|
33
|
+
CHALLENGE_TYPES
|
|
34
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "aap-agent-core",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Core cryptographic utilities for Agent Attestation Protocol",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"types": "index.d.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./index.js",
|
|
10
|
+
"./identity": "./identity.js",
|
|
11
|
+
"./crypto": "./crypto.js"
|
|
12
|
+
},
|
|
13
|
+
"files": ["*.js", "README.md"],
|
|
14
|
+
"keywords": ["aap", "agent", "attestation", "crypto", "secp256k1", "ai", "verification"],
|
|
15
|
+
"author": "ira-hash",
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "https://github.com/ira-hash/agent-attestation-protocol.git",
|
|
20
|
+
"directory": "packages/core"
|
|
21
|
+
},
|
|
22
|
+
"homepage": "https://github.com/ira-hash/agent-attestation-protocol#readme",
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/ira-hash/agent-attestation-protocol/issues"
|
|
25
|
+
},
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=18.0.0"
|
|
28
|
+
}
|
|
29
|
+
}
|