@seams/wallet-server 0.44.0 → 0.45.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 +4 -4
- package/artifact-manifest.json +11 -11
- package/cloudflare-router-ab/build/deriver-a/build-stamp.json +1 -1
- package/cloudflare-router-ab/build/deriver-b/build-stamp.json +1 -1
- package/cloudflare-router-ab/build/router/build-stamp.json +1 -1
- package/cloudflare-router-ab/build/signing-worker/build-stamp.json +1 -1
- package/cloudflare-router-ab/build/tenant-root-control-plane/build-stamp.json +1 -1
- package/dist/esm/authorization/factorEvidence.js +37 -1
- package/dist/esm/authorization/voiceIdDeviceProof.js +32 -6
- package/dist/esm/hosted-wallet-gateway.js +1 -1
- package/dist/esm/router/cloudflare/d1/auth/d1RouterApiAuthService.js +1 -0
- package/dist/esm/router/cloudflare/d1/wallet/d1VoiceIdSessions.js +34 -1
- package/dist/esm/router/cloudflare.js +317 -100
- package/dist/esm/router/domains/ed25519Yao/export/routerAbEd25519YaoExport.js +181 -14
- package/dist/esm/router/domains/ed25519Yao/export/routerAbEd25519YaoExportRequestScopedCloudflare.js +21 -5
- package/dist/esm/router/domains/signingOperations/routerAbOperationStepUp.js +4 -0
- package/dist/esm/router/transport/fetch/routes/ecdsaOperationStepUpFactor.js +51 -1
- package/dist/esm/router/transport/fetch/routes/thresholdEcdsa.js +3 -1
- package/dist/esm/shared-ts/src/authorization/capabilityKinds.js +2 -1
- package/dist/esm/shared-ts/src/authorization/voiceIdDeviceAssertion.js +70 -1
- package/dist/esm/shared-ts/src/utils/routerAbEcdsaDerivation.js +18 -1
- package/dist/esm/shared-ts/src/utils/routerAbEd25519YaoDigests.js +8 -1
- package/dist/types/shared-ts/src/authorization/capabilityKinds.d.ts +2 -1
- package/dist/types/shared-ts/src/authorization/voiceIdDeviceAssertion.d.ts +30 -0
- package/dist/types/shared-ts/src/utils/routerAbEcdsaDerivation.d.ts +11 -1
- package/dist/types/shared-ts/src/utils/routerAbEd25519YaoDigests.d.ts +3 -0
- package/dist/types/wallet-server/src/authorization/domain.d.ts +3 -0
- package/dist/types/wallet-server/src/authorization/factorEvidence.d.ts +11 -1
- package/dist/types/wallet-server/src/authorization/voiceIdDeviceProof.d.ts +27 -1
- package/dist/types/wallet-server/src/router/cloudflare/d1/wallet/d1VoiceIdSessions.d.ts +3 -1
- package/dist/types/wallet-server/src/router/domains/ed25519Yao/export/routerAbEd25519YaoExport.d.ts +36 -2
- package/dist/types/wallet-server/src/router/domains/voiceId/voiceIdSessions.d.ts +23 -1
- package/dist/types/wallet-server/src/router/framework/authServicePort.d.ts +1 -0
- package/dist/types/wallet-server/src/router/transport/fetch/routes/ecdsaOperationStepUpFactor.d.ts +2 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { parseEmailOtpChallengeId, parseThresholdEd25519SessionId, parseWalletId, parseWebAuthnCredentialIdB64u, parseWebAuthnRpId } from "../../../../shared-ts/src/utils/domainIds.js";
|
|
1
|
+
import { parseEmailOtpChallengeId, parseThresholdEd25519SessionId, parseWalletAuthMethodId, parseWalletId, parseWebAuthnCredentialIdB64u, parseWebAuthnRpId } from "../../../../shared-ts/src/utils/domainIds.js";
|
|
2
2
|
import { isPlainObject } from "../../../../shared-ts/src/utils/validation.js";
|
|
3
3
|
import { base64UrlEncode } from "../../../../shared-ts/src/utils/base64.js";
|
|
4
4
|
import { alphabetizeStringify, sha256BytesUtf8 } from "../../../../shared-ts/src/utils/digests.js";
|
|
@@ -10,8 +10,9 @@ import { sameRouterAbMpcMaterialActivationRef } from "../../../../shared-ts/src/
|
|
|
10
10
|
import { deriveRouterAbEd25519YaoExportAuthorizationDigestV1, deriveRouterAbEd25519YaoExportConfirmationDigestV1, deriveRouterAbEd25519YaoRuntimePolicyBindingV1 } from "../../../../shared-ts/src/utils/routerAbEd25519YaoDigests.js";
|
|
11
11
|
import { ROUTER_AB_ED25519_YAO_EXPORT_ADMISSION_PATH_V1, ROUTER_AB_ED25519_YAO_EXPORT_EXECUTE_PATH_V1, parseRouterAbEd25519YaoExportAdmissionReceiptV1, parseRouterAbEd25519YaoExportAdmissionRequestV1, parseRouterAbEd25519YaoExportExecuteRequestV1, parseRouterAbEd25519YaoExportResultV1 } from "../../../../shared-ts/src/utils/routerAbEd25519Yao.js";
|
|
12
12
|
import { walletAuthAuthorityRef } from "../../../../shared-ts/src/utils/walletAuthAuthority.js";
|
|
13
|
+
import { parseVoiceIdOperationAssertion } from "../../../../shared-ts/src/authorization/voiceIdDeviceAssertion.js";
|
|
13
14
|
import { parseSessionOrigin, parseVerifiedOwnerProofId } from "../../../../authorization/domain.js";
|
|
14
|
-
import { buildVerifiedWalletOperationEmailOtpFactorResult, buildVerifiedWalletOperationPasskeyFactorResult } from "../../../../authorization/factorEvidence.js";
|
|
15
|
+
import { buildVerifiedWalletOperationEmailOtpFactorResult, buildVerifiedWalletOperationPasskeyFactorResult, buildVerifiedWalletOperationVoiceIdFactorResult } from "../../../../authorization/factorEvidence.js";
|
|
15
16
|
import { WALLET_EMAIL_OTP_EXPORT_OPERATION } from "../../../../shared-ts/src/utils/emailOtpDomain.js";
|
|
16
17
|
import { parseWebAuthnAuthenticationCredential, webAuthnCredentialIdB64uFromCredential } from "../../../auth/webAuthnCredentialCodecs.js";
|
|
17
18
|
import { normalizeCorsOrigin } from "../../../../core/SessionService.js";
|
|
@@ -657,7 +658,8 @@ function exportPasskeyCredentialId(authorization) {
|
|
|
657
658
|
const parsed = webAuthnCredentialIdB64uFromCredential(authorization.webauthnAuthentication);
|
|
658
659
|
return parsed.ok ? parsed.credentialIdB64u : null;
|
|
659
660
|
}
|
|
660
|
-
case "email_otp_factor":
|
|
661
|
+
case "email_otp_factor":
|
|
662
|
+
case "voiceid_device": return null;
|
|
661
663
|
default: return assertNeverExportAdmissionAuthorization(authorization);
|
|
662
664
|
}
|
|
663
665
|
}
|
|
@@ -671,6 +673,10 @@ function exportAuthorizationDigestAuthority(authorization, passkeyCredentialId)
|
|
|
671
673
|
kind: "email_otp",
|
|
672
674
|
providerSubjectId: authorization.providerSubjectId
|
|
673
675
|
};
|
|
676
|
+
case "voiceid_device": return {
|
|
677
|
+
kind: "voiceid",
|
|
678
|
+
deviceKeyId: authorization.assertion.statement.deviceKeyId
|
|
679
|
+
};
|
|
674
680
|
default: return assertNeverExportAdmissionAuthorization(authorization);
|
|
675
681
|
}
|
|
676
682
|
}
|
|
@@ -831,6 +837,14 @@ async function authorizeExportAdmission(args) {
|
|
|
831
837
|
expiresAtMs: authorization.expires_at_ms
|
|
832
838
|
});
|
|
833
839
|
}
|
|
840
|
+
if (args.input.authorization.kind === "voiceid_device") return await authorizeVoiceIdExportAdmission({
|
|
841
|
+
walletId: walletId.value,
|
|
842
|
+
input: args.input,
|
|
843
|
+
authorization: args.input.authorization,
|
|
844
|
+
voiceId: args.voiceId,
|
|
845
|
+
authorizedOperations: args.authorizedOperations,
|
|
846
|
+
nowMs
|
|
847
|
+
});
|
|
834
848
|
const authority = await args.walletAuthMethods.resolveActiveEmailOtpAuthorityForVerifiedMethod({
|
|
835
849
|
walletId: String(walletId.value),
|
|
836
850
|
walletAuthMethodId: args.input.authorization.walletAuthMethodId,
|
|
@@ -865,6 +879,77 @@ async function authorizeExportAdmission(args) {
|
|
|
865
879
|
factorRelease: checked.factorRelease
|
|
866
880
|
};
|
|
867
881
|
}
|
|
882
|
+
async function verifyVoiceIdExportProof(args) {
|
|
883
|
+
const walletAuthMethodId = parseWalletAuthMethodId(args.authorization.walletAuthMethodId);
|
|
884
|
+
let requestOrigin;
|
|
885
|
+
let operationChallenge;
|
|
886
|
+
try {
|
|
887
|
+
requestOrigin = parseSessionOrigin(args.input.expectedOrigin);
|
|
888
|
+
operationChallenge = parseDigestB64u(base64UrlEncode(Uint8Array.from(args.input.body.authorization.confirmation_digest)));
|
|
889
|
+
} catch {
|
|
890
|
+
return authorizationFailure({
|
|
891
|
+
status: 403,
|
|
892
|
+
code: "export_authorization_invalid",
|
|
893
|
+
message: "Ed25519 Yao export VoiceID scope is invalid"
|
|
894
|
+
});
|
|
895
|
+
}
|
|
896
|
+
if (!walletAuthMethodId.ok) return authorizationFailure({
|
|
897
|
+
status: 403,
|
|
898
|
+
code: "invalid_body",
|
|
899
|
+
message: walletAuthMethodId.error.message
|
|
900
|
+
});
|
|
901
|
+
let verified;
|
|
902
|
+
try {
|
|
903
|
+
verified = await args.voiceId.verifyVoiceIdOperation({
|
|
904
|
+
walletId: args.walletId,
|
|
905
|
+
walletAuthMethodId: walletAuthMethodId.value,
|
|
906
|
+
assertion: args.authorization.assertion,
|
|
907
|
+
operationKind: "near.export_key",
|
|
908
|
+
operationChallenge,
|
|
909
|
+
requestOrigin,
|
|
910
|
+
nowMs: args.nowMs
|
|
911
|
+
});
|
|
912
|
+
} catch {
|
|
913
|
+
return authorizationFailure({
|
|
914
|
+
status: 503,
|
|
915
|
+
code: "export_voiceid_unavailable",
|
|
916
|
+
message: "VoiceID export verification is unavailable"
|
|
917
|
+
});
|
|
918
|
+
}
|
|
919
|
+
if (verified.kind === "rejected") return authorizationFailure({
|
|
920
|
+
status: 403,
|
|
921
|
+
code: verified.code === "method_unavailable" ? "export_voiceid_method_unavailable" : "export_voiceid_not_verified",
|
|
922
|
+
message: "Fresh VoiceID export proof was not verified"
|
|
923
|
+
});
|
|
924
|
+
return {
|
|
925
|
+
ok: true,
|
|
926
|
+
verified
|
|
927
|
+
};
|
|
928
|
+
}
|
|
929
|
+
async function authorizeVoiceIdExportAdmission(args) {
|
|
930
|
+
const checked = await verifyVoiceIdExportProof(args);
|
|
931
|
+
if (!checked.ok) return checked;
|
|
932
|
+
const verified = checked.verified;
|
|
933
|
+
const recorded = await recordFreshExportProof({
|
|
934
|
+
walletId: args.walletId,
|
|
935
|
+
input: args.input,
|
|
936
|
+
authority: verified.authority,
|
|
937
|
+
authorizedOperations: args.authorizedOperations,
|
|
938
|
+
voiceIdProof: verified.proof,
|
|
939
|
+
verifiedAtMs: args.nowMs,
|
|
940
|
+
expiresAtMs: Math.min(args.input.body.authorization.expires_at_ms, verified.proof.statement.expiresAtMs)
|
|
941
|
+
});
|
|
942
|
+
if (!recorded.ok) return recorded;
|
|
943
|
+
return {
|
|
944
|
+
ok: true,
|
|
945
|
+
admission: recorded.admission,
|
|
946
|
+
walletId: recorded.walletId,
|
|
947
|
+
custodyRelease: {
|
|
948
|
+
kind: "voiceid_custody_envelope",
|
|
949
|
+
envelope: verified.custodyEnvelope
|
|
950
|
+
}
|
|
951
|
+
};
|
|
952
|
+
}
|
|
868
953
|
async function recordFreshExportProof(args) {
|
|
869
954
|
const authorizationIdentitySuffix = base64UrlEncode(Uint8Array.from(args.input.body.authorization.authorization_digest));
|
|
870
955
|
const principalId = parsePrincipalId(`ed25519-export:${args.walletId}`);
|
|
@@ -891,11 +976,9 @@ async function recordFreshExportProof(args) {
|
|
|
891
976
|
let challengeId = null;
|
|
892
977
|
let assertionDigest = null;
|
|
893
978
|
switch (args.authority.factor.kind) {
|
|
894
|
-
case "voiceid":
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
message: "Use a passkey or Email OTP to export keys."
|
|
898
|
-
});
|
|
979
|
+
case "voiceid":
|
|
980
|
+
factorId = parseAuthFactorId(`voiceid:${args.authority.factor.deviceKeyId}`);
|
|
981
|
+
break;
|
|
899
982
|
case "passkey":
|
|
900
983
|
factorId = parseAuthFactorId(`passkey:${args.authority.factor.credentialIdB64u}`);
|
|
901
984
|
try {
|
|
@@ -911,14 +994,15 @@ async function recordFreshExportProof(args) {
|
|
|
911
994
|
case "email_otp_factor":
|
|
912
995
|
rawChallengeId = args.input.authorization.challengeId;
|
|
913
996
|
break;
|
|
914
|
-
case "passkey":
|
|
997
|
+
case "passkey":
|
|
998
|
+
case "voiceid_device": break;
|
|
915
999
|
default: assertNeverExportAdmissionAuthorization(args.input.authorization);
|
|
916
1000
|
}
|
|
917
1001
|
challengeId = parseEmailOtpChallengeId(rawChallengeId);
|
|
918
1002
|
break;
|
|
919
1003
|
}
|
|
920
1004
|
}
|
|
921
|
-
if (!principalId.ok || !capabilityId.ok || !operationId.ok || !evidenceId.ok || !evidenceSetId.ok || !authorizedOperationId.ok || !auditEventId.ok || !factorId.ok || challengeId !== null && !challengeId.ok || args.authority.factor.kind === "passkey" && assertionDigest === null) return authorizationFailure({
|
|
1005
|
+
if (!principalId.ok || !capabilityId.ok || !operationId.ok || !evidenceId.ok || !evidenceSetId.ok || !authorizedOperationId.ok || !auditEventId.ok || !factorId.ok || challengeId !== null && !challengeId.ok || args.authority.factor.kind === "passkey" && assertionDigest === null || args.authority.factor.kind === "voiceid" && !args.voiceIdProof) return authorizationFailure({
|
|
922
1006
|
status: 403,
|
|
923
1007
|
code: "export_authorization_invalid",
|
|
924
1008
|
message: "Ed25519 Yao export owner proof identifiers are invalid"
|
|
@@ -974,6 +1058,21 @@ async function recordFreshExportProof(args) {
|
|
|
974
1058
|
expiresAtMs: args.expiresAtMs
|
|
975
1059
|
});
|
|
976
1060
|
break;
|
|
1061
|
+
case "voiceid":
|
|
1062
|
+
factor = buildVerifiedWalletOperationVoiceIdFactorResult({
|
|
1063
|
+
tenantId: args.authorizedOperations.tenantId,
|
|
1064
|
+
principalId: principalId.value,
|
|
1065
|
+
walletId: args.walletId,
|
|
1066
|
+
requestOrigin,
|
|
1067
|
+
audience: requestOrigin,
|
|
1068
|
+
factorId: factorId.value,
|
|
1069
|
+
authorityRef,
|
|
1070
|
+
operation,
|
|
1071
|
+
proof: requireVoiceIdProof(args.voiceIdProof),
|
|
1072
|
+
verifiedAtMs: args.verifiedAtMs,
|
|
1073
|
+
expiresAtMs: args.expiresAtMs
|
|
1074
|
+
});
|
|
1075
|
+
break;
|
|
977
1076
|
}
|
|
978
1077
|
const evidence = await args.authorizedOperations.recordVerifiedWalletOperationFactorEvidenceSet({
|
|
979
1078
|
operation,
|
|
@@ -1003,6 +1102,10 @@ async function recordFreshExportProof(args) {
|
|
|
1003
1102
|
})
|
|
1004
1103
|
};
|
|
1005
1104
|
}
|
|
1105
|
+
function requireVoiceIdProof(proof) {
|
|
1106
|
+
if (!proof) throw new Error("VoiceID export proof is required");
|
|
1107
|
+
return proof;
|
|
1108
|
+
}
|
|
1006
1109
|
var ExportOperationAdmission = class {
|
|
1007
1110
|
constructor(authorizedOperations, operation) {
|
|
1008
1111
|
this.authorizedOperations = authorizedOperations;
|
|
@@ -1053,9 +1156,10 @@ function exportOwnerProofRejected(code) {
|
|
|
1053
1156
|
});
|
|
1054
1157
|
}
|
|
1055
1158
|
var RouterAbEd25519YaoExportOwnerProofAuthorizationAdapter = class {
|
|
1056
|
-
constructor(webAuthn, emailOtp, walletAuthMethods, authorizedOperations, resolveEd25519MaterialActivation, admitEstablishedHome) {
|
|
1159
|
+
constructor(webAuthn, emailOtp, voiceId, walletAuthMethods, authorizedOperations, resolveEd25519MaterialActivation, admitEstablishedHome) {
|
|
1057
1160
|
this.webAuthn = webAuthn;
|
|
1058
1161
|
this.emailOtp = emailOtp;
|
|
1162
|
+
this.voiceId = voiceId;
|
|
1059
1163
|
this.walletAuthMethods = walletAuthMethods;
|
|
1060
1164
|
this.authorizedOperations = authorizedOperations;
|
|
1061
1165
|
this.resolveEd25519MaterialActivation = resolveEd25519MaterialActivation;
|
|
@@ -1071,6 +1175,7 @@ var RouterAbEd25519YaoExportOwnerProofAuthorizationAdapter = class {
|
|
|
1071
1175
|
input,
|
|
1072
1176
|
webAuthn: this.webAuthn,
|
|
1073
1177
|
emailOtp: this.emailOtp,
|
|
1178
|
+
voiceId: this.voiceId,
|
|
1074
1179
|
walletAuthMethods: this.walletAuthMethods,
|
|
1075
1180
|
authorizedOperations: this.authorizedOperations
|
|
1076
1181
|
});
|
|
@@ -1095,7 +1200,8 @@ var RouterAbEd25519YaoExportOwnerProofAuthorizationAdapter = class {
|
|
|
1095
1200
|
authorizationIdentity: activeIdentity.authorizationIdentity,
|
|
1096
1201
|
ownershipGeneration: home.ownershipGeneration,
|
|
1097
1202
|
admission: checked.admission,
|
|
1098
|
-
...checked.factorRelease ? { factorRelease: checked.factorRelease } : {}
|
|
1203
|
+
...checked.factorRelease ? { factorRelease: checked.factorRelease } : {},
|
|
1204
|
+
...checked.custodyRelease ? { custodyRelease: checked.custodyRelease } : {}
|
|
1099
1205
|
};
|
|
1100
1206
|
}
|
|
1101
1207
|
authorizeExecution(input) {
|
|
@@ -1112,6 +1218,29 @@ var RouterAbEd25519YaoExportOwnerProofAuthorizationAdapter = class {
|
|
|
1112
1218
|
authorizationIdentity
|
|
1113
1219
|
} : checked;
|
|
1114
1220
|
}
|
|
1221
|
+
async releaseVoiceIdCustodyForAuthorizedExport(input) {
|
|
1222
|
+
const walletId = parseWalletId(input.body.application_binding.wallet_id);
|
|
1223
|
+
if (input.authorization.kind !== "voiceid_device" || !walletId.ok) return authorizationFailure({
|
|
1224
|
+
status: 403,
|
|
1225
|
+
code: "export_authorization_method_mismatch",
|
|
1226
|
+
message: "Only a VoiceID export releases a VoiceID custody envelope"
|
|
1227
|
+
});
|
|
1228
|
+
const checked = await verifyVoiceIdExportProof({
|
|
1229
|
+
walletId: walletId.value,
|
|
1230
|
+
input,
|
|
1231
|
+
authorization: input.authorization,
|
|
1232
|
+
voiceId: this.voiceId,
|
|
1233
|
+
nowMs: Date.now()
|
|
1234
|
+
});
|
|
1235
|
+
if (!checked.ok) return checked;
|
|
1236
|
+
return {
|
|
1237
|
+
ok: true,
|
|
1238
|
+
custodyRelease: {
|
|
1239
|
+
kind: "voiceid_custody_envelope",
|
|
1240
|
+
envelope: checked.verified.custodyEnvelope
|
|
1241
|
+
}
|
|
1242
|
+
};
|
|
1243
|
+
}
|
|
1115
1244
|
async resolveAuthorizationIdentity(body) {
|
|
1116
1245
|
return await resolveActiveExportAuthorizationIdentity({
|
|
1117
1246
|
body,
|
|
@@ -1127,7 +1256,7 @@ const ROUTES = Object.freeze([defineRoute({
|
|
|
1127
1256
|
auth: {
|
|
1128
1257
|
plane: "public",
|
|
1129
1258
|
proof: "challenge_exchange",
|
|
1130
|
-
rationale: "Exact-seed export requires fresh passkey assertion
|
|
1259
|
+
rationale: "Exact-seed export requires a fresh passkey assertion, Email OTP factor possession or VoiceID device proof."
|
|
1131
1260
|
},
|
|
1132
1261
|
metering: { kind: "none" },
|
|
1133
1262
|
requiredServices: [],
|
|
@@ -1213,6 +1342,42 @@ function parseEmailOtpExportAdmissionAuthorization(authorization) {
|
|
|
1213
1342
|
}
|
|
1214
1343
|
};
|
|
1215
1344
|
}
|
|
1345
|
+
function parseVoiceIdExportAdmissionAuthorization(authorization) {
|
|
1346
|
+
const unexpectedField = firstUnexpectedField(authorization, [
|
|
1347
|
+
"kind",
|
|
1348
|
+
"walletAuthMethodId",
|
|
1349
|
+
"assertion"
|
|
1350
|
+
]);
|
|
1351
|
+
if (unexpectedField) return {
|
|
1352
|
+
ok: false,
|
|
1353
|
+
message: `VoiceID export authorization has unknown field: ${unexpectedField}`
|
|
1354
|
+
};
|
|
1355
|
+
let assertion;
|
|
1356
|
+
try {
|
|
1357
|
+
assertion = parseVoiceIdOperationAssertion(authorization.assertion);
|
|
1358
|
+
} catch (error) {
|
|
1359
|
+
return {
|
|
1360
|
+
ok: false,
|
|
1361
|
+
message: error instanceof Error ? error.message : "VoiceID export assertion is invalid"
|
|
1362
|
+
};
|
|
1363
|
+
}
|
|
1364
|
+
if (typeof authorization.walletAuthMethodId !== "string" || authorization.walletAuthMethodId !== assertion.statement.walletAuthMethodId) return {
|
|
1365
|
+
ok: false,
|
|
1366
|
+
message: "walletAuthMethodId must name the signing VoiceID method"
|
|
1367
|
+
};
|
|
1368
|
+
if (assertion.statement.operationKind !== "near.export_key") return {
|
|
1369
|
+
ok: false,
|
|
1370
|
+
message: "VoiceID export assertion names another operation"
|
|
1371
|
+
};
|
|
1372
|
+
return {
|
|
1373
|
+
ok: true,
|
|
1374
|
+
value: {
|
|
1375
|
+
kind: "voiceid_device",
|
|
1376
|
+
walletAuthMethodId: authorization.walletAuthMethodId,
|
|
1377
|
+
assertion
|
|
1378
|
+
}
|
|
1379
|
+
};
|
|
1380
|
+
}
|
|
1216
1381
|
function parseExportAdmissionAuthorization(value) {
|
|
1217
1382
|
if (!isPlainObject(value)) return {
|
|
1218
1383
|
ok: false,
|
|
@@ -1221,6 +1386,7 @@ function parseExportAdmissionAuthorization(value) {
|
|
|
1221
1386
|
switch (value.kind) {
|
|
1222
1387
|
case "passkey": return parsePasskeyExportAdmissionAuthorization(value);
|
|
1223
1388
|
case "email_otp_factor": return parseEmailOtpExportAdmissionAuthorization(value);
|
|
1389
|
+
case "voiceid_device": return parseVoiceIdExportAdmissionAuthorization(value);
|
|
1224
1390
|
default: return {
|
|
1225
1391
|
ok: false,
|
|
1226
1392
|
message: "export admission authorization kind is invalid"
|
|
@@ -1311,7 +1477,8 @@ var RouterAbEd25519YaoExportRouteExtension = class {
|
|
|
1311
1477
|
const result = await this.service.admitExport(parsed.protocol, authorized.authorizationIdentity, authorized.ownershipGeneration, authorized.admission, authorized.factorRelease ?? null, traceContext.value);
|
|
1312
1478
|
return result.ok ? json({
|
|
1313
1479
|
protocol: result.value,
|
|
1314
|
-
...authorized.factorRelease ? { factorRelease: authorized.factorRelease } : {}
|
|
1480
|
+
...authorized.factorRelease ? { factorRelease: authorized.factorRelease } : {},
|
|
1481
|
+
...authorized.custodyRelease ? { voiceIdCustodyEnvelope: authorized.custodyRelease.envelope } : {}
|
|
1315
1482
|
}, { status: result.status }) : json({
|
|
1316
1483
|
ok: false,
|
|
1317
1484
|
code: result.code,
|
package/dist/esm/router/domains/ed25519Yao/export/routerAbEd25519YaoExportRequestScopedCloudflare.js
CHANGED
|
@@ -165,7 +165,8 @@ async function handleAdmissionRequest(context, raw) {
|
|
|
165
165
|
const result = await runAdmission(context, parsed.protocol, admission.authorizationIdentity);
|
|
166
166
|
if (result.ok) return json({
|
|
167
167
|
protocol: result.value,
|
|
168
|
-
...admission.factorRelease ? { factorRelease: admission.factorRelease } : {}
|
|
168
|
+
...admission.factorRelease ? { factorRelease: admission.factorRelease } : {},
|
|
169
|
+
...admission.custodyRelease ? { voiceIdCustodyEnvelope: admission.custodyRelease.envelope } : {}
|
|
169
170
|
}, { status: result.status });
|
|
170
171
|
return exportResponse(result);
|
|
171
172
|
}
|
|
@@ -204,10 +205,22 @@ async function runAuthorization(context, parsed, expectedOrigin) {
|
|
|
204
205
|
const resolved = await context.input.authorization.resolveAuthorizationIdentity(parsed.protocol);
|
|
205
206
|
if (!resolved.ok) return resolved;
|
|
206
207
|
if (!sameAuthorizationIdentity(resolved.authorizationIdentity, existingAuthority.authorizationIdentity)) return authorizationFailure(409, "export_authorization_conflict", "Export authorization owner changed for an existing request");
|
|
208
|
+
let custodyRelease = null;
|
|
209
|
+
if (parsed.authorization.kind === "voiceid_device") {
|
|
210
|
+
const released = await context.input.authorization.releaseVoiceIdCustodyForAuthorizedExport({
|
|
211
|
+
request: context.input.request,
|
|
212
|
+
body: parsed.protocol,
|
|
213
|
+
authorization: parsed.authorization,
|
|
214
|
+
expectedOrigin
|
|
215
|
+
});
|
|
216
|
+
if (!released.ok) return released;
|
|
217
|
+
custodyRelease = released.custodyRelease;
|
|
218
|
+
}
|
|
207
219
|
verified = {
|
|
208
220
|
identity: existingAuthority.authorizationIdentity,
|
|
209
221
|
ownershipGeneration: existingAuthority.ownershipGeneration,
|
|
210
|
-
admission: null
|
|
222
|
+
admission: null,
|
|
223
|
+
...custodyRelease ? { custodyRelease } : {}
|
|
211
224
|
};
|
|
212
225
|
} else {
|
|
213
226
|
let authorized;
|
|
@@ -226,7 +239,8 @@ async function runAuthorization(context, parsed, expectedOrigin) {
|
|
|
226
239
|
identity: authorized.authorizationIdentity,
|
|
227
240
|
ownershipGeneration: authorized.ownershipGeneration,
|
|
228
241
|
admission: authorized.admission,
|
|
229
|
-
...authorized.factorRelease ? { factorRelease: authorized.factorRelease } : {}
|
|
242
|
+
...authorized.factorRelease ? { factorRelease: authorized.factorRelease } : {},
|
|
243
|
+
...authorized.custodyRelease ? { custodyRelease: authorized.custodyRelease } : {}
|
|
230
244
|
};
|
|
231
245
|
}
|
|
232
246
|
const authorizationFingerprint = await authorizationFingerprintForIdentity(parsed, verified.identity);
|
|
@@ -236,7 +250,8 @@ async function runAuthorization(context, parsed, expectedOrigin) {
|
|
|
236
250
|
identity: verified.identity,
|
|
237
251
|
ownershipGeneration: verified.ownershipGeneration,
|
|
238
252
|
admission: verified.admission,
|
|
239
|
-
...decision.value.factorRelease ? { factorRelease: decision.value.factorRelease } : {}
|
|
253
|
+
...decision.value.factorRelease ? { factorRelease: decision.value.factorRelease } : {},
|
|
254
|
+
...verified.custodyRelease ? { custodyRelease: verified.custodyRelease } : {}
|
|
240
255
|
}) : decision.value;
|
|
241
256
|
if (!verified.admission) return authorizationFailure(503, "export_authorization_uncertain", "Export authorization changed while it was being replayed");
|
|
242
257
|
const prepared = await verified.admission.prepare();
|
|
@@ -264,7 +279,8 @@ function authorizedRun(verified) {
|
|
|
264
279
|
return {
|
|
265
280
|
ok: true,
|
|
266
281
|
authorizationIdentity: verified.identity,
|
|
267
|
-
...verified.factorRelease ? { factorRelease: verified.factorRelease } : {}
|
|
282
|
+
...verified.factorRelease ? { factorRelease: verified.factorRelease } : {},
|
|
283
|
+
...verified.custodyRelease ? { custodyRelease: verified.custodyRelease } : {}
|
|
268
284
|
};
|
|
269
285
|
}
|
|
270
286
|
async function persistAuthorizationUncertain(context, request) {
|
|
@@ -266,6 +266,10 @@ function walletExecutionLaneAuthSourceFromAuthority(authority) {
|
|
|
266
266
|
kind: "passkey",
|
|
267
267
|
credentialIdB64u: authority.factor.credentialIdB64u
|
|
268
268
|
};
|
|
269
|
+
if (authority.factor.kind === "voiceid") return {
|
|
270
|
+
kind: "voiceid",
|
|
271
|
+
deviceKeyId: authority.factor.deviceKeyId
|
|
272
|
+
};
|
|
269
273
|
const providerSubject = parseProviderSubject(authority.factor.providerUserId);
|
|
270
274
|
return providerSubject.ok ? {
|
|
271
275
|
kind: "oidc_provider",
|
|
@@ -4,7 +4,8 @@ import { alphabetizeStringify, sha256BytesUtf8 } from "../../../../shared-ts/src
|
|
|
4
4
|
import { parseDigestB64u } from "../../../../shared-ts/src/utils/canonicalPrimitives.js";
|
|
5
5
|
import "../../../../shared-ts/src/utils/encoders.js";
|
|
6
6
|
import { parseAuthFactorId } from "../../../../shared-ts/src/authorization/capabilityKinds.js";
|
|
7
|
-
import {
|
|
7
|
+
import { walletAuthAuthoritiesMatch } from "../../../../shared-ts/src/utils/walletAuthAuthority.js";
|
|
8
|
+
import { buildVerifiedWalletOperationEmailOtpFactorResult, buildVerifiedWalletOperationPasskeyFactorResult, buildVerifiedWalletOperationVoiceIdFactorResult } from "../../../../authorization/factorEvidence.js";
|
|
8
9
|
import { EMAIL_OTP_CHANNEL, WALLET_EMAIL_OTP_EXPORT_OPERATION, WALLET_EMAIL_OTP_TRANSACTION_SIGN_OPERATION } from "../../../../shared-ts/src/utils/emailOtpDomain.js";
|
|
9
10
|
import { hashEmailOtpOperationBinding } from "../../../domains/emailOtp/emailOtpSessionRouteHelpers.js";
|
|
10
11
|
import { json } from "../../../framework/http.js";
|
|
@@ -152,6 +153,55 @@ async function verifyEcdsaOperationStepUpFactor(input) {
|
|
|
152
153
|
} : { kind: "not_requested" }
|
|
153
154
|
};
|
|
154
155
|
}
|
|
156
|
+
case "voiceid": {
|
|
157
|
+
if (input.operation.operation.operationKind !== "evm.export_key" || input.authSource.kind !== "voiceid" || proof.authority.factor.deviceKeyId !== input.authSource.deviceKeyId) return {
|
|
158
|
+
ok: false,
|
|
159
|
+
response: json({
|
|
160
|
+
ok: false,
|
|
161
|
+
code: "scope_mismatch",
|
|
162
|
+
message: "VoiceID authority changed"
|
|
163
|
+
}, { status: 403 })
|
|
164
|
+
};
|
|
165
|
+
const verified = await input.voiceId.verifyVoiceIdOperation({
|
|
166
|
+
walletId: input.walletId,
|
|
167
|
+
walletAuthMethodId: proof.authority.bindingId,
|
|
168
|
+
assertion: proof.assertion,
|
|
169
|
+
operationKind: "evm.export_key",
|
|
170
|
+
operationChallenge: parseDigestB64u(input.expectedChallenge),
|
|
171
|
+
requestOrigin: input.requestOrigin,
|
|
172
|
+
nowMs: Date.now()
|
|
173
|
+
});
|
|
174
|
+
if (verified.kind === "rejected" || !walletAuthAuthoritiesMatch(verified.authority, proof.authority)) return {
|
|
175
|
+
ok: false,
|
|
176
|
+
response: json({
|
|
177
|
+
ok: false,
|
|
178
|
+
code: "not_verified",
|
|
179
|
+
message: "VoiceID operation proof was not verified"
|
|
180
|
+
}, { status: 401 })
|
|
181
|
+
};
|
|
182
|
+
const verifiedAtMs = Date.now();
|
|
183
|
+
const expiresAtMs = Math.min(input.expiresAtMs, verified.proof.statement.expiresAtMs);
|
|
184
|
+
if (expiresAtMs <= verifiedAtMs) return expiredStepUpFactor();
|
|
185
|
+
const factorId = parseAuthFactorId(`voiceid:${proof.authority.factor.deviceKeyId}`);
|
|
186
|
+
if (!factorId.ok) throw new Error(factorId.error.message);
|
|
187
|
+
return {
|
|
188
|
+
ok: true,
|
|
189
|
+
factor: buildVerifiedWalletOperationVoiceIdFactorResult({
|
|
190
|
+
tenantId: input.operation.tenantId,
|
|
191
|
+
principalId: input.operation.principalId,
|
|
192
|
+
walletId: input.walletId,
|
|
193
|
+
requestOrigin: input.requestOrigin,
|
|
194
|
+
audience: input.requestOrigin,
|
|
195
|
+
factorId: factorId.value,
|
|
196
|
+
authorityRef: input.authorityRef,
|
|
197
|
+
operation: input.operation,
|
|
198
|
+
proof: verified.proof,
|
|
199
|
+
verifiedAtMs,
|
|
200
|
+
expiresAtMs
|
|
201
|
+
}),
|
|
202
|
+
unseal: { kind: "not_requested" }
|
|
203
|
+
};
|
|
204
|
+
}
|
|
155
205
|
default: throw new Error("Unsupported ECDSA operation step-up proof");
|
|
156
206
|
}
|
|
157
207
|
}
|
|
@@ -31,6 +31,7 @@ async function verifyActiveEcdsaOperationStepUpAuthority(walletAuthMethods, proo
|
|
|
31
31
|
switch (proof.kind) {
|
|
32
32
|
case "passkey": return await walletAuthMethods.verifyActivePasskeyAuthority(proof.authority);
|
|
33
33
|
case "email_otp": return await walletAuthMethods.verifyActiveEmailOtpAuthority(proof.authority);
|
|
34
|
+
case "voiceid": return { ok: true };
|
|
34
35
|
default: throw new Error("Unsupported ECDSA operation step-up proof");
|
|
35
36
|
}
|
|
36
37
|
}
|
|
@@ -569,7 +570,8 @@ async function issueEcdsaOperationStepUpAuthorization(input) {
|
|
|
569
570
|
expectedChallenge,
|
|
570
571
|
expiresAtMs,
|
|
571
572
|
webAuthn: input.ctx.service.webAuthn,
|
|
572
|
-
emailOtp: input.ctx.service.emailOtp
|
|
573
|
+
emailOtp: input.ctx.service.emailOtp,
|
|
574
|
+
voiceId: input.ctx.service.walletUnlock
|
|
573
575
|
});
|
|
574
576
|
if (!verifiedFactor.ok) return verifiedFactor.response;
|
|
575
577
|
const evidenceSet = await input.ctx.service.authorizedOperations.recordVerifiedWalletOperationFactorEvidenceSet({
|
|
@@ -22,7 +22,8 @@ const EVM_ECDSA_MPC_OPERATION_KINDS = {
|
|
|
22
22
|
const AUTHORIZATION_EVIDENCE_KINDS = {
|
|
23
23
|
seamsSession: "seams_session",
|
|
24
24
|
passkeyAssertion: "passkey_assertion",
|
|
25
|
-
emailOtp: "email_otp"
|
|
25
|
+
emailOtp: "email_otp",
|
|
26
|
+
voiceIdDevice: "voiceid_device"
|
|
26
27
|
};
|
|
27
28
|
const AUTHORIZATION_GRANT_KINDS = { walletSession: "wallet_session_authorization" };
|
|
28
29
|
const CAPABILITY_KIND_VALUES = Object.values(CAPABILITY_KINDS);
|
|
@@ -104,7 +104,76 @@ function voiceIdWalletSessionChallengeBytes(challenge) {
|
|
|
104
104
|
const payload = alphabetizeStringify(challenge);
|
|
105
105
|
return new TextEncoder().encode(`${CHALLENGE_DOMAIN}|${payload}`);
|
|
106
106
|
}
|
|
107
|
+
const OPERATION_DOMAIN = "seams:wallet-operation:voiceid-device:v1";
|
|
108
|
+
function parseVoiceIdOperationKind(value) {
|
|
109
|
+
if (value === "near.export_key" || value === "evm.export_key") return value;
|
|
110
|
+
throw new Error("VoiceID operation kind is unsupported");
|
|
111
|
+
}
|
|
112
|
+
function buildVoiceIdOperationStatement(fields) {
|
|
113
|
+
requireWindow(fields.issuedAtMs, fields.expiresAtMs);
|
|
114
|
+
requireOrigin(fields.requestOrigin, "VoiceID requestOrigin");
|
|
115
|
+
return {
|
|
116
|
+
kind: "voiceid_operation_statement_v1",
|
|
117
|
+
walletId: fields.walletId,
|
|
118
|
+
walletAuthMethodId: fields.walletAuthMethodId,
|
|
119
|
+
walletAuthorityId: fields.walletAuthorityId,
|
|
120
|
+
deviceKeyId: fields.deviceKeyId,
|
|
121
|
+
operationKind: parseVoiceIdOperationKind(fields.operationKind),
|
|
122
|
+
operationChallenge: parseDigestB64u(fields.operationChallenge),
|
|
123
|
+
requestOrigin: fields.requestOrigin,
|
|
124
|
+
issuedAtMs: fields.issuedAtMs,
|
|
125
|
+
expiresAtMs: fields.expiresAtMs
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
function parseVoiceIdOperationStatement(raw) {
|
|
129
|
+
if (!isPlainObject(raw) || !exactKeys(raw, [
|
|
130
|
+
"kind",
|
|
131
|
+
"walletId",
|
|
132
|
+
"walletAuthMethodId",
|
|
133
|
+
"walletAuthorityId",
|
|
134
|
+
"deviceKeyId",
|
|
135
|
+
"operationKind",
|
|
136
|
+
"operationChallenge",
|
|
137
|
+
"requestOrigin",
|
|
138
|
+
"issuedAtMs",
|
|
139
|
+
"expiresAtMs"
|
|
140
|
+
]) || raw.kind !== "voiceid_operation_statement_v1") throw new Error("Invalid VoiceID operation statement");
|
|
141
|
+
const walletId = parseWalletId(raw.walletId);
|
|
142
|
+
const walletAuthMethodId = parseWalletAuthMethodId(raw.walletAuthMethodId);
|
|
143
|
+
const walletAuthorityId = parseWalletAuthorityId(raw.walletAuthorityId);
|
|
144
|
+
if (!walletId.ok || !walletAuthMethodId.ok || !walletAuthorityId.ok) throw new Error("VoiceID operation identity is invalid");
|
|
145
|
+
if (typeof raw.issuedAtMs !== "number" || typeof raw.expiresAtMs !== "number") throw new Error("VoiceID operation times must be numbers");
|
|
146
|
+
return buildVoiceIdOperationStatement({
|
|
147
|
+
walletId: walletId.value,
|
|
148
|
+
walletAuthMethodId: walletAuthMethodId.value,
|
|
149
|
+
walletAuthorityId: walletAuthorityId.value,
|
|
150
|
+
deviceKeyId: parseDigestB64u(raw.deviceKeyId),
|
|
151
|
+
operationKind: parseVoiceIdOperationKind(raw.operationKind),
|
|
152
|
+
operationChallenge: parseDigestB64u(raw.operationChallenge),
|
|
153
|
+
requestOrigin: requireOrigin(raw.requestOrigin, "VoiceID requestOrigin"),
|
|
154
|
+
issuedAtMs: raw.issuedAtMs,
|
|
155
|
+
expiresAtMs: raw.expiresAtMs
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
function parseVoiceIdOperationAssertion(raw) {
|
|
159
|
+
if (!isPlainObject(raw) || !exactKeys(raw, [
|
|
160
|
+
"kind",
|
|
161
|
+
"statement",
|
|
162
|
+
"signatureB64u"
|
|
163
|
+
]) || raw.kind !== "voiceid_operation_assertion_v1" || typeof raw.signatureB64u !== "string") throw new Error("Invalid VoiceID operation assertion");
|
|
164
|
+
const signature = base64UrlDecode(raw.signatureB64u);
|
|
165
|
+
if (signature.length !== 64 || base64UrlEncode(signature) !== raw.signatureB64u) throw new Error("VoiceID signature must be canonical Ed25519 bytes");
|
|
166
|
+
return {
|
|
167
|
+
kind: "voiceid_operation_assertion_v1",
|
|
168
|
+
statement: parseVoiceIdOperationStatement(raw.statement),
|
|
169
|
+
signatureB64u: raw.signatureB64u
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
function voiceIdOperationStatementBytes(statement) {
|
|
173
|
+
const payload = alphabetizeStringify(statement);
|
|
174
|
+
return new TextEncoder().encode(`${OPERATION_DOMAIN}|${payload}`);
|
|
175
|
+
}
|
|
107
176
|
//#endregion
|
|
108
|
-
export { buildVoiceIdWalletSessionChallenge, parseVoiceIdDeviceAssertion, parseVoiceIdDevicePublicKeyB64u, parseVoiceIdWalletSessionChallenge, voiceIdWalletSessionChallengeBytes };
|
|
177
|
+
export { buildVoiceIdWalletSessionChallenge, parseVoiceIdDeviceAssertion, parseVoiceIdDevicePublicKeyB64u, parseVoiceIdOperationAssertion, parseVoiceIdWalletSessionChallenge, voiceIdOperationStatementBytes, voiceIdWalletSessionChallengeBytes };
|
|
109
178
|
|
|
110
179
|
//# sourceMappingURL=voiceIdDeviceAssertion.js.map
|
|
@@ -9,7 +9,8 @@ import { requireRouterAbX25519PublicKey } from "./routerAbPublicKeyset.js";
|
|
|
9
9
|
import { canonicalRouterAbMpcMaterialActivationRefBytes, canonicalRouterAbNormalSigningAuthorizationBytes, parseRouterAbMpcMaterialActivationRef, parseRouterAbNormalSigningAuthorization, routerAbMpcMaterialActivationRefToWire, sameRouterAbMpcMaterialActivationRef } from "./routerAbNormalSigningIdentity.js";
|
|
10
10
|
import { normalizeRuntimePolicyScope } from "../threshold/signingRootScope.js";
|
|
11
11
|
import { parseEcdsaServerGeneration } from "./ecdsaCapabilityActivation.js";
|
|
12
|
-
import { isEmailOtpWalletAuthAuthority, isPasskeyWalletAuthAuthority, parseWalletAuthAuthority } from "./walletAuthAuthority.js";
|
|
12
|
+
import { isEmailOtpWalletAuthAuthority, isPasskeyWalletAuthAuthority, isVoiceIdWalletAuthAuthority, parseWalletAuthAuthority } from "./walletAuthAuthority.js";
|
|
13
|
+
import { parseVoiceIdOperationAssertion } from "../authorization/voiceIdDeviceAssertion.js";
|
|
13
14
|
//#region ../shared-ts/src/utils/routerAbEcdsaDerivation.ts
|
|
14
15
|
/**
|
|
15
16
|
* The longest an ECDSA presignature ceremony may run. The Gateway authorizes a
|
|
@@ -1463,6 +1464,22 @@ function parseRouterAbEcdsaOperationStepUpAuthorizationRequestV1(value) {
|
|
|
1463
1464
|
otp_code: requireAsciiNonEmptyString(proof.otp_code, "operationStepUpAuthorizationRequest.proof.otp_code")
|
|
1464
1465
|
};
|
|
1465
1466
|
break;
|
|
1467
|
+
case "voiceid": {
|
|
1468
|
+
requireExactKeys(proof, "operationStepUpAuthorizationRequest.proof", [
|
|
1469
|
+
"kind",
|
|
1470
|
+
"authority",
|
|
1471
|
+
"assertion"
|
|
1472
|
+
]);
|
|
1473
|
+
if (!authority || !isVoiceIdWalletAuthAuthority(authority)) throw new Error("operationStepUpAuthorizationRequest.proof requires an exact VoiceID authority");
|
|
1474
|
+
const assertion = parseVoiceIdOperationAssertion(proof.assertion);
|
|
1475
|
+
if (assertion.statement.walletId !== authority.walletId || assertion.statement.walletAuthMethodId !== authority.bindingId || assertion.statement.deviceKeyId !== authority.factor.deviceKeyId) throw new Error("operationStepUpAuthorizationRequest.proof names another VoiceID method");
|
|
1476
|
+
parsedProof = {
|
|
1477
|
+
kind: "voiceid",
|
|
1478
|
+
authority,
|
|
1479
|
+
assertion
|
|
1480
|
+
};
|
|
1481
|
+
break;
|
|
1482
|
+
}
|
|
1466
1483
|
default: throw new Error("operationStepUpAuthorizationRequest.proof.kind is invalid");
|
|
1467
1484
|
}
|
|
1468
1485
|
return {
|
|
@@ -139,6 +139,13 @@ async function deriveRouterAbEd25519YaoExportConfirmationDigestV1(input) {
|
|
|
139
139
|
]);
|
|
140
140
|
return Array.from(await sha256(encoded));
|
|
141
141
|
}
|
|
142
|
+
function exportAuthoritySubject(authority) {
|
|
143
|
+
switch (authority.kind) {
|
|
144
|
+
case "passkey": return requireVisibleIdentifier(authority.credentialIdB64u, "credentialIdB64u");
|
|
145
|
+
case "email_otp": return requireVisibleIdentifier(authority.providerSubjectId, "providerSubjectId");
|
|
146
|
+
case "voiceid": return requireVisibleIdentifier(authority.deviceKeyId, "deviceKeyId");
|
|
147
|
+
}
|
|
148
|
+
}
|
|
142
149
|
async function deriveRouterAbEd25519YaoExportAuthorizationDigestV1(input) {
|
|
143
150
|
const confirmationDigest = requireBytes32(input.confirmationDigest, "export confirmation digest", true);
|
|
144
151
|
const nonce = requireBytes32(input.nonce, "export authorization nonce", true);
|
|
@@ -150,7 +157,7 @@ async function deriveRouterAbEd25519YaoExportAuthorizationDigestV1(input) {
|
|
|
150
157
|
labeledField("issuedAtMs", u64BigEndian(input.issuedAtMs)),
|
|
151
158
|
labeledField("expiresAtMs", u64BigEndian(input.expiresAtMs)),
|
|
152
159
|
labeledField("authorityKind", UTF8.encode(input.authority.kind)),
|
|
153
|
-
labeledField("authoritySubject", UTF8.encode(
|
|
160
|
+
labeledField("authoritySubject", UTF8.encode(exportAuthoritySubject(input.authority)))
|
|
154
161
|
]);
|
|
155
162
|
return Array.from(await sha256(encoded));
|
|
156
163
|
}
|
|
@@ -48,9 +48,10 @@ export declare const AUTHORIZATION_EVIDENCE_KINDS: {
|
|
|
48
48
|
readonly seamsSession: 'seams_session';
|
|
49
49
|
readonly passkeyAssertion: 'passkey_assertion';
|
|
50
50
|
readonly emailOtp: 'email_otp';
|
|
51
|
+
readonly voiceIdDevice: 'voiceid_device';
|
|
51
52
|
};
|
|
52
53
|
type SessionAuthorizationEvidenceKind = typeof AUTHORIZATION_EVIDENCE_KINDS.seamsSession;
|
|
53
|
-
type InteractiveAuthorizationEvidenceKind = typeof AUTHORIZATION_EVIDENCE_KINDS.passkeyAssertion | typeof AUTHORIZATION_EVIDENCE_KINDS.emailOtp;
|
|
54
|
+
type InteractiveAuthorizationEvidenceKind = typeof AUTHORIZATION_EVIDENCE_KINDS.passkeyAssertion | typeof AUTHORIZATION_EVIDENCE_KINDS.emailOtp | typeof AUTHORIZATION_EVIDENCE_KINDS.voiceIdDevice;
|
|
54
55
|
export type AuthorizationEvidenceKind = SessionAuthorizationEvidenceKind | InteractiveAuthorizationEvidenceKind;
|
|
55
56
|
export type AuthorizationEvidenceRequirement = {
|
|
56
57
|
readonly mode: 'all' | 'any';
|