@auths-dev/sdk 0.0.1 → 0.1.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/Cargo.toml +45 -0
- package/README.md +163 -4
- package/__test__/client.spec.ts +78 -0
- package/__test__/exports.spec.ts +57 -0
- package/__test__/integration.spec.ts +407 -0
- package/__test__/policy.spec.ts +202 -0
- package/__test__/verify.spec.ts +88 -0
- package/build.rs +5 -0
- package/index.d.ts +259 -0
- package/index.js +622 -1
- package/lib/artifacts.ts +124 -0
- package/lib/attestations.ts +126 -0
- package/lib/audit.ts +189 -0
- package/lib/client.ts +293 -0
- package/lib/commits.ts +70 -0
- package/lib/devices.ts +178 -0
- package/lib/errors.ts +306 -0
- package/lib/identity.ts +280 -0
- package/lib/index.ts +125 -0
- package/lib/native.ts +255 -0
- package/lib/org.ts +235 -0
- package/lib/pairing.ts +271 -0
- package/lib/policy.ts +669 -0
- package/lib/signing.ts +204 -0
- package/lib/trust.ts +152 -0
- package/lib/types.ts +179 -0
- package/lib/verify.ts +241 -0
- package/lib/witness.ts +91 -0
- package/npm/darwin-arm64/README.md +3 -0
- package/npm/darwin-arm64/package.json +23 -0
- package/npm/linux-arm64-gnu/README.md +3 -0
- package/npm/linux-arm64-gnu/package.json +26 -0
- package/npm/linux-x64-gnu/README.md +3 -0
- package/npm/linux-x64-gnu/package.json +26 -0
- package/npm/win32-arm64-msvc/README.md +3 -0
- package/npm/win32-arm64-msvc/package.json +23 -0
- package/npm/win32-x64-msvc/README.md +3 -0
- package/npm/win32-x64-msvc/package.json +23 -0
- package/package.json +51 -16
- package/src/artifact.rs +217 -0
- package/src/attestation_query.rs +104 -0
- package/src/audit.rs +128 -0
- package/src/commit_sign.rs +63 -0
- package/src/device.rs +212 -0
- package/src/diagnostics.rs +106 -0
- package/src/error.rs +5 -0
- package/src/helpers.rs +60 -0
- package/src/identity.rs +467 -0
- package/src/lib.rs +26 -0
- package/src/org.rs +430 -0
- package/src/pairing.rs +454 -0
- package/src/policy.rs +147 -0
- package/src/sign.rs +215 -0
- package/src/trust.rs +189 -0
- package/src/types.rs +205 -0
- package/src/verify.rs +447 -0
- package/src/witness.rs +138 -0
- package/tsconfig.json +19 -0
- package/typedoc.json +18 -0
- package/vitest.config.ts +12 -0
package/src/identity.rs
ADDED
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
use std::path::PathBuf;
|
|
2
|
+
use std::sync::Arc;
|
|
3
|
+
|
|
4
|
+
use auths_core::crypto::signer::encrypt_keypair;
|
|
5
|
+
use auths_core::signing::PrefilledPassphraseProvider;
|
|
6
|
+
use auths_core::storage::keychain::{KeyAlias, KeyRole, get_platform_keychain_with_config};
|
|
7
|
+
use auths_id::identity::helpers::{encode_seed_as_pkcs8, extract_seed_bytes};
|
|
8
|
+
use auths_id::identity::initialize::initialize_registry_identity;
|
|
9
|
+
use auths_id::storage::attestation::AttestationSource;
|
|
10
|
+
use auths_sdk::context::AuthsContext;
|
|
11
|
+
use auths_sdk::device::link_device;
|
|
12
|
+
use auths_sdk::types::{DeviceLinkConfig, IdentityRotationConfig};
|
|
13
|
+
use auths_sdk::workflows::rotation::rotate_identity;
|
|
14
|
+
use auths_storage::git::{
|
|
15
|
+
GitRegistryBackend, RegistryAttestationStorage, RegistryConfig, RegistryIdentityStorage,
|
|
16
|
+
};
|
|
17
|
+
use auths_verifier::clock::SystemClock;
|
|
18
|
+
use auths_verifier::core::Capability;
|
|
19
|
+
use auths_verifier::types::DeviceDID;
|
|
20
|
+
use napi_derive::napi;
|
|
21
|
+
use ring::rand::SystemRandom;
|
|
22
|
+
use ring::signature::{Ed25519KeyPair, KeyPair};
|
|
23
|
+
|
|
24
|
+
use crate::error::format_error;
|
|
25
|
+
use crate::helpers::{make_env_config, resolve_key_alias, resolve_passphrase};
|
|
26
|
+
use crate::types::{
|
|
27
|
+
NapiAgentIdentityBundle, NapiDelegatedAgentBundle, NapiIdentityResult, NapiRotationResult,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
fn init_backend(repo: &PathBuf) -> napi::Result<Arc<GitRegistryBackend>> {
|
|
31
|
+
let config = RegistryConfig::single_tenant(repo);
|
|
32
|
+
let backend = GitRegistryBackend::from_config_unchecked(config);
|
|
33
|
+
backend.init_if_needed().map_err(|e| {
|
|
34
|
+
format_error(
|
|
35
|
+
"AUTHS_REGISTRY_ERROR",
|
|
36
|
+
format!("Failed to initialize registry: {e}"),
|
|
37
|
+
)
|
|
38
|
+
})?;
|
|
39
|
+
Ok(Arc::new(backend))
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
fn open_backend(repo: &PathBuf) -> napi::Result<Arc<GitRegistryBackend>> {
|
|
43
|
+
let config = RegistryConfig::single_tenant(repo);
|
|
44
|
+
let backend = GitRegistryBackend::open_existing(config).map_err(|e| {
|
|
45
|
+
format_error(
|
|
46
|
+
"AUTHS_REGISTRY_ERROR",
|
|
47
|
+
format!("Failed to open registry: {e}"),
|
|
48
|
+
)
|
|
49
|
+
})?;
|
|
50
|
+
Ok(Arc::new(backend))
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
#[napi]
|
|
54
|
+
pub fn create_identity(
|
|
55
|
+
key_alias: String,
|
|
56
|
+
repo_path: String,
|
|
57
|
+
passphrase: Option<String>,
|
|
58
|
+
) -> napi::Result<NapiIdentityResult> {
|
|
59
|
+
let passphrase_str = resolve_passphrase(passphrase);
|
|
60
|
+
let env_config = make_env_config(&passphrase_str, &repo_path);
|
|
61
|
+
let alias = KeyAlias::new(&key_alias)
|
|
62
|
+
.map_err(|e| format_error("AUTHS_KEY_NOT_FOUND", format!("Invalid key alias: {e}")))?;
|
|
63
|
+
let provider = PrefilledPassphraseProvider::new(&passphrase_str);
|
|
64
|
+
|
|
65
|
+
let repo = PathBuf::from(shellexpand::tilde(&repo_path).as_ref());
|
|
66
|
+
let backend = init_backend(&repo)?;
|
|
67
|
+
|
|
68
|
+
let keychain = get_platform_keychain_with_config(&env_config)
|
|
69
|
+
.map_err(|e| format_error("AUTHS_KEYCHAIN_ERROR", format!("Keychain error: {e}")))?;
|
|
70
|
+
|
|
71
|
+
let (identity_did, result_alias) =
|
|
72
|
+
initialize_registry_identity(backend, &alias, &provider, keychain.as_ref(), None).map_err(
|
|
73
|
+
|e| {
|
|
74
|
+
format_error(
|
|
75
|
+
"AUTHS_IDENTITY_ERROR",
|
|
76
|
+
format!("Identity creation failed: {e}"),
|
|
77
|
+
)
|
|
78
|
+
},
|
|
79
|
+
)?;
|
|
80
|
+
|
|
81
|
+
let pub_bytes = auths_core::storage::keychain::extract_public_key_bytes(
|
|
82
|
+
keychain.as_ref(),
|
|
83
|
+
&result_alias,
|
|
84
|
+
&provider,
|
|
85
|
+
)
|
|
86
|
+
.map_err(|e| {
|
|
87
|
+
format_error(
|
|
88
|
+
"AUTHS_CRYPTO_ERROR",
|
|
89
|
+
format!("Public key extraction failed: {e}"),
|
|
90
|
+
)
|
|
91
|
+
})?;
|
|
92
|
+
|
|
93
|
+
Ok(NapiIdentityResult {
|
|
94
|
+
did: identity_did.to_string(),
|
|
95
|
+
key_alias: result_alias.to_string(),
|
|
96
|
+
public_key_hex: hex::encode(pub_bytes),
|
|
97
|
+
})
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
#[napi]
|
|
101
|
+
pub fn create_agent_identity(
|
|
102
|
+
agent_name: String,
|
|
103
|
+
capabilities: Vec<String>,
|
|
104
|
+
repo_path: String,
|
|
105
|
+
passphrase: Option<String>,
|
|
106
|
+
) -> napi::Result<NapiAgentIdentityBundle> {
|
|
107
|
+
let passphrase_str = resolve_passphrase(passphrase);
|
|
108
|
+
let env_config = make_env_config(&passphrase_str, &repo_path);
|
|
109
|
+
#[allow(clippy::disallowed_methods)]
|
|
110
|
+
// INVARIANT: agent_name is user-provided, format produces valid alias
|
|
111
|
+
let alias = KeyAlias::new_unchecked(format!("{}-agent", agent_name));
|
|
112
|
+
let provider = PrefilledPassphraseProvider::new(&passphrase_str);
|
|
113
|
+
let clock = Arc::new(SystemClock);
|
|
114
|
+
|
|
115
|
+
let repo = PathBuf::from(shellexpand::tilde(&repo_path).as_ref());
|
|
116
|
+
let backend = init_backend(&repo)?;
|
|
117
|
+
|
|
118
|
+
let keychain = get_platform_keychain_with_config(&env_config)
|
|
119
|
+
.map_err(|e| format_error("AUTHS_KEYCHAIN_ERROR", format!("Keychain error: {e}")))?;
|
|
120
|
+
|
|
121
|
+
let parsed_caps: Vec<Capability> = capabilities
|
|
122
|
+
.iter()
|
|
123
|
+
.map(|c| {
|
|
124
|
+
Capability::parse(c).map_err(|e| {
|
|
125
|
+
format_error(
|
|
126
|
+
"AUTHS_INVALID_INPUT",
|
|
127
|
+
format!("Invalid capability '{c}': {e}"),
|
|
128
|
+
)
|
|
129
|
+
})
|
|
130
|
+
})
|
|
131
|
+
.collect::<napi::Result<Vec<_>>>()?;
|
|
132
|
+
|
|
133
|
+
let (identity_did, result_alias) =
|
|
134
|
+
initialize_registry_identity(backend.clone(), &alias, &provider, keychain.as_ref(), None)
|
|
135
|
+
.map_err(|e| {
|
|
136
|
+
format_error(
|
|
137
|
+
"AUTHS_IDENTITY_ERROR",
|
|
138
|
+
format!("Agent identity creation failed: {e}"),
|
|
139
|
+
)
|
|
140
|
+
})?;
|
|
141
|
+
|
|
142
|
+
let pub_bytes = auths_core::storage::keychain::extract_public_key_bytes(
|
|
143
|
+
keychain.as_ref(),
|
|
144
|
+
&result_alias,
|
|
145
|
+
&provider,
|
|
146
|
+
)
|
|
147
|
+
.map_err(|e| {
|
|
148
|
+
format_error(
|
|
149
|
+
"AUTHS_CRYPTO_ERROR",
|
|
150
|
+
format!("Public key extraction failed: {e}"),
|
|
151
|
+
)
|
|
152
|
+
})?;
|
|
153
|
+
|
|
154
|
+
// Use link_device to produce a proper signed self-attestation,
|
|
155
|
+
// following the same pattern as delegate_agent.
|
|
156
|
+
let link_config = DeviceLinkConfig {
|
|
157
|
+
identity_key_alias: result_alias.clone(),
|
|
158
|
+
device_key_alias: Some(result_alias.clone()),
|
|
159
|
+
device_did: None,
|
|
160
|
+
capabilities: parsed_caps,
|
|
161
|
+
expires_in: None,
|
|
162
|
+
note: Some(format!("Agent: {}", agent_name)),
|
|
163
|
+
payload: None,
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
let provider = Arc::new(provider);
|
|
167
|
+
let keychain: Arc<dyn auths_core::storage::keychain::KeyStorage + Send + Sync> =
|
|
168
|
+
Arc::from(keychain);
|
|
169
|
+
let identity_storage = Arc::new(RegistryIdentityStorage::new(&repo));
|
|
170
|
+
let attestation_storage = Arc::new(RegistryAttestationStorage::new(&repo));
|
|
171
|
+
|
|
172
|
+
let ctx = AuthsContext::builder()
|
|
173
|
+
.registry(backend)
|
|
174
|
+
.key_storage(keychain)
|
|
175
|
+
.clock(clock.clone())
|
|
176
|
+
.identity_storage(identity_storage)
|
|
177
|
+
.attestation_sink(attestation_storage.clone())
|
|
178
|
+
.attestation_source(attestation_storage.clone())
|
|
179
|
+
.passphrase_provider(provider)
|
|
180
|
+
.build();
|
|
181
|
+
|
|
182
|
+
let result = link_device(link_config, &ctx, clock.as_ref()).map_err(|e| {
|
|
183
|
+
format_error(
|
|
184
|
+
"AUTHS_IDENTITY_ERROR",
|
|
185
|
+
format!("Agent self-attestation failed: {e}"),
|
|
186
|
+
)
|
|
187
|
+
})?;
|
|
188
|
+
|
|
189
|
+
#[allow(clippy::disallowed_methods)] // INVARIANT: device_did from SDK setup result
|
|
190
|
+
let device_did = DeviceDID::new_unchecked(result.device_did.to_string());
|
|
191
|
+
let attestations = attestation_storage
|
|
192
|
+
.load_attestations_for_device(&device_did)
|
|
193
|
+
.map_err(|e| {
|
|
194
|
+
format_error(
|
|
195
|
+
"AUTHS_REGISTRY_ERROR",
|
|
196
|
+
format!("Failed to load attestation: {e}"),
|
|
197
|
+
)
|
|
198
|
+
})?;
|
|
199
|
+
|
|
200
|
+
let attestation = attestations.last().ok_or_else(|| {
|
|
201
|
+
format_error(
|
|
202
|
+
"AUTHS_REGISTRY_ERROR",
|
|
203
|
+
"No attestation found after self-attestation",
|
|
204
|
+
)
|
|
205
|
+
})?;
|
|
206
|
+
|
|
207
|
+
let attestation_json = serde_json::to_string(attestation).map_err(|e| {
|
|
208
|
+
format_error(
|
|
209
|
+
"AUTHS_SERIALIZATION_ERROR",
|
|
210
|
+
format!("Serialization failed: {e}"),
|
|
211
|
+
)
|
|
212
|
+
})?;
|
|
213
|
+
|
|
214
|
+
Ok(NapiAgentIdentityBundle {
|
|
215
|
+
agent_did: identity_did.to_string(),
|
|
216
|
+
key_alias: result_alias.to_string(),
|
|
217
|
+
attestation_json,
|
|
218
|
+
public_key_hex: hex::encode(pub_bytes),
|
|
219
|
+
repo_path: Some(repo.to_string_lossy().to_string()),
|
|
220
|
+
})
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
#[napi]
|
|
224
|
+
pub fn delegate_agent(
|
|
225
|
+
agent_name: String,
|
|
226
|
+
capabilities: Vec<String>,
|
|
227
|
+
parent_repo_path: String,
|
|
228
|
+
passphrase: Option<String>,
|
|
229
|
+
expires_in: Option<i64>,
|
|
230
|
+
identity_did: Option<String>,
|
|
231
|
+
) -> napi::Result<NapiDelegatedAgentBundle> {
|
|
232
|
+
let passphrase_str = resolve_passphrase(passphrase);
|
|
233
|
+
let env_config = make_env_config(&passphrase_str, &parent_repo_path);
|
|
234
|
+
let provider = Arc::new(PrefilledPassphraseProvider::new(&passphrase_str));
|
|
235
|
+
let clock = Arc::new(SystemClock);
|
|
236
|
+
|
|
237
|
+
let repo = PathBuf::from(shellexpand::tilde(&parent_repo_path).as_ref());
|
|
238
|
+
let backend = open_backend(&repo)?;
|
|
239
|
+
|
|
240
|
+
let keychain = get_platform_keychain_with_config(&env_config)
|
|
241
|
+
.map_err(|e| format_error("AUTHS_KEYCHAIN_ERROR", format!("Keychain error: {e}")))?;
|
|
242
|
+
|
|
243
|
+
let parent_alias = if let Some(ref did) = identity_did {
|
|
244
|
+
resolve_key_alias(did, keychain.as_ref())?
|
|
245
|
+
} else {
|
|
246
|
+
let aliases = keychain
|
|
247
|
+
.list_aliases()
|
|
248
|
+
.map_err(|e| format_error("AUTHS_KEYCHAIN_ERROR", format!("Keychain error: {e}")))?;
|
|
249
|
+
aliases
|
|
250
|
+
.into_iter()
|
|
251
|
+
.find(|a| !a.as_str().contains("--next-"))
|
|
252
|
+
.ok_or_else(|| {
|
|
253
|
+
format_error("AUTHS_KEY_NOT_FOUND", "No identity key found in keychain")
|
|
254
|
+
})?
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
#[allow(clippy::disallowed_methods)]
|
|
258
|
+
// INVARIANT: agent_name is user-provided, format produces valid alias
|
|
259
|
+
let agent_alias = KeyAlias::new_unchecked(format!("{}-agent", agent_name));
|
|
260
|
+
let rng = SystemRandom::new();
|
|
261
|
+
let pkcs8 = Ed25519KeyPair::generate_pkcs8(&rng)
|
|
262
|
+
.map_err(|e| format_error("AUTHS_CRYPTO_ERROR", format!("Key generation failed: {e}")))?;
|
|
263
|
+
let keypair = Ed25519KeyPair::from_pkcs8(pkcs8.as_ref())
|
|
264
|
+
.map_err(|e| format_error("AUTHS_CRYPTO_ERROR", format!("Key parsing failed: {e}")))?;
|
|
265
|
+
let agent_pubkey = keypair.public_key().as_ref().to_vec();
|
|
266
|
+
|
|
267
|
+
let (parent_did, _, _) = keychain
|
|
268
|
+
.load_key(&parent_alias)
|
|
269
|
+
.map_err(|e| format_error("AUTHS_KEY_NOT_FOUND", format!("Key load failed: {e}")))?;
|
|
270
|
+
|
|
271
|
+
let seed = extract_seed_bytes(pkcs8.as_ref())
|
|
272
|
+
.map_err(|e| format_error("AUTHS_CRYPTO_ERROR", format!("Seed extraction failed: {e}")))?;
|
|
273
|
+
let seed_pkcs8 = encode_seed_as_pkcs8(seed)
|
|
274
|
+
.map_err(|e| format_error("AUTHS_CRYPTO_ERROR", format!("PKCS8 encoding failed: {e}")))?;
|
|
275
|
+
let encrypted = encrypt_keypair(&seed_pkcs8, &passphrase_str)
|
|
276
|
+
.map_err(|e| format_error("AUTHS_CRYPTO_ERROR", format!("Key encryption failed: {e}")))?;
|
|
277
|
+
keychain
|
|
278
|
+
.store_key(
|
|
279
|
+
&agent_alias,
|
|
280
|
+
&parent_did,
|
|
281
|
+
KeyRole::DelegatedAgent,
|
|
282
|
+
&encrypted,
|
|
283
|
+
)
|
|
284
|
+
.map_err(|e| format_error("AUTHS_KEYCHAIN_ERROR", format!("Key storage failed: {e}")))?;
|
|
285
|
+
|
|
286
|
+
let parsed_caps: Vec<Capability> = capabilities
|
|
287
|
+
.iter()
|
|
288
|
+
.map(|c| {
|
|
289
|
+
Capability::parse(c).map_err(|e| {
|
|
290
|
+
format_error(
|
|
291
|
+
"AUTHS_INVALID_INPUT",
|
|
292
|
+
format!("Invalid capability '{c}': {e}"),
|
|
293
|
+
)
|
|
294
|
+
})
|
|
295
|
+
})
|
|
296
|
+
.collect::<napi::Result<Vec<_>>>()?;
|
|
297
|
+
|
|
298
|
+
let link_config = DeviceLinkConfig {
|
|
299
|
+
identity_key_alias: parent_alias,
|
|
300
|
+
device_key_alias: Some(agent_alias.clone()),
|
|
301
|
+
device_did: None,
|
|
302
|
+
capabilities: parsed_caps,
|
|
303
|
+
expires_in: expires_in.map(|s| s as u64),
|
|
304
|
+
note: Some(format!("Agent: {}", agent_name)),
|
|
305
|
+
payload: None,
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
let keychain: Arc<dyn auths_core::storage::keychain::KeyStorage + Send + Sync> =
|
|
309
|
+
Arc::from(keychain);
|
|
310
|
+
let identity_storage = Arc::new(RegistryIdentityStorage::new(&repo));
|
|
311
|
+
let attestation_storage = Arc::new(RegistryAttestationStorage::new(&repo));
|
|
312
|
+
|
|
313
|
+
let ctx = AuthsContext::builder()
|
|
314
|
+
.registry(backend)
|
|
315
|
+
.key_storage(keychain)
|
|
316
|
+
.clock(clock.clone())
|
|
317
|
+
.identity_storage(identity_storage)
|
|
318
|
+
.attestation_sink(attestation_storage.clone())
|
|
319
|
+
.attestation_source(attestation_storage.clone())
|
|
320
|
+
.passphrase_provider(provider)
|
|
321
|
+
.build();
|
|
322
|
+
|
|
323
|
+
let result = link_device(link_config, &ctx, clock.as_ref()).map_err(|e| {
|
|
324
|
+
format_error(
|
|
325
|
+
"AUTHS_IDENTITY_ERROR",
|
|
326
|
+
format!("Agent provisioning failed: {e}"),
|
|
327
|
+
)
|
|
328
|
+
})?;
|
|
329
|
+
|
|
330
|
+
#[allow(clippy::disallowed_methods)] // INVARIANT: device_did from SDK setup result
|
|
331
|
+
let device_did = DeviceDID::new_unchecked(result.device_did.to_string());
|
|
332
|
+
let attestations = attestation_storage
|
|
333
|
+
.load_attestations_for_device(&device_did)
|
|
334
|
+
.map_err(|e| {
|
|
335
|
+
format_error(
|
|
336
|
+
"AUTHS_REGISTRY_ERROR",
|
|
337
|
+
format!("Failed to load attestation: {e}"),
|
|
338
|
+
)
|
|
339
|
+
})?;
|
|
340
|
+
|
|
341
|
+
let attestation = attestations.last().ok_or_else(|| {
|
|
342
|
+
format_error(
|
|
343
|
+
"AUTHS_REGISTRY_ERROR",
|
|
344
|
+
"No attestation found after provisioning",
|
|
345
|
+
)
|
|
346
|
+
})?;
|
|
347
|
+
|
|
348
|
+
let attestation_json = serde_json::to_string(attestation).map_err(|e| {
|
|
349
|
+
format_error(
|
|
350
|
+
"AUTHS_SERIALIZATION_ERROR",
|
|
351
|
+
format!("Serialization failed: {e}"),
|
|
352
|
+
)
|
|
353
|
+
})?;
|
|
354
|
+
|
|
355
|
+
Ok(NapiDelegatedAgentBundle {
|
|
356
|
+
agent_did: result.device_did.to_string(),
|
|
357
|
+
key_alias: agent_alias.to_string(),
|
|
358
|
+
attestation_json,
|
|
359
|
+
public_key_hex: hex::encode(&agent_pubkey),
|
|
360
|
+
repo_path: Some(repo.to_string_lossy().to_string()),
|
|
361
|
+
})
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
#[napi]
|
|
365
|
+
pub fn rotate_identity_keys(
|
|
366
|
+
repo_path: String,
|
|
367
|
+
identity_key_alias: Option<String>,
|
|
368
|
+
next_key_alias: Option<String>,
|
|
369
|
+
passphrase: Option<String>,
|
|
370
|
+
) -> napi::Result<NapiRotationResult> {
|
|
371
|
+
let passphrase_str = resolve_passphrase(passphrase);
|
|
372
|
+
let env_config = make_env_config(&passphrase_str, &repo_path);
|
|
373
|
+
let provider = Arc::new(PrefilledPassphraseProvider::new(&passphrase_str));
|
|
374
|
+
let clock = Arc::new(SystemClock);
|
|
375
|
+
|
|
376
|
+
let repo = PathBuf::from(shellexpand::tilde(&repo_path).as_ref());
|
|
377
|
+
let backend = open_backend(&repo)?;
|
|
378
|
+
|
|
379
|
+
let keychain = get_platform_keychain_with_config(&env_config)
|
|
380
|
+
.map_err(|e| format_error("AUTHS_KEYCHAIN_ERROR", format!("Keychain error: {e}")))?;
|
|
381
|
+
let keychain: Arc<dyn auths_core::storage::keychain::KeyStorage + Send + Sync> =
|
|
382
|
+
Arc::from(keychain);
|
|
383
|
+
|
|
384
|
+
let identity_storage = Arc::new(RegistryIdentityStorage::new(&repo));
|
|
385
|
+
let attestation_storage = Arc::new(RegistryAttestationStorage::new(&repo));
|
|
386
|
+
|
|
387
|
+
let alias = identity_key_alias
|
|
388
|
+
.as_deref()
|
|
389
|
+
.map(|a| resolve_key_alias(a, keychain.as_ref()))
|
|
390
|
+
.transpose()?;
|
|
391
|
+
|
|
392
|
+
let ctx = AuthsContext::builder()
|
|
393
|
+
.registry(backend)
|
|
394
|
+
.key_storage(keychain)
|
|
395
|
+
.clock(clock.clone())
|
|
396
|
+
.identity_storage(identity_storage)
|
|
397
|
+
.attestation_sink(attestation_storage.clone())
|
|
398
|
+
.attestation_source(attestation_storage)
|
|
399
|
+
.passphrase_provider(provider)
|
|
400
|
+
.build();
|
|
401
|
+
|
|
402
|
+
let next_alias = next_key_alias
|
|
403
|
+
.as_deref()
|
|
404
|
+
.map(|a| {
|
|
405
|
+
KeyAlias::new(a).map_err(|e| {
|
|
406
|
+
format_error(
|
|
407
|
+
"AUTHS_KEY_NOT_FOUND",
|
|
408
|
+
format!("Invalid next key alias: {e}"),
|
|
409
|
+
)
|
|
410
|
+
})
|
|
411
|
+
})
|
|
412
|
+
.transpose()?;
|
|
413
|
+
|
|
414
|
+
let rotation_config = IdentityRotationConfig {
|
|
415
|
+
repo_path: repo,
|
|
416
|
+
identity_key_alias: alias,
|
|
417
|
+
next_key_alias: next_alias,
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
let result = rotate_identity(rotation_config, &ctx, clock.as_ref())
|
|
421
|
+
.map_err(|e| format_error("AUTHS_ROTATION_ERROR", format!("Key rotation failed: {e}")))?;
|
|
422
|
+
|
|
423
|
+
Ok(NapiRotationResult {
|
|
424
|
+
controller_did: result.controller_did.to_string(),
|
|
425
|
+
new_key_fingerprint: result.new_key_fingerprint,
|
|
426
|
+
previous_key_fingerprint: result.previous_key_fingerprint,
|
|
427
|
+
sequence: result.sequence as i64,
|
|
428
|
+
})
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
#[napi]
|
|
432
|
+
pub fn get_identity_public_key(
|
|
433
|
+
identity_did: String,
|
|
434
|
+
repo_path: String,
|
|
435
|
+
passphrase: Option<String>,
|
|
436
|
+
) -> napi::Result<String> {
|
|
437
|
+
let passphrase_str = resolve_passphrase(passphrase);
|
|
438
|
+
let env_config = make_env_config(&passphrase_str, &repo_path);
|
|
439
|
+
let provider = PrefilledPassphraseProvider::new(&passphrase_str);
|
|
440
|
+
|
|
441
|
+
let keychain = get_platform_keychain_with_config(&env_config)
|
|
442
|
+
.map_err(|e| format_error("AUTHS_KEYCHAIN_ERROR", format!("Keychain error: {e}")))?;
|
|
443
|
+
|
|
444
|
+
let did = auths_verifier::types::IdentityDID::parse(&identity_did)
|
|
445
|
+
.map_err(|e| format_error("AUTHS_INVALID_INPUT", e))?;
|
|
446
|
+
let aliases = keychain
|
|
447
|
+
.list_aliases_for_identity_with_role(&did, KeyRole::Primary)
|
|
448
|
+
.map_err(|e| format_error("AUTHS_KEY_NOT_FOUND", format!("Key lookup failed: {e}")))?;
|
|
449
|
+
let alias = aliases.first().ok_or_else(|| {
|
|
450
|
+
format_error(
|
|
451
|
+
"AUTHS_KEY_NOT_FOUND",
|
|
452
|
+
format!("No primary key found for identity '{identity_did}'"),
|
|
453
|
+
)
|
|
454
|
+
})?;
|
|
455
|
+
let pub_bytes = auths_core::storage::keychain::extract_public_key_bytes(
|
|
456
|
+
keychain.as_ref(),
|
|
457
|
+
alias,
|
|
458
|
+
&provider,
|
|
459
|
+
)
|
|
460
|
+
.map_err(|e| {
|
|
461
|
+
format_error(
|
|
462
|
+
"AUTHS_CRYPTO_ERROR",
|
|
463
|
+
format!("Public key extraction failed: {e}"),
|
|
464
|
+
)
|
|
465
|
+
})?;
|
|
466
|
+
Ok(hex::encode(pub_bytes))
|
|
467
|
+
}
|
package/src/lib.rs
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#![deny(clippy::all)]
|
|
2
|
+
|
|
3
|
+
pub mod artifact;
|
|
4
|
+
pub mod attestation_query;
|
|
5
|
+
pub mod audit;
|
|
6
|
+
pub mod commit_sign;
|
|
7
|
+
pub mod device;
|
|
8
|
+
pub mod diagnostics;
|
|
9
|
+
pub mod error;
|
|
10
|
+
pub mod helpers;
|
|
11
|
+
pub mod identity;
|
|
12
|
+
pub mod org;
|
|
13
|
+
pub mod pairing;
|
|
14
|
+
pub mod policy;
|
|
15
|
+
pub mod sign;
|
|
16
|
+
pub mod trust;
|
|
17
|
+
pub mod types;
|
|
18
|
+
pub mod verify;
|
|
19
|
+
pub mod witness;
|
|
20
|
+
|
|
21
|
+
use napi_derive::napi;
|
|
22
|
+
|
|
23
|
+
#[napi]
|
|
24
|
+
pub fn version() -> String {
|
|
25
|
+
env!("CARGO_PKG_VERSION").to_string()
|
|
26
|
+
}
|