sec-ry 1.1.5 → 1.1.7

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.
@@ -170,7 +170,7 @@ var require_package = __commonJS({
170
170
  "package.json"(exports2, module2) {
171
171
  module2.exports = {
172
172
  name: "sec-ry",
173
- version: "1.1.5",
173
+ version: "1.1.7",
174
174
  description: "ChaCha20-Poly1305 / AES-256-GCM token encryption CLI and library",
175
175
  main: "dist/index.js",
176
176
  types: "dist/index.d.ts",
@@ -300,13 +300,16 @@ var require_ui = __commonJS({
300
300
  function box(rows) {
301
301
  if (exports2.QUIET)
302
302
  return;
303
+ const MAX_W = 80;
303
304
  const lw = Math.max(...rows.map((r) => r.label.length));
305
+ const valMax = MAX_W - lw - 6;
304
306
  const lines = rows.map((r) => {
305
307
  const pad = " ".repeat(lw - r.label.length + 1);
306
308
  const vc = r.valueColor ?? _c.hi;
307
- return `${_c.gray}${r.label}${_c.r}${pad}${vc}${r.value}${_c.r}`;
309
+ const val = r.value.length > valMax ? r.value.slice(0, valMax - 1) + "\u2026" : r.value;
310
+ return `${_c.gray}${r.label}${_c.r}${pad}${vc}${val}${_c.r}`;
308
311
  });
309
- const width = Math.max(...lines.map(_visLen)) + 4;
312
+ const width = Math.min(MAX_W, Math.max(...lines.map(_visLen)) + 4);
310
313
  const hline = (l, r) => `${_c.gray}${l}${"\u2500".repeat(width - 2)}${r}${_c.r}
311
314
  `;
312
315
  _ui(hline("\u250C", "\u2510"));
@@ -428,9 +431,11 @@ var require_args = __commonJS({
428
431
  Object.defineProperty(exports2, "__esModule", { value: true });
429
432
  exports2.parseArgs = parseArgs;
430
433
  exports2.getFlag = getFlag;
434
+ exports2.getFlags = getFlags;
431
435
  exports2.hasFlag = hasFlag;
432
436
  function parseArgs(argv) {
433
437
  const flags = {};
438
+ const multi = {};
434
439
  const positional = [];
435
440
  let i = 0;
436
441
  const cmd = argv[0] ?? "";
@@ -439,10 +444,16 @@ var require_args = __commonJS({
439
444
  const a = argv[i];
440
445
  if (a.startsWith("-")) {
441
446
  const key = a.replace(/^-+/, "");
442
- const next = argv[i + 1];
443
- if (next && !next.startsWith("-")) {
444
- flags[key] = next;
445
- i += 2;
447
+ const vals = [];
448
+ let j = i + 1;
449
+ while (j < argv.length && !argv[j].startsWith("-")) {
450
+ vals.push(argv[j]);
451
+ j++;
452
+ }
453
+ if (vals.length > 0) {
454
+ flags[key] = vals[0];
455
+ multi[key] = vals;
456
+ i = j;
446
457
  } else {
447
458
  flags[key] = true;
448
459
  i++;
@@ -452,7 +463,7 @@ var require_args = __commonJS({
452
463
  i++;
453
464
  }
454
465
  }
455
- return { cmd, positional, flags };
466
+ return { cmd, positional, flags, multi };
456
467
  }
457
468
  function getFlag(flags, ...keys) {
458
469
  for (const k of keys) {
@@ -462,6 +473,13 @@ var require_args = __commonJS({
462
473
  }
463
474
  return void 0;
464
475
  }
476
+ function getFlags(multi, ...keys) {
477
+ for (const k of keys) {
478
+ if (multi[k] && multi[k].length > 0)
479
+ return multi[k];
480
+ }
481
+ return [];
482
+ }
465
483
  function hasFlag(flags, ...keys) {
466
484
  return keys.some((k) => k in flags);
467
485
  }
@@ -989,6 +1007,9 @@ var require_crypto = __commonJS({
989
1007
  exports2.inspectToken = inspectToken;
990
1008
  exports2.generatePassword = generatePassword;
991
1009
  exports2.parseExpiry = parseExpiry;
1010
+ exports2._unseal = _unseal;
1011
+ exports2.sealEnc = sealEnc;
1012
+ exports2.open = open;
992
1013
  exports2.seal = seal;
993
1014
  exports2.sealVerify = sealVerify;
994
1015
  exports2.sealS2 = sealS2;
@@ -1190,24 +1211,118 @@ var require_crypto = __commonJS({
1190
1211
  const u = { s: 1e3, m: 6e4, h: 36e5, d: 864e5 };
1191
1212
  return Date.now() + parseInt(m[1], 10) * u[m[2]];
1192
1213
  }
1214
+ var _PFX_SE = "secry:se:";
1215
+ var _PFX_SEI = "secry:sei:";
1216
+ var _CPX_SE = "sec:se:";
1217
+ var _CPX_SEI = "sec:sei:";
1218
+ var _SYM_VAL = /* @__PURE__ */ Symbol("sealed.value");
1219
+ function _makeSeal(value) {
1220
+ const obj = /* @__PURE__ */ Object.create(null);
1221
+ Object.defineProperty(obj, _SYM_VAL, { value, enumerable: false, configurable: false, writable: false });
1222
+ obj.toString = () => "[sealed]";
1223
+ obj.toJSON = () => "[sealed]";
1224
+ obj.valueOf = () => "[sealed]";
1225
+ obj[Symbol.toPrimitive] = () => "[sealed]";
1226
+ obj[/* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom")] = () => "[sealed]";
1227
+ Object.freeze(obj);
1228
+ return obj;
1229
+ }
1230
+ function _unseal(s) {
1231
+ return s[_SYM_VAL];
1232
+ }
1233
+ var _MAX_IPS = 5;
1234
+ function sealEnc(value, masterKey, opts = {}) {
1235
+ const compact = !!opts.compact;
1236
+ const sl = compact ? _SL_C : _SL;
1237
+ const salt = (0, crypto_1.randomBytes)(sl);
1238
+ const nonce = (0, crypto_1.randomBytes)(_NL);
1239
+ const key = _kdf(masterKey, salt);
1240
+ let ips = [];
1241
+ if (opts.ip) {
1242
+ ips = Array.isArray(opts.ip) ? opts.ip : [opts.ip];
1243
+ if (!opts.ipForce && ips.length > _MAX_IPS)
1244
+ throw Object.assign(new Error(`max ${_MAX_IPS} IPs allowed (use ipForce to override)`), { code: "ERR_TOO_MANY_IPS" });
1245
+ }
1246
+ let inner;
1247
+ if (ips.length > 0) {
1248
+ const hashes = ips.map((ip) => (0, crypto_1.createHmac)("sha256", key).update(ip, "utf8").digest());
1249
+ inner = Buffer.concat([Buffer.from([1, ips.length]), ...hashes, Buffer.from(value, "utf8")]);
1250
+ } else {
1251
+ inner = Buffer.concat([Buffer.from([0, 0]), Buffer.from(value, "utf8")]);
1252
+ }
1253
+ const ciph = (0, crypto_1.createCipheriv)(_ALG_V1, key, nonce, { authTagLength: _TL });
1254
+ const body = Buffer.concat([ciph.update(inner), ciph.final()]);
1255
+ const tag = ciph.getAuthTag();
1256
+ const pfx = ips.length > 0 ? compact ? _CPX_SEI : _PFX_SEI : compact ? _CPX_SE : _PFX_SE;
1257
+ return pfx + Buffer.concat([salt, nonce, tag, body]).toString("base64url");
1258
+ }
1259
+ async function open(token, masterKey, cb, opts = {}) {
1260
+ const pfx = token.startsWith(_PFX_SEI) ? _PFX_SEI : token.startsWith(_CPX_SEI) ? _CPX_SEI : token.startsWith(_PFX_SE) ? _PFX_SE : token.startsWith(_CPX_SE) ? _CPX_SE : null;
1261
+ if (!pfx)
1262
+ throw Object.assign(new Error("invalid seal-enc token"), { code: "ERR_FORMAT" });
1263
+ const compact = pfx === _CPX_SE || pfx === _CPX_SEI;
1264
+ const sl = compact ? _SL_C : _SL;
1265
+ const payload = Buffer.from(token.slice(pfx.length), "base64url");
1266
+ const salt = payload.subarray(0, sl);
1267
+ const nonce = payload.subarray(sl, sl + _NL);
1268
+ const tag = payload.subarray(sl + _NL, sl + _NL + _TL);
1269
+ const body = payload.subarray(sl + _NL + _TL);
1270
+ const key = _kdf(masterKey, salt);
1271
+ let inner;
1272
+ try {
1273
+ const dc = (0, crypto_1.createDecipheriv)(_ALG_V1, key, nonce, { authTagLength: _TL });
1274
+ dc.setAuthTag(tag);
1275
+ inner = Buffer.concat([dc.update(body), dc.final()]);
1276
+ } catch {
1277
+ throw Object.assign(new Error("wrong master key or corrupted token"), { code: "ERR_AUTH" });
1278
+ }
1279
+ const hasIp = inner[0] === 1;
1280
+ if (hasIp) {
1281
+ if (!opts.ip)
1282
+ throw Object.assign(new Error("this token requires an ip"), { code: "ERR_IP_REQUIRED" });
1283
+ const count = inner[1];
1284
+ const hashes = [];
1285
+ for (let i = 0; i < count; i++)
1286
+ hashes.push(inner.subarray(2 + i * 32, 2 + (i + 1) * 32));
1287
+ const given = (0, crypto_1.createHmac)("sha256", key).update(opts.ip, "utf8").digest();
1288
+ const matched = hashes.some((h) => h.length === given.length && require("crypto").timingSafeEqual(h, given));
1289
+ if (!matched)
1290
+ throw Object.assign(new Error("ip mismatch"), { code: "ERR_IP_MISMATCH" });
1291
+ const value2 = inner.subarray(2 + count * 32).toString("utf8");
1292
+ const sealed2 = _makeSeal(value2);
1293
+ const result = await cb(sealed2);
1294
+ sealed2[_SYM_VAL];
1295
+ return result;
1296
+ }
1297
+ const value = inner.subarray(2).toString("utf8");
1298
+ const sealed = _makeSeal(value);
1299
+ return await cb(sealed);
1300
+ }
1193
1301
  var _PFX_S1 = "secry:s1:";
1194
1302
  var _PFX_S2 = "secry:s2:";
1303
+ var _CPX_S1 = "sec:s1:";
1304
+ var _CPX_S2 = "sec:s2:";
1195
1305
  var _PEPPER_S2 = "enclove.secry.s2.pepper.v1";
1196
- function seal(input, secret) {
1197
- const salt = (0, crypto_1.randomBytes)(16);
1306
+ function seal(input, secret, compact = false) {
1307
+ const sl = compact ? _SL_C : 16;
1308
+ const pfx = compact ? _CPX_S1 : _PFX_S1;
1309
+ const salt = (0, crypto_1.randomBytes)(sl);
1198
1310
  const key = (0, crypto_1.scryptSync)(Buffer.from(secret, "utf8"), salt, 32, _SCRYPT);
1199
1311
  const mac = (0, crypto_1.createHmac)("sha256", key).update(input, "utf8").digest();
1200
- return _PFX_S1 + Buffer.concat([salt, mac]).toString("base64url");
1312
+ return pfx + Buffer.concat([salt, mac]).toString("base64url");
1201
1313
  }
1202
1314
  function sealVerify(input, secret, token) {
1203
- if (!token.startsWith(_PFX_S1))
1315
+ const isCompact = token.startsWith(_CPX_S1);
1316
+ const pfx = isCompact ? _CPX_S1 : _PFX_S1;
1317
+ if (!token.startsWith(pfx) && !token.startsWith(_PFX_S1))
1204
1318
  return false;
1205
1319
  try {
1206
- const raw = Buffer.from(token.slice(_PFX_S1.length), "base64url");
1207
- if (raw.length < 48)
1320
+ const sl = isCompact ? _SL_C : 16;
1321
+ const raw = Buffer.from(token.slice(pfx.length), "base64url");
1322
+ if (raw.length < sl + 32)
1208
1323
  return false;
1209
- const salt = raw.subarray(0, 16);
1210
- const mac = raw.subarray(16);
1324
+ const salt = raw.subarray(0, sl);
1325
+ const mac = raw.subarray(sl);
1211
1326
  const key = (0, crypto_1.scryptSync)(Buffer.from(secret, "utf8"), salt, 32, _SCRYPT);
1212
1327
  const exp = (0, crypto_1.createHmac)("sha256", key).update(input, "utf8").digest();
1213
1328
  return exp.length === mac.length && require("crypto").timingSafeEqual(exp, mac);
@@ -1215,23 +1330,28 @@ var require_crypto = __commonJS({
1215
1330
  return false;
1216
1331
  }
1217
1332
  }
1218
- async function sealS2(input, secret) {
1219
- const salt = (0, crypto_1.randomBytes)(32);
1333
+ async function sealS2(input, secret, compact = false) {
1334
+ const sl = compact ? _SL_C : 32;
1335
+ const pfx = compact ? _CPX_S2 : _PFX_S2;
1336
+ const salt = (0, crypto_1.randomBytes)(sl);
1220
1337
  const peppered = Buffer.from(_PEPPER_S2 + secret, "utf8");
1221
1338
  const key = (0, crypto_1.scryptSync)(peppered, salt, 32, _SCRYPT_S2);
1222
1339
  const { blake3: blake32 } = await Promise.resolve().then(() => __importStar((init_blake3(), __toCommonJS(blake3_exports))));
1223
1340
  const out = blake32(new TextEncoder().encode(input), { key, dkLen: 64 });
1224
- return _PFX_S2 + Buffer.concat([salt, Buffer.from(out)]).toString("base64url");
1341
+ return pfx + Buffer.concat([salt, Buffer.from(out)]).toString("base64url");
1225
1342
  }
1226
1343
  async function sealVerifyS2(input, secret, token) {
1227
- if (!token.startsWith(_PFX_S2))
1344
+ const isCompact = token.startsWith(_CPX_S2);
1345
+ const pfx = isCompact ? _CPX_S2 : _PFX_S2;
1346
+ if (!token.startsWith(pfx) && !token.startsWith(_PFX_S2))
1228
1347
  return false;
1229
1348
  try {
1230
- const raw = Buffer.from(token.slice(_PFX_S2.length), "base64url");
1231
- if (raw.length < 96)
1349
+ const sl = isCompact ? _SL_C : 32;
1350
+ const raw = Buffer.from(token.slice(pfx.length), "base64url");
1351
+ if (raw.length < sl + 64)
1232
1352
  return false;
1233
- const salt = raw.subarray(0, 32);
1234
- const mac = raw.subarray(32);
1353
+ const salt = raw.subarray(0, sl);
1354
+ const mac = raw.subarray(sl);
1235
1355
  const peppered = Buffer.from(_PEPPER_S2 + secret, "utf8");
1236
1356
  const key = (0, crypto_1.scryptSync)(peppered, salt, 32, _SCRYPT_S2);
1237
1357
  const { blake3: blake32 } = await Promise.resolve().then(() => __importStar((init_blake3(), __toCommonJS(blake3_exports))));
@@ -2110,8 +2230,48 @@ ${_c.gray}${"\u2500".repeat(48)}${_c.r}
2110
2230
  var require_seal = __commonJS({
2111
2231
  "dist/cmd/seal.js"(exports2) {
2112
2232
  "use strict";
2233
+ var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
2234
+ if (k2 === void 0) k2 = k;
2235
+ var desc = Object.getOwnPropertyDescriptor(m, k);
2236
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
2237
+ desc = { enumerable: true, get: function() {
2238
+ return m[k];
2239
+ } };
2240
+ }
2241
+ Object.defineProperty(o, k2, desc);
2242
+ }) : (function(o, m, k, k2) {
2243
+ if (k2 === void 0) k2 = k;
2244
+ o[k2] = m[k];
2245
+ }));
2246
+ var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
2247
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
2248
+ }) : function(o, v) {
2249
+ o["default"] = v;
2250
+ });
2251
+ var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
2252
+ var ownKeys = function(o) {
2253
+ ownKeys = Object.getOwnPropertyNames || function(o2) {
2254
+ var ar = [];
2255
+ for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
2256
+ return ar;
2257
+ };
2258
+ return ownKeys(o);
2259
+ };
2260
+ return function(mod) {
2261
+ if (mod && mod.__esModule) return mod;
2262
+ var result = {};
2263
+ if (mod != null) {
2264
+ for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
2265
+ }
2266
+ __setModuleDefault(result, mod);
2267
+ return result;
2268
+ };
2269
+ })();
2113
2270
  Object.defineProperty(exports2, "__esModule", { value: true });
2114
2271
  exports2.cmdSeal = cmdSeal;
2272
+ exports2.cmdSei = cmdSei;
2273
+ exports2.cmdSeiOpen = cmdSeiOpen;
2274
+ exports2.cmdSealOpen = cmdSealOpen;
2115
2275
  exports2.cmdSealVerify = cmdSealVerify;
2116
2276
  var crypto_1 = require_crypto();
2117
2277
  var password_1 = require_password();
@@ -2119,6 +2279,17 @@ var require_seal = __commonJS({
2119
2279
  var clip_1 = require_clip();
2120
2280
  var rc_1 = require_rc();
2121
2281
  var args_12 = require_args();
2282
+ var os = __importStar(require("os"));
2283
+ function _localIp() {
2284
+ const ifaces = os.networkInterfaces();
2285
+ for (const name of Object.keys(ifaces)) {
2286
+ for (const iface of ifaces[name] ?? []) {
2287
+ if (iface.family === "IPv4" && !iface.internal)
2288
+ return iface.address;
2289
+ }
2290
+ }
2291
+ return "127.0.0.1";
2292
+ }
2122
2293
  async function cmdSeal(parsed2) {
2123
2294
  const rc = (0, rc_1.loadRC)();
2124
2295
  const text = parsed2.positional[0];
@@ -2129,8 +2300,49 @@ var require_seal = __commonJS({
2129
2300
  const useClip = (0, args_12.hasFlag)(parsed2.flags, "clip") || !!rc.clip;
2130
2301
  const quiet = (0, args_12.hasFlag)(parsed2.flags, "quiet") || (0, args_12.hasFlag)(parsed2.flags, "q");
2131
2302
  const useS2 = (0, args_12.hasFlag)(parsed2.flags, "s2");
2303
+ const useEnc = (0, args_12.hasFlag)(parsed2.flags, "enc");
2304
+ const useIp = (0, args_12.hasFlag)(parsed2.flags, "ip");
2305
+ const ipForce = (0, args_12.hasFlag)(parsed2.flags, "ipf");
2306
+ const compact = (0, args_12.hasFlag)(parsed2.flags, "compact");
2132
2307
  const t0 = Date.now();
2133
- const token = useS2 ? await (0, crypto_1.sealS2)(text, secret) : (0, crypto_1.seal)(text, secret);
2308
+ if (useEnc) {
2309
+ let ips;
2310
+ if (useIp) {
2311
+ const extras = (0, args_12.getFlags)(parsed2.multi, "ip");
2312
+ ips = extras.length > 0 ? [_localIp(), ...extras] : [_localIp()];
2313
+ } else if (ipForce) {
2314
+ const extras = (0, args_12.getFlags)(parsed2.multi, "ipf");
2315
+ ips = extras.length > 0 ? [_localIp(), ...extras] : [_localIp()];
2316
+ }
2317
+ if (ips && !ipForce && ips.length > 5)
2318
+ (0, ui_12.err)("max 5 IPs allowed \u2014 use --ipf to override");
2319
+ const token2 = (0, crypto_1.sealEnc)(text, secret, { ip: ips, ipForce, compact });
2320
+ const elapsed2 = Date.now() - t0;
2321
+ if (!quiet) {
2322
+ (0, ui_12.section)("seal");
2323
+ (0, ui_12.box)([
2324
+ { label: "input", value: text.length > 52 ? text.slice(0, 52) + "\u2026" : text },
2325
+ { label: "algo", value: "AES-256-GCM / scrypt" },
2326
+ { label: "mode", value: `reversible${ips ? ` \xB7 ip-bound (${ips.length})` : ""}${compact ? " \xB7 compact" : ""}` },
2327
+ ...ips ? ips.map((ip, i) => ({ label: `ip[${i}]`, value: ip })) : [],
2328
+ { label: "note", value: ips ? "use seal:open \u2014 any of the bound IPs are accepted" : "use seal:open to retrieve" },
2329
+ { label: "token", value: token2 }
2330
+ ]);
2331
+ (0, ui_12.ms)(elapsed2);
2332
+ }
2333
+ if (useClip) {
2334
+ const copied = (0, clip_1.copyToClipboard)(token2);
2335
+ if (copied) {
2336
+ const { info } = require_ui();
2337
+ if (!quiet)
2338
+ info("copied to clipboard");
2339
+ } else if (!quiet)
2340
+ (0, ui_12.warn)("--clip: no clipboard tool found");
2341
+ }
2342
+ (0, ui_12.data)(token2);
2343
+ return;
2344
+ }
2345
+ const token = useS2 ? await (0, crypto_1.sealS2)(text, secret, compact) : (0, crypto_1.seal)(text, secret, compact);
2134
2346
  const elapsed = Date.now() - t0;
2135
2347
  if (!quiet) {
2136
2348
  (0, ui_12.section)("seal");
@@ -2138,7 +2350,8 @@ var require_seal = __commonJS({
2138
2350
  { label: "input", value: text.length > 52 ? text.slice(0, 52) + "\u2026" : text },
2139
2351
  { label: "algo", value: useS2 ? "BLAKE3 / scrypt N=2^16" : "HMAC-SHA256 / scrypt" },
2140
2352
  { label: "version", value: useS2 ? "s2" : "s1" },
2141
- { label: "note", value: "one-way \u2014 cannot be reversed" },
2353
+ { label: "mode", value: "one-way" },
2354
+ { label: "note", value: "cannot be reversed" },
2142
2355
  { label: "token", value: token }
2143
2356
  ]);
2144
2357
  (0, ui_12.ms)(elapsed);
@@ -2156,6 +2369,104 @@ var require_seal = __commonJS({
2156
2369
  }
2157
2370
  (0, ui_12.data)(token);
2158
2371
  }
2372
+ async function cmdSei(parsed2) {
2373
+ const rc = (0, rc_1.loadRC)();
2374
+ const text = parsed2.positional[0];
2375
+ if (!text)
2376
+ (0, ui_12.err)("missing <value> argument");
2377
+ const rawPw = (0, args_12.getFlag)(parsed2.flags, "sw") ?? rc.password;
2378
+ const secret = (0, password_1.resolvePassword)(rawPw ?? "");
2379
+ const ipForce = (0, args_12.hasFlag)(parsed2.flags, "ipf");
2380
+ const compact = (0, args_12.hasFlag)(parsed2.flags, "compact");
2381
+ const useClip = (0, args_12.hasFlag)(parsed2.flags, "clip") || !!rc.clip;
2382
+ const quiet = (0, args_12.hasFlag)(parsed2.flags, "quiet") || (0, args_12.hasFlag)(parsed2.flags, "q");
2383
+ const t0 = Date.now();
2384
+ const extraIp = (0, args_12.getFlags)(parsed2.multi, "ip");
2385
+ const extraIpf = (0, args_12.getFlags)(parsed2.multi, "ipf");
2386
+ const extras = ipForce ? extraIpf : extraIp;
2387
+ const ips = [_localIp(), ...extras];
2388
+ if (!ipForce && ips.length > 5)
2389
+ (0, ui_12.err)("max 5 IPs allowed \u2014 use --ipf to override");
2390
+ const token = (0, crypto_1.sealEnc)(text, secret, { ip: ips, ipForce, compact });
2391
+ const elapsed = Date.now() - t0;
2392
+ if (!quiet) {
2393
+ (0, ui_12.section)("sei");
2394
+ (0, ui_12.box)([
2395
+ { label: "input", value: text.length > 52 ? text.slice(0, 52) + "\u2026" : text },
2396
+ { label: "algo", value: "AES-256-GCM / scrypt" },
2397
+ { label: "mode", value: `${compact ? "compact (sec:sei:)" : "full (secry:sei:)"} \xB7 ip-bound (${ips.length})` },
2398
+ ...ips.map((ip, i) => ({ label: `ip[${i}]`, value: ip })),
2399
+ { label: "note", value: "reversible \u2014 use sei:open to retrieve" },
2400
+ { label: "token", value: token }
2401
+ ]);
2402
+ (0, ui_12.ms)(elapsed);
2403
+ }
2404
+ if (useClip) {
2405
+ const copied = (0, clip_1.copyToClipboard)(token);
2406
+ if (copied) {
2407
+ const { info } = require_ui();
2408
+ if (!quiet)
2409
+ info("copied to clipboard");
2410
+ } else if (!quiet)
2411
+ (0, ui_12.warn)("--clip: no clipboard tool found");
2412
+ }
2413
+ (0, ui_12.data)(token);
2414
+ }
2415
+ async function cmdSeiOpen(parsed2) {
2416
+ const rc = (0, rc_1.loadRC)();
2417
+ const token = parsed2.positional[0];
2418
+ if (!token)
2419
+ (0, ui_12.err)("missing <token> argument");
2420
+ const rawPw = (0, args_12.getFlag)(parsed2.flags, "sw") ?? rc.password;
2421
+ if (!rawPw)
2422
+ (0, ui_12.err)("-sw <secret> is required");
2423
+ const secret = (0, password_1.resolvePassword)(rawPw);
2424
+ const quiet = (0, args_12.hasFlag)(parsed2.flags, "quiet") || (0, args_12.hasFlag)(parsed2.flags, "q");
2425
+ const t0 = Date.now();
2426
+ const detectedIp = _localIp();
2427
+ await (0, crypto_1.open)(token, secret, (sealed) => {
2428
+ const elapsed = Date.now() - t0;
2429
+ const value = (0, crypto_1._unseal)(sealed);
2430
+ if (!quiet) {
2431
+ (0, ui_12.section)("sei open");
2432
+ (0, ui_12.box)([
2433
+ { label: "token", value: token.length > 52 ? token.slice(0, 52) + "\u2026" : token },
2434
+ { label: "ip", value: detectedIp },
2435
+ { label: "result", value }
2436
+ ]);
2437
+ (0, ui_12.ms)(elapsed);
2438
+ }
2439
+ (0, ui_12.data)(value);
2440
+ }, { ip: detectedIp });
2441
+ }
2442
+ async function cmdSealOpen(parsed2) {
2443
+ const rc = (0, rc_1.loadRC)();
2444
+ const token = parsed2.positional[0];
2445
+ if (!token)
2446
+ (0, ui_12.err)("missing <token> argument");
2447
+ const rawPw = (0, args_12.getFlag)(parsed2.flags, "sw") ?? rc.password;
2448
+ if (!rawPw)
2449
+ (0, ui_12.err)("-sw <secret> is required");
2450
+ const secret = (0, password_1.resolvePassword)(rawPw);
2451
+ const quiet = (0, args_12.hasFlag)(parsed2.flags, "quiet") || (0, args_12.hasFlag)(parsed2.flags, "q");
2452
+ const t0 = Date.now();
2453
+ const needsIp = token.startsWith("secry:sei:") || token.startsWith("sec:sei:");
2454
+ const detectedIp = needsIp ? _localIp() : void 0;
2455
+ await (0, crypto_1.open)(token, secret, (sealed) => {
2456
+ const elapsed = Date.now() - t0;
2457
+ const value = (0, crypto_1._unseal)(sealed);
2458
+ if (!quiet) {
2459
+ (0, ui_12.section)("seal open");
2460
+ (0, ui_12.box)([
2461
+ { label: "token", value: token.length > 52 ? token.slice(0, 52) + "\u2026" : token },
2462
+ ...detectedIp ? [{ label: "ip", value: detectedIp }] : [],
2463
+ { label: "result", value }
2464
+ ]);
2465
+ (0, ui_12.ms)(elapsed);
2466
+ }
2467
+ (0, ui_12.data)(value);
2468
+ }, detectedIp ? { ip: detectedIp } : {});
2469
+ }
2159
2470
  async function cmdSealVerify(parsed2) {
2160
2471
  const rc = (0, rc_1.loadRC)();
2161
2472
  const text = parsed2.positional[0];
@@ -7153,9 +7464,18 @@ async function main() {
7153
7464
  case "seal":
7154
7465
  await (0, seal_1.cmdSeal)(parsed);
7155
7466
  break;
7467
+ case "seal:open":
7468
+ await (0, seal_1.cmdSealOpen)(parsed);
7469
+ break;
7156
7470
  case "seal:verify":
7157
7471
  await (0, seal_1.cmdSealVerify)(parsed);
7158
7472
  break;
7473
+ case "sei":
7474
+ await (0, seal_1.cmdSei)(parsed);
7475
+ break;
7476
+ case "sei:open":
7477
+ await (0, seal_1.cmdSeiOpen)(parsed);
7478
+ break;
7159
7479
  case "changelog":
7160
7480
  case "changes":
7161
7481
  await (0, changelog_1.cmdChangelog)(parsed);