-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsetup-entity.js
More file actions
101 lines (89 loc) · 4.03 KB
/
Copy pathsetup-entity.js
File metadata and controls
101 lines (89 loc) · 4.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
const crypto = require('crypto');
const https = require('https');
// ─────────────────────────────────────────────────────────────
// Circle Entity Secret Generator
//
// Generates a 32-byte entity secret, encrypts it with Circle's
// RSA public key (RSA-OAEP + SHA-256), and outputs both the
// raw secret (for .env) and the ciphertext (for Circle Console).
//
// Usage: node setup-entity.js
// ─────────────────────────────────────────────────────────────
const CIRCLE_API_KEY = process.env.CIRCLE_API_KEY || 'TEST_API_KEY:f9364513b7403813dfbd1fc51bd2ba6a:df05b81515a4707f8da48ea10f43cc79';
function fetchPublicKey() {
return new Promise((resolve, reject) => {
const options = {
hostname: 'api.circle.com',
path: '/v1/w3s/config/entity/publicKey',
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + CIRCLE_API_KEY,
},
};
const req = https.request(options, (res) => {
let data = '';
res.on('data', (chunk) => { data += chunk; });
res.on('end', () => {
if (res.statusCode !== 200) {
reject(new Error('Circle API returned ' + res.statusCode + ': ' + data));
return;
}
try {
const parsed = JSON.parse(data);
const publicKey = parsed.data && parsed.data.publicKey;
if (!publicKey) {
reject(new Error('No publicKey in response'));
return;
}
resolve(publicKey);
} catch (e) {
reject(new Error('Failed to parse response: ' + e.message));
}
});
});
req.on('error', reject);
req.end();
});
}
async function main() {
console.log('============================================================');
console.log(' Circle Entity Secret Setup');
console.log('============================================================\n');
// Step 1 — Fetch RSA public key
console.log('[1/4] Fetching RSA public key from Circle API...');
const publicKeyPem = await fetchPublicKey();
console.log(' Done.\n');
// Step 2 — Generate 32-byte random entity secret
const entitySecret = crypto.randomBytes(32).toString('hex');
console.log('[2/4] Entity Secret (64 hex chars):');
console.log(' ' + entitySecret + '\n');
// Step 3 — Encrypt with RSA-OAEP + SHA-256
const encrypted = crypto.publicEncrypt(
{
key: publicKeyPem,
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
oaepHash: 'sha256',
},
Buffer.from(entitySecret, 'hex'),
);
// Step 4 — Base64 encode
const ciphertext = encrypted.toString('base64');
console.log('[3/4] Ciphertext (base64):');
console.log(' ' + ciphertext + '\n');
console.log('[4/4] Ciphertext length: ' + ciphertext.length + ' characters\n');
console.log('============================================================');
console.log(' NEXT STEPS');
console.log('============================================================\n');
console.log('1. Add to .env:');
console.log(' ENTITY_SECRET=' + entitySecret + '\n');
console.log('2. Register the ciphertext at:');
console.log(' https://console.circle.com > Programmable Wallets > Configurator');
console.log(' Or via API: POST /v1/w3s/config/entity\n');
console.log('WARNING: Save the entity secret securely - it cannot be recovered!');
console.log('============================================================');
}
main().catch((err) => {
console.error('Error:', err.message);
process.exit(1);
});