diff --git a/bun.lock b/bun.lock
index fa00eed..7dcf291 100644
--- a/bun.lock
+++ b/bun.lock
@@ -1,6 +1,5 @@
{
"lockfileVersion": 1,
- "configVersion": 0,
"workspaces": {
"": {
"name": "pdfbox-ts",
@@ -14,6 +13,8 @@
"pkijs": "^3.3.3",
},
"devDependencies": {
+ "@aws-sdk/client-kms": "^3.0.0",
+ "@aws-sdk/client-secrets-manager": "^3.0.0",
"@cantoo/pdf-lib": "^2.6.1",
"@google-cloud/kms": "^5.0.0",
"@google-cloud/secret-manager": "^6.0.0",
@@ -31,16 +32,84 @@
"vitest": "^4.0.16",
},
"peerDependencies": {
+ "@aws-sdk/client-kms": "^3.0.0",
+ "@aws-sdk/client-secrets-manager": "^3.0.0",
"@google-cloud/kms": "^5.0.0",
"@google-cloud/secret-manager": "^6.0.0",
},
"optionalPeers": [
+ "@aws-sdk/client-kms",
+ "@aws-sdk/client-secrets-manager",
"@google-cloud/kms",
"@google-cloud/secret-manager",
],
},
},
"packages": {
+ "@aws-crypto/sha256-browser": ["@aws-crypto/sha256-browser@5.2.0", "", { "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", "@aws-crypto/supports-web-crypto": "^5.2.0", "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-locate-window": "^3.0.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw=="],
+
+ "@aws-crypto/sha256-js": ["@aws-crypto/sha256-js@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA=="],
+
+ "@aws-crypto/supports-web-crypto": ["@aws-crypto/supports-web-crypto@5.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg=="],
+
+ "@aws-crypto/util": ["@aws-crypto/util@5.2.0", "", { "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ=="],
+
+ "@aws-sdk/client-kms": ["@aws-sdk/client-kms@3.1034.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.974.3", "@aws-sdk/credential-provider-node": "^3.972.34", "@aws-sdk/middleware-host-header": "^3.972.10", "@aws-sdk/middleware-logger": "^3.972.10", "@aws-sdk/middleware-recursion-detection": "^3.972.11", "@aws-sdk/middleware-user-agent": "^3.972.33", "@aws-sdk/region-config-resolver": "^3.972.13", "@aws-sdk/types": "^3.973.8", "@aws-sdk/util-endpoints": "^3.996.8", "@aws-sdk/util-user-agent-browser": "^3.972.10", "@aws-sdk/util-user-agent-node": "^3.973.19", "@smithy/config-resolver": "^4.4.17", "@smithy/core": "^3.23.16", "@smithy/fetch-http-handler": "^5.3.17", "@smithy/hash-node": "^4.2.14", "@smithy/invalid-dependency": "^4.2.14", "@smithy/middleware-content-length": "^4.2.14", "@smithy/middleware-endpoint": "^4.4.31", "@smithy/middleware-retry": "^4.5.4", "@smithy/middleware-serde": "^4.2.19", "@smithy/middleware-stack": "^4.2.14", "@smithy/node-config-provider": "^4.3.14", "@smithy/node-http-handler": "^4.6.0", "@smithy/protocol-http": "^5.3.14", "@smithy/smithy-client": "^4.12.12", "@smithy/types": "^4.14.1", "@smithy/url-parser": "^4.2.14", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-body-length-node": "^4.2.3", "@smithy/util-defaults-mode-browser": "^4.3.48", "@smithy/util-defaults-mode-node": "^4.2.53", "@smithy/util-endpoints": "^3.4.2", "@smithy/util-middleware": "^4.2.14", "@smithy/util-retry": "^4.3.3", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-SJNtvd7ft/LXTN37ELP8hmM2iE4VPOyaoyg81IUyDUXJ/7fmduLBEuwDSgB19i8/aCzwEQwYM8GYF2DbF4a8fw=="],
+
+ "@aws-sdk/client-secrets-manager": ["@aws-sdk/client-secrets-manager@3.1034.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.974.3", "@aws-sdk/credential-provider-node": "^3.972.34", "@aws-sdk/middleware-host-header": "^3.972.10", "@aws-sdk/middleware-logger": "^3.972.10", "@aws-sdk/middleware-recursion-detection": "^3.972.11", "@aws-sdk/middleware-user-agent": "^3.972.33", "@aws-sdk/region-config-resolver": "^3.972.13", "@aws-sdk/types": "^3.973.8", "@aws-sdk/util-endpoints": "^3.996.8", "@aws-sdk/util-user-agent-browser": "^3.972.10", "@aws-sdk/util-user-agent-node": "^3.973.19", "@smithy/config-resolver": "^4.4.17", "@smithy/core": "^3.23.16", "@smithy/fetch-http-handler": "^5.3.17", "@smithy/hash-node": "^4.2.14", "@smithy/invalid-dependency": "^4.2.14", "@smithy/middleware-content-length": "^4.2.14", "@smithy/middleware-endpoint": "^4.4.31", "@smithy/middleware-retry": "^4.5.4", "@smithy/middleware-serde": "^4.2.19", "@smithy/middleware-stack": "^4.2.14", "@smithy/node-config-provider": "^4.3.14", "@smithy/node-http-handler": "^4.6.0", "@smithy/protocol-http": "^5.3.14", "@smithy/smithy-client": "^4.12.12", "@smithy/types": "^4.14.1", "@smithy/url-parser": "^4.2.14", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-body-length-node": "^4.2.3", "@smithy/util-defaults-mode-browser": "^4.3.48", "@smithy/util-defaults-mode-node": "^4.2.53", "@smithy/util-endpoints": "^3.4.2", "@smithy/util-middleware": "^4.2.14", "@smithy/util-retry": "^4.3.3", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-t0OVwmzNzkDswx+vFa1r3JWX0FFpzi1vkGXRtLujzeLKarTB92GjAVNqDzrjShAsf3xLLwtWWh7R9cmr6zidZQ=="],
+
+ "@aws-sdk/core": ["@aws-sdk/core@3.974.3", "", { "dependencies": { "@aws-sdk/types": "^3.973.8", "@aws-sdk/xml-builder": "^3.972.18", "@smithy/core": "^3.23.16", "@smithy/node-config-provider": "^4.3.14", "@smithy/property-provider": "^4.2.14", "@smithy/protocol-http": "^5.3.14", "@smithy/signature-v4": "^5.3.14", "@smithy/smithy-client": "^4.12.12", "@smithy/types": "^4.14.1", "@smithy/util-base64": "^4.3.2", "@smithy/util-middleware": "^4.2.14", "@smithy/util-retry": "^4.3.3", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-W3aJJm2clu8OmsrwMOMnfof13O6LGnbknnZIQeSRbxjqKah2nVvkjbUBBZVhWrt08KC69H7WsINTdrxC/2SXQw=="],
+
+ "@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.972.29", "", { "dependencies": { "@aws-sdk/core": "^3.974.3", "@aws-sdk/types": "^3.973.8", "@smithy/property-provider": "^4.2.14", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-rf+AlUxgTeSzQ/4zoS0D+Bt7XvgpY48PnWG8Yg/N9fdMgyK2Jaqa+6tLZp4MYMIMHkGrfAxnbSeb2YLMGFMg6g=="],
+
+ "@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.972.31", "", { "dependencies": { "@aws-sdk/core": "^3.974.3", "@aws-sdk/types": "^3.973.8", "@smithy/fetch-http-handler": "^5.3.17", "@smithy/node-http-handler": "^4.6.0", "@smithy/property-provider": "^4.2.14", "@smithy/protocol-http": "^5.3.14", "@smithy/smithy-client": "^4.12.12", "@smithy/types": "^4.14.1", "@smithy/util-stream": "^4.5.24", "tslib": "^2.6.2" } }, "sha512-TR2/lQ3qKFj2EOrsiASzemsNEz2uzZ/SUBf48+U4Cr9a/FZlHfH/hwAeBJNBp1gMyJNxROJZhT3dn1cO+jnYfQ=="],
+
+ "@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.972.33", "", { "dependencies": { "@aws-sdk/core": "^3.974.3", "@aws-sdk/credential-provider-env": "^3.972.29", "@aws-sdk/credential-provider-http": "^3.972.31", "@aws-sdk/credential-provider-login": "^3.972.33", "@aws-sdk/credential-provider-process": "^3.972.29", "@aws-sdk/credential-provider-sso": "^3.972.33", "@aws-sdk/credential-provider-web-identity": "^3.972.33", "@aws-sdk/nested-clients": "^3.997.1", "@aws-sdk/types": "^3.973.8", "@smithy/credential-provider-imds": "^4.2.14", "@smithy/property-provider": "^4.2.14", "@smithy/shared-ini-file-loader": "^4.4.9", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-UwdbJbOrgnOxZbshaNZ4DzX35h5wQd33MNYTGzWhN3ORG9lG9KQbDX6l6tDJSAdaGTktJoZPSritmUoW1rYkRA=="],
+
+ "@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.972.33", "", { "dependencies": { "@aws-sdk/core": "^3.974.3", "@aws-sdk/nested-clients": "^3.997.1", "@aws-sdk/types": "^3.973.8", "@smithy/property-provider": "^4.2.14", "@smithy/protocol-http": "^5.3.14", "@smithy/shared-ini-file-loader": "^4.4.9", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-WyZuPVoDM1HGNl41eVg8HSSXIB+FGkuuK63GhDbh4TMdfWU03AciWvF/QqOVWvJtWVYaLddANJ+aUklVr2ieuw=="],
+
+ "@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.972.34", "", { "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.29", "@aws-sdk/credential-provider-http": "^3.972.31", "@aws-sdk/credential-provider-ini": "^3.972.33", "@aws-sdk/credential-provider-process": "^3.972.29", "@aws-sdk/credential-provider-sso": "^3.972.33", "@aws-sdk/credential-provider-web-identity": "^3.972.33", "@aws-sdk/types": "^3.973.8", "@smithy/credential-provider-imds": "^4.2.14", "@smithy/property-provider": "^4.2.14", "@smithy/shared-ini-file-loader": "^4.4.9", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-sPcisURibKU4x0PCWJkWF1KJYm49Cph9dCn/PAnG5FU0wq5Id3g2v7RuEWAtNlKv1Af4gUJYBVGOeNpSEEx41A=="],
+
+ "@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.972.29", "", { "dependencies": { "@aws-sdk/core": "^3.974.3", "@aws-sdk/types": "^3.973.8", "@smithy/property-provider": "^4.2.14", "@smithy/shared-ini-file-loader": "^4.4.9", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-DURisqWS3bUgiwMXTmzymVNGlcRW0FnbPZ3SZknhmxnCXm3n9idkTJ6T+Uir359KRKtJNFLRViskk8HsSVLi1w=="],
+
+ "@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.972.33", "", { "dependencies": { "@aws-sdk/core": "^3.974.3", "@aws-sdk/nested-clients": "^3.997.1", "@aws-sdk/token-providers": "3.1034.0", "@aws-sdk/types": "^3.973.8", "@smithy/property-provider": "^4.2.14", "@smithy/shared-ini-file-loader": "^4.4.9", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-9y9obU4IQWru9f+NiiscUeyCe5ZmQav4FKEb1qfUNrik/C3BzBGUnHQWyPEyXjOX9cb+vx1TYx0qZBtinKdzTA=="],
+
+ "@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.972.33", "", { "dependencies": { "@aws-sdk/core": "^3.974.3", "@aws-sdk/nested-clients": "^3.997.1", "@aws-sdk/types": "^3.973.8", "@smithy/property-provider": "^4.2.14", "@smithy/shared-ini-file-loader": "^4.4.9", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-RazhlN0YAkna2T2p2v4YuuRlVBVRNo8V0SL+9JePTWDndEUAeOBAjYeQfAMbtDyCh120+zA0Op6V0jS4dw2+iw=="],
+
+ "@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.972.10", "", { "dependencies": { "@aws-sdk/types": "^3.973.8", "@smithy/protocol-http": "^5.3.14", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-IJSsIMeVQ8MMCPbuh1AbltkFhLBLXn7aejzfX5YKT/VLDHn++Dcz8886tXckE+wQssyPUhaXrJhdakO2VilRhg=="],
+
+ "@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.972.10", "", { "dependencies": { "@aws-sdk/types": "^3.973.8", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-OOuGvvz1Dm20SjZo5oEBePFqxt5nf8AwkNDSyUHvD9/bfNASmstcYxFAHUowy4n6Io7mWUZ04JURZwSBvyQanQ=="],
+
+ "@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.972.11", "", { "dependencies": { "@aws-sdk/types": "^3.973.8", "@aws/lambda-invoke-store": "^0.2.2", "@smithy/protocol-http": "^5.3.14", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-+zz6f79Kj9V5qFK2P+D8Ehjnw4AhphAlCAsPjUqEcInA9umtSSKMrHbSagEeOIsDNuvVrH98bjRHcyQukTrhaQ=="],
+
+ "@aws-sdk/middleware-sdk-s3": ["@aws-sdk/middleware-sdk-s3@3.972.32", "", { "dependencies": { "@aws-sdk/core": "^3.974.3", "@aws-sdk/types": "^3.973.8", "@aws-sdk/util-arn-parser": "^3.972.3", "@smithy/core": "^3.23.16", "@smithy/node-config-provider": "^4.3.14", "@smithy/protocol-http": "^5.3.14", "@smithy/signature-v4": "^5.3.14", "@smithy/smithy-client": "^4.12.12", "@smithy/types": "^4.14.1", "@smithy/util-config-provider": "^4.2.2", "@smithy/util-middleware": "^4.2.14", "@smithy/util-stream": "^4.5.24", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-dc2O2x0V5pGJhmdQYQveUIFtMZsur7GrGuSgoKM4oQJuEcfvwnJ3sj+ip6WnxR5l6TrX5zkl4KgcgswOy3wAzQ=="],
+
+ "@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.972.33", "", { "dependencies": { "@aws-sdk/core": "^3.974.3", "@aws-sdk/types": "^3.973.8", "@aws-sdk/util-endpoints": "^3.996.8", "@smithy/core": "^3.23.16", "@smithy/protocol-http": "^5.3.14", "@smithy/types": "^4.14.1", "@smithy/util-retry": "^4.3.3", "tslib": "^2.6.2" } }, "sha512-mqtT3Fo7xanWMk2SbAcKLGGI/q1GHWNrExBj7cnWP2W2mkTMheXB4ntJvwPZ1UxPrQobrsv2dWFXmaOJeSOiDg=="],
+
+ "@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.997.1", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.974.3", "@aws-sdk/middleware-host-header": "^3.972.10", "@aws-sdk/middleware-logger": "^3.972.10", "@aws-sdk/middleware-recursion-detection": "^3.972.11", "@aws-sdk/middleware-user-agent": "^3.972.33", "@aws-sdk/region-config-resolver": "^3.972.13", "@aws-sdk/signature-v4-multi-region": "^3.996.20", "@aws-sdk/types": "^3.973.8", "@aws-sdk/util-endpoints": "^3.996.8", "@aws-sdk/util-user-agent-browser": "^3.972.10", "@aws-sdk/util-user-agent-node": "^3.973.19", "@smithy/config-resolver": "^4.4.17", "@smithy/core": "^3.23.16", "@smithy/fetch-http-handler": "^5.3.17", "@smithy/hash-node": "^4.2.14", "@smithy/invalid-dependency": "^4.2.14", "@smithy/middleware-content-length": "^4.2.14", "@smithy/middleware-endpoint": "^4.4.31", "@smithy/middleware-retry": "^4.5.4", "@smithy/middleware-serde": "^4.2.19", "@smithy/middleware-stack": "^4.2.14", "@smithy/node-config-provider": "^4.3.14", "@smithy/node-http-handler": "^4.6.0", "@smithy/protocol-http": "^5.3.14", "@smithy/smithy-client": "^4.12.12", "@smithy/types": "^4.14.1", "@smithy/url-parser": "^4.2.14", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-body-length-node": "^4.2.3", "@smithy/util-defaults-mode-browser": "^4.3.48", "@smithy/util-defaults-mode-node": "^4.2.53", "@smithy/util-endpoints": "^3.4.2", "@smithy/util-middleware": "^4.2.14", "@smithy/util-retry": "^4.3.3", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-Afc9hc2WZs3X4Jb8dnxyuYiZsLoWRO51roTCRf497gPnAKN2WRdXANu1vaVCTzwnDMOYFXb/cYv4ZSjxqAqcKA=="],
+
+ "@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.972.13", "", { "dependencies": { "@aws-sdk/types": "^3.973.8", "@smithy/config-resolver": "^4.4.17", "@smithy/node-config-provider": "^4.3.14", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-CvJ2ZIjK/jVD/lbOpowBVElJyC1YxLTIJ13yM0AEo0t2v7swOzGjSA6lJGH+DwZXQhcjUjoYwc8bVYCX5MDr1A=="],
+
+ "@aws-sdk/signature-v4-multi-region": ["@aws-sdk/signature-v4-multi-region@3.996.20", "", { "dependencies": { "@aws-sdk/middleware-sdk-s3": "^3.972.32", "@aws-sdk/types": "^3.973.8", "@smithy/protocol-http": "^5.3.14", "@smithy/signature-v4": "^5.3.14", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-MEj6DhEcaO8RgVtFCJ+xpCQnZC3Iesr09avdY75qkMQfckQULu447IegK7Rs1MCGerVBfKnJQ4q+pQq9hI5lng=="],
+
+ "@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.1034.0", "", { "dependencies": { "@aws-sdk/core": "^3.974.3", "@aws-sdk/nested-clients": "^3.997.1", "@aws-sdk/types": "^3.973.8", "@smithy/property-provider": "^4.2.14", "@smithy/shared-ini-file-loader": "^4.4.9", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-8E+KGcD4ET0H9FXJ2/ZWbfFnQNYEkTZZYJxAs1lkdJlve1AYuqaydInIFfvNgoz5GbYtzbK8/ugsSMu5wPm6kA=="],
+
+ "@aws-sdk/types": ["@aws-sdk/types@3.973.8", "", { "dependencies": { "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-gjlAdtHMbtR9X5iIhVUvbVcy55KnznpC6bkDUWW9z915bi0ckdUr5cjf16Kp6xq0bP5HBD2xzgbL9F9Quv5vUw=="],
+
+ "@aws-sdk/util-arn-parser": ["@aws-sdk/util-arn-parser@3.972.3", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-HzSD8PMFrvgi2Kserxuff5VitNq2sgf3w9qxmskKDiDTThWfVteJxuCS9JXiPIPtmCrp+7N9asfIaVhBFORllA=="],
+
+ "@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.996.8", "", { "dependencies": { "@aws-sdk/types": "^3.973.8", "@smithy/types": "^4.14.1", "@smithy/url-parser": "^4.2.14", "@smithy/util-endpoints": "^3.4.2", "tslib": "^2.6.2" } }, "sha512-oOZHcRDihk5iEe5V25NVWg45b3qEA8OpHWVdU/XQh8Zj4heVPAJqWvMphQnU7LkufmUo10EpvFPZuQMiFLJK3g=="],
+
+ "@aws-sdk/util-locate-window": ["@aws-sdk/util-locate-window@3.965.5", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ=="],
+
+ "@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.972.10", "", { "dependencies": { "@aws-sdk/types": "^3.973.8", "@smithy/types": "^4.14.1", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-FAzqXvfEssGdSIz8ejatan0bOdx1qefBWKF/gWmVBXIP1HkS7v/wjjaqrAGGKvyihrXTXW00/2/1nTJtxpXz7g=="],
+
+ "@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.973.19", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "^3.972.33", "@aws-sdk/types": "^3.973.8", "@smithy/node-config-provider": "^4.3.14", "@smithy/types": "^4.14.1", "@smithy/util-config-provider": "^4.2.2", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-ZAfHjpzdbrzkAftC139JoYGfXzDh5HY+AxRzw8pGJ8cULsf+l721sKAMK8mV5NvRETaW/BwghSwQhGgoNgrxMw=="],
+
+ "@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.18", "", { "dependencies": { "@smithy/types": "^4.14.1", "fast-xml-parser": "5.5.8", "tslib": "^2.6.2" } }, "sha512-BMDNVG1ETXRhl1tnisQiYBef3RShJ1kfZA7x7afivTFMLirfHNTb6U71K569HNXhSXbQZsweHvSDZ6euBw8hPA=="],
+
+ "@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.2.4", "", {}, "sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ=="],
+
"@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
"@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="],
@@ -287,6 +356,84 @@
"@scure/base": ["@scure/base@2.0.0", "", {}, "sha512-3E1kpuZginKkek01ovG8krQ0Z44E3DHPjc5S2rjJw9lZn3KSQOs8S7wqikF/AH7iRanHypj85uGyxk0XAyC37w=="],
+ "@smithy/config-resolver": ["@smithy/config-resolver@4.4.17", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.14", "@smithy/types": "^4.14.1", "@smithy/util-config-provider": "^4.2.2", "@smithy/util-endpoints": "^3.4.2", "@smithy/util-middleware": "^4.2.14", "tslib": "^2.6.2" } }, "sha512-TzDZcAnhTyAHbXVxWZo7/tEcrIeFq20IBk8So3OLOetWpR8EwY/yEqBMBFaJMeyEiREDq4NfEl+qO3OAUD+vbQ=="],
+
+ "@smithy/core": ["@smithy/core@3.23.16", "", { "dependencies": { "@smithy/protocol-http": "^5.3.14", "@smithy/types": "^4.14.1", "@smithy/url-parser": "^4.2.14", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-middleware": "^4.2.14", "@smithy/util-stream": "^4.5.24", "@smithy/util-utf8": "^4.2.2", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-JStomOrINQA1VqNEopLsgcdgwd42au7mykKqVr30XFw89wLt9sDxJDi4djVPRwQmmzyTGy/uOvTc2ultMpFi1w=="],
+
+ "@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.2.14", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.14", "@smithy/property-provider": "^4.2.14", "@smithy/types": "^4.14.1", "@smithy/url-parser": "^4.2.14", "tslib": "^2.6.2" } }, "sha512-Au28zBN48ZAoXdooGUHemuVBrkE+Ie6RPmGNIAJsFqj33Vhb6xAgRifUydZ2aY+M+KaMAETAlKk5NC5h1G7wpg=="],
+
+ "@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.17", "", { "dependencies": { "@smithy/protocol-http": "^5.3.14", "@smithy/querystring-builder": "^4.2.14", "@smithy/types": "^4.14.1", "@smithy/util-base64": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-bXOvQzaSm6MnmLaWA1elgfQcAtN4UP3vXqV97bHuoOrHQOJiLT3ds6o9eo5bqd0TJfRFpzdGnDQdW3FACiAVdw=="],
+
+ "@smithy/hash-node": ["@smithy/hash-node@4.2.14", "", { "dependencies": { "@smithy/types": "^4.14.1", "@smithy/util-buffer-from": "^4.2.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-8ZBDY2DD4wr+GGjTpPtiglEsqr0lUP+KHqgZcWczFf6qeZ/YRjMIOoQWVQlmwu7EtxKTd8YXD8lblmYcpBIA1g=="],
+
+ "@smithy/invalid-dependency": ["@smithy/invalid-dependency@4.2.14", "", { "dependencies": { "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-c21qJiTSb25xvvOp+H2TNZzPCngrvl5vIPqPB8zQ/DmJF4QWXO19x1dWfMJZ6wZuuWUPPm0gV8C0cU3+ifcWuw=="],
+
+ "@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow=="],
+
+ "@smithy/middleware-content-length": ["@smithy/middleware-content-length@4.2.14", "", { "dependencies": { "@smithy/protocol-http": "^5.3.14", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-xhHq7fX4/3lv5NHxLUk3OeEvl0xZ+Ek3qIbWaCL4f9JwgDZEclPBElljaZCAItdGPQl/kSM4LPMOpy1MYgprpw=="],
+
+ "@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.31", "", { "dependencies": { "@smithy/core": "^3.23.16", "@smithy/middleware-serde": "^4.2.19", "@smithy/node-config-provider": "^4.3.14", "@smithy/shared-ini-file-loader": "^4.4.9", "@smithy/types": "^4.14.1", "@smithy/url-parser": "^4.2.14", "@smithy/util-middleware": "^4.2.14", "tslib": "^2.6.2" } }, "sha512-KJPdCIN2kOE2aGmqZd7eUTr4WQwOGgtLWgUkswGJggs7rBcQYQjcZMEDa3C0DwbOiXS9L8/wDoQHkfxBYLfiLw=="],
+
+ "@smithy/middleware-retry": ["@smithy/middleware-retry@4.5.4", "", { "dependencies": { "@smithy/core": "^3.23.16", "@smithy/node-config-provider": "^4.3.14", "@smithy/protocol-http": "^5.3.14", "@smithy/service-error-classification": "^4.3.0", "@smithy/smithy-client": "^4.12.12", "@smithy/types": "^4.14.1", "@smithy/util-middleware": "^4.2.14", "@smithy/util-retry": "^4.3.3", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-/z7nIFK+ZRW3Ie/l3NEVGdy34LvmEOzBrtBAvgWZ/4PrKX0xP3kWm8pkfcwUk523SqxZhdbQP9JSXgjF77Uhpw=="],
+
+ "@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.19", "", { "dependencies": { "@smithy/core": "^3.23.16", "@smithy/protocol-http": "^5.3.14", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-Q6y+W9h3iYVMCKWDoVge+OC1LKFqbEKaq8SIWG2X2bWJRpd/6dDLyICcNLT6PbjH3Rr6bmg/SeDB25XFOFfeEw=="],
+
+ "@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.14", "", { "dependencies": { "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-2dvkUKLuFdKsCRmOE4Mn63co0Djtsm+JMh0bYZQupN1pJwMeE8FmQmRLLzzEMN0dnNi7CDCYYH8F0EVwWiPBeA=="],
+
+ "@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.14", "", { "dependencies": { "@smithy/property-provider": "^4.2.14", "@smithy/shared-ini-file-loader": "^4.4.9", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-S+gFjyo/weSVL0P1b9Ts8C/CwIfNCgUPikk3sl6QVsfE/uUuO+QsF+NsE/JkpvWqqyz1wg7HFdiaZuj5CoBMRg=="],
+
+ "@smithy/node-http-handler": ["@smithy/node-http-handler@4.6.0", "", { "dependencies": { "@smithy/protocol-http": "^5.3.14", "@smithy/querystring-builder": "^4.2.14", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-P734cAoTFtuGfWa/R3jgBnGlURt2w9bYEBwQNMKf58sRM9RShirB2mKwLsVP+jlG/wxpCu8abv8NxdUts8tdLA=="],
+
+ "@smithy/property-provider": ["@smithy/property-provider@4.2.14", "", { "dependencies": { "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-WuM31CgfsnQ/10i7NYr0PyxqknD72Y5uMfUMVSniPjbEPceiTErb4eIqJQ+pdxNEAUEWrewrGjIRjVbVHsxZiQ=="],
+
+ "@smithy/protocol-http": ["@smithy/protocol-http@5.3.14", "", { "dependencies": { "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-dN5F8kHx8RNU0r+pCwNmFZyz6ChjMkzShy/zup6MtkRmmix4vZzJdW+di7x//b1LiynIev88FM18ie+wwPcQtQ=="],
+
+ "@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.14", "", { "dependencies": { "@smithy/types": "^4.14.1", "@smithy/util-uri-escape": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-XYA5Z0IqTeF+5XDdh4BBmSA0HvbgVZIyv4cmOoUheDNR57K1HgBp9ukUMx3Cr3XpDHHpLBnexPE3LAtDsZkj2A=="],
+
+ "@smithy/querystring-parser": ["@smithy/querystring-parser@4.2.14", "", { "dependencies": { "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-hr+YyqBD23GVvRxGGrcc/oOeNlK3PzT5Fu4dzrDXxzS1LpFiuL2PQQqKPs87M79aW7ziMs+nvB3qdw77SqE7Lw=="],
+
+ "@smithy/service-error-classification": ["@smithy/service-error-classification@4.3.0", "", { "dependencies": { "@smithy/types": "^4.14.1" } }, "sha512-9jKsBYQRPR0xBLgc2415RsA5PIcP2sis4oBdN9s0D13cg1B1284mNTjx9Yc+BEERXzuPm5ObktI96OxsKh8E9A=="],
+
+ "@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.4.9", "", { "dependencies": { "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-495/V2I15SHgedSJoDPD23JuSfKAp726ZI1V0wtjB07Wh7q/0tri/0e0DLefZCHgxZonrGKt/OCTpAtP1wE1kQ=="],
+
+ "@smithy/signature-v4": ["@smithy/signature-v4@5.3.14", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.2", "@smithy/protocol-http": "^5.3.14", "@smithy/types": "^4.14.1", "@smithy/util-hex-encoding": "^4.2.2", "@smithy/util-middleware": "^4.2.14", "@smithy/util-uri-escape": "^4.2.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-1D9Y/nmlVjCeSivCbhZ7hgEpmHyY1h0GvpSZt3l0xcD9JjmjVC1CHOozS6+Gh+/ldMH8JuJ6cujObQqfayAVFA=="],
+
+ "@smithy/smithy-client": ["@smithy/smithy-client@4.12.12", "", { "dependencies": { "@smithy/core": "^3.23.16", "@smithy/middleware-endpoint": "^4.4.31", "@smithy/middleware-stack": "^4.2.14", "@smithy/protocol-http": "^5.3.14", "@smithy/types": "^4.14.1", "@smithy/util-stream": "^4.5.24", "tslib": "^2.6.2" } }, "sha512-daO7SJn4eM6ArbmrEs+/BTbH7af8AEbSL3OMQdcRvvn8tuUcR5rU2n6DgxIV53aXMS42uwK8NgKKCh5XgqYOPQ=="],
+
+ "@smithy/types": ["@smithy/types@4.14.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-59b5HtSVrVR/eYNei3BUj3DCPKD/G7EtDDe7OEJE7i7FtQFugYo6MxbotS8mVJkLNVf8gYaAlEBwwtJ9HzhWSg=="],
+
+ "@smithy/url-parser": ["@smithy/url-parser@4.2.14", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.14", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-p06BiBigJ8bTA3MgnOfCtDUWnAMY0YfedO/GRpmc7p+wg3KW8vbXy1xwSu5ASy0wV7rRYtlfZOIKH4XqfhjSQQ=="],
+
+ "@smithy/util-base64": ["@smithy/util-base64@4.3.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ=="],
+
+ "@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.2.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-JKCrLNOup3OOgmzeaKQwi4ZCTWlYR5H4Gm1r2uTMVBXoemo1UEghk5vtMi1xSu2ymgKVGW631e2fp9/R610ZjQ=="],
+
+ "@smithy/util-body-length-node": ["@smithy/util-body-length-node@4.2.3", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-ZkJGvqBzMHVHE7r/hcuCxlTY8pQr1kMtdsVPs7ex4mMU+EAbcXppfo5NmyxMYi2XU49eqaz56j2gsk4dHHPG/g=="],
+
+ "@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.2", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q=="],
+
+ "@smithy/util-config-provider": ["@smithy/util-config-provider@4.2.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ=="],
+
+ "@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.48", "", { "dependencies": { "@smithy/property-provider": "^4.2.14", "@smithy/smithy-client": "^4.12.12", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-hxVRVPYaRDWa6YQdse1aWX1qrksmLsvNyGBKdc32q4jFzSjxYVNWfstknAfR228TnzS4tzgswXRuYIbhXBuXFQ=="],
+
+ "@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.53", "", { "dependencies": { "@smithy/config-resolver": "^4.4.17", "@smithy/credential-provider-imds": "^4.2.14", "@smithy/node-config-provider": "^4.3.14", "@smithy/property-provider": "^4.2.14", "@smithy/smithy-client": "^4.12.12", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-ybgCk+9JdBq8pYC8Y6U5fjyS8e4sboyAShetxPNL0rRBtaVl56GSFAxsolVBIea1tXR4LPIzL8i6xqmcf0+DCQ=="],
+
+ "@smithy/util-endpoints": ["@smithy/util-endpoints@3.4.2", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.14", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-a55Tr+3OKld4TTtnT+RhKOQHyPxm3j/xL4OR83WBUhLJaKDS9dnJ7arRMOp3t31dcLhApwG9bgvrRXBHlLdIkg=="],
+
+ "@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg=="],
+
+ "@smithy/util-middleware": ["@smithy/util-middleware@4.2.14", "", { "dependencies": { "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-1Su2vj9RYNDEv/V+2E+jXkkwGsgR7dc4sfHn9Z7ruzQHJIEni9zzw5CauvRXlFJfmgcqYP8fWa0dkh2Q2YaQyw=="],
+
+ "@smithy/util-retry": ["@smithy/util-retry@4.3.3", "", { "dependencies": { "@smithy/service-error-classification": "^4.3.0", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-idjUvd4M9Jj6rXkhqw4H4reHoweuK4ZxYWyOrEp4N2rOF5VtaOlQGLDQJva/8WanNXk9ScQtsAb7o5UHGvFm4A=="],
+
+ "@smithy/util-stream": ["@smithy/util-stream@4.5.24", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.17", "@smithy/node-http-handler": "^4.6.0", "@smithy/types": "^4.14.1", "@smithy/util-base64": "^4.3.2", "@smithy/util-buffer-from": "^4.2.2", "@smithy/util-hex-encoding": "^4.2.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-na5vv2mBSDzXewLEEoWGI7LQQkfpmFEomBsmOpzLFjqGctm0iMwXY5lAwesY9pIaErkccW0qzEOUcYP+WKneXg=="],
+
+ "@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw=="],
+
+ "@smithy/util-utf8": ["@smithy/util-utf8@4.2.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw=="],
+
+ "@smithy/uuid": ["@smithy/uuid@1.1.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g=="],
+
"@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
"@tootallnate/once": ["@tootallnate/once@2.0.0", "", {}, "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A=="],
@@ -347,6 +494,8 @@
"birpc": ["birpc@4.0.0", "", {}, "sha512-LShSxJP0KTmd101b6DRyGBj57LZxSDYWKitQNW/mi8GRMvZb078Uf9+pveax1DrVL89vm7mWe+TovdI/UDOuPw=="],
+ "bowser": ["bowser@2.14.1", "", {}, "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg=="],
+
"brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
@@ -419,6 +568,10 @@
"extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="],
+ "fast-xml-builder": ["fast-xml-builder@1.1.5", "", { "dependencies": { "path-expression-matcher": "^1.1.3" } }, "sha512-4TJn/8FKLeslLAH3dnohXqE3QSoxkhvaMzepOIZytwJXZO69Bfz0HBdDHzOTOon6G59Zrk6VQ2bEiv1t61rfkA=="],
+
+ "fast-xml-parser": ["fast-xml-parser@5.5.8", "", { "dependencies": { "fast-xml-builder": "^1.1.4", "path-expression-matcher": "^1.2.0", "strnum": "^2.2.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-Z7Fh2nVQSb2d+poDViM063ix2ZGt9jmY1nWhPfHBOK2Hgnb/OW3P4Et3P/81SEej0J7QbWtJqxO05h8QYfK7LQ=="],
+
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
"fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="],
@@ -553,6 +706,8 @@
"pako": ["pako@2.1.0", "", {}, "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug=="],
+ "path-expression-matcher": ["path-expression-matcher@1.5.0", "", {}, "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ=="],
+
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
"path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
@@ -639,6 +794,8 @@
"strip-ansi-cjs": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
+ "strnum": ["strnum@2.2.3", "", {}, "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg=="],
+
"stubs": ["stubs@3.0.0", "", {}, "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw=="],
"supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
@@ -697,6 +854,10 @@
"yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
+ "@aws-crypto/sha256-browser/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="],
+
+ "@aws-crypto/util/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="],
+
"@cantoo/pdf-lib/pako": ["pako@1.0.11", "", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="],
"@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
@@ -747,6 +908,10 @@
"yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
+ "@aws-crypto/sha256-browser/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="],
+
+ "@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="],
+
"@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
"cliui/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
@@ -803,6 +968,10 @@
"yargs/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
+ "@aws-crypto/sha256-browser/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="],
+
+ "@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="],
+
"yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
}
}
diff --git a/content/docs/guides/signatures/aws-kms.mdx b/content/docs/guides/signatures/aws-kms.mdx
new file mode 100644
index 0000000..373681f
--- /dev/null
+++ b/content/docs/guides/signatures/aws-kms.mdx
@@ -0,0 +1,188 @@
+---
+title: AWS KMS
+description: Sign PDFs with asymmetric keys stored in AWS Key Management Service (KMS), including FIPS 140-2-validated HSM-backed keys.
+---
+
+# AWS KMS
+
+Sign PDFs using asymmetric keys stored in AWS Key Management Service (KMS). Ideal for AWS-native deployments where private keys must stay on FIPS-validated hardware for compliance reasons.
+
+
+ The private key never leaves KMS — only the digest is sent for signing.
+
+
+## Installation
+
+The AWS KMS client is an optional peer dependency:
+
+```bash
+npm install @aws-sdk/client-kms
+```
+
+For loading certificates from AWS Secrets Manager:
+
+```bash
+npm install @aws-sdk/client-secrets-manager
+```
+
+## Quick Start
+
+```typescript
+import { PDF, AwsKmsSigner } from "@libpdf/core";
+import { readFile, writeFile } from "fs/promises";
+
+// Load your DER-encoded certificate (issued by your CA for the KMS key)
+const certificate = await readFile("certificate.der");
+
+// Create signer with KMS key reference
+const signer = await AwsKmsSigner.create({
+ keyId: "arn:aws:kms:us-east-1:123456789012:key/abcd1234-5678-...",
+ certificate,
+});
+
+// Sign the PDF
+const pdf = await PDF.load(await readFile("document.pdf"));
+const { bytes } = await pdf.sign({ signer });
+
+await writeFile("signed.pdf", bytes);
+```
+
+---
+
+## Authentication
+
+`AwsKmsSigner` uses the [AWS SDK default credential chain](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/setting-credentials-node.html). Common authentication methods:
+
+| Method | Environment | Setup |
+| ----------------------- | ------------------------ | ---------------------------------------------------------------------- |
+| IAM Role | EC2/ECS/Lambda/EKS | Attach a role; credentials resolve automatically via instance metadata |
+| Environment Variables | Any | Set `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_SESSION_TOKEN` |
+| Shared Credentials File | Local development | `aws configure` writes `~/.aws/credentials` |
+| Web Identity (OIDC) | EKS IRSA, GitHub Actions | Set `AWS_WEB_IDENTITY_TOKEN_FILE` and `AWS_ROLE_ARN` |
+
+### Required IAM Permissions
+
+The authenticating principal needs these IAM actions on the key:
+
+- `kms:Sign` — sign with the key
+- `kms:GetPublicKey` — read public key metadata for validation
+
+Minimal policy:
+
+```json
+{
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Action": ["kms:Sign", "kms:GetPublicKey"],
+ "Resource": "arn:aws:kms:us-east-1:123456789012:key/abcd1234-..."
+ }
+ ]
+}
+```
+
+If using the Secrets Manager helper, also grant `secretsmanager:GetSecretValue` on the cert secret.
+
+---
+
+## AwsKmsSigner.create(options)
+
+Create a new KMS signer instance. Calls `GetPublicKey` internally to determine supported algorithms, select one, and validate that the certificate's public key matches the KMS key.
+
+### Options
+
+```typescript
+const signer = await AwsKmsSigner.create({
+ keyId: "arn:aws:kms:us-east-1:123456789012:key/abcd1234-...",
+ region: "us-east-1",
+ certificate,
+ buildChain: true,
+});
+```
+
+| Option | Type | Description |
+| ------------------ | -------------- | -------------------------------------------------------------------------------------------------------------- |
+| `keyId` | `string` | KMS key ARN, key ID, or alias (`alias/my-key`). Required. |
+| `region` | `string` | AWS region. Optional if `AWS_REGION` env var is set or a preconfigured `client` is supplied. |
+| `signingAlgorithm` | `string` | Optional. Which `SigningAlgorithmSpec` to use when the key supports multiple. Defaults to the first supported. |
+| `certificate` | `Uint8Array` | DER-encoded X.509 signing certificate. Required. |
+| `certificateChain` | `Uint8Array[]` | Optional. DER-encoded intermediates `[intermediate, ..., root]`. |
+| `buildChain` | `boolean` | Optional. Fetch missing chain certs via AIA extensions. Default `false`. |
+| `chainTimeout` | `number` | Optional. Timeout (ms) for AIA chain building. Default `15000`. |
+| `client` | `KMSClient` | Optional. Pre-configured KMS client instance. |
+
+### Signing algorithm selection
+
+AWS KMS keys can support multiple signing algorithms (e.g. an `RSA_2048` key supports `RSASSA_PKCS1_V1_5_SHA_256/384/512` and `RSASSA_PSS_SHA_256/384/512`). `AwsKmsSigner.create()` reads `SigningAlgorithms` from the `GetPublicKey` response and uses the first one unless you pass `signingAlgorithm` explicitly.
+
+For maximum Adobe Reader compatibility, prefer PKCS#1 v1.5 over PSS:
+
+```typescript
+const signer = await AwsKmsSigner.create({
+ keyId: "alias/pdf-signing",
+ signingAlgorithm: "RSASSA_PKCS1_V1_5_SHA_256",
+ certificate,
+});
+```
+
+Supported algorithms:
+
+- `RSASSA_PKCS1_V1_5_SHA_256` / `SHA_384` / `SHA_512`
+- `RSASSA_PSS_SHA_256` / `SHA_384` / `SHA_512`
+- `ECDSA_SHA_256` / `SHA_384` / `SHA_512`
+
+---
+
+## AwsKmsSigner.getCertificateFromSecretsManager(secretId, options?)
+
+Load a PEM or DER-encoded signing certificate from AWS Secrets Manager:
+
+```typescript
+const { cert, chain } = await AwsKmsSigner.getCertificateFromSecretsManager(
+ "arn:aws:secretsmanager:us-east-1:123456789012:secret:signing-cert-AbCdEf",
+);
+
+const signer = await AwsKmsSigner.create({
+ keyId: "alias/pdf-signing",
+ certificate: cert,
+ certificateChain: chain,
+});
+```
+
+Cross-region is supported — pass `{ region: "..." }` if the secret lives in a different region than your default.
+
+
+ Never store private keys in Secrets Manager. The private key lives in KMS and never leaves it.
+
+
+---
+
+## Creating a signing key in AWS
+
+Minimal AWS CLI workflow for a new RSA 2048 signing key:
+
+```bash
+aws kms create-key \
+ --key-spec RSA_2048 \
+ --key-usage SIGN_VERIFY \
+ --description "PDF signing key"
+
+aws kms create-alias \
+ --alias-name alias/pdf-signing \
+ --target-key-id
+```
+
+For an AATL-trusted signature, submit a CSR (signed with `kms:Sign` against the KMS public key) to an Adobe Approved Trust List CA that supports HSM attestation, then use the returned certificate as `certificate` above.
+
+---
+
+## Troubleshooting
+
+**`Certificate public key does not match KMS key`** — The certificate you supplied wasn't issued for the KMS key referenced by `keyId`. Re-issue the certificate against the KMS public key (retrieve it with `aws kms get-public-key --key-id ...`).
+
+**`Permission denied for key`** — Missing `kms:Sign` or `kms:GetPublicKey` on the key. Check the key policy _and_ the IAM policy of the signing principal.
+
+**`Key is disabled`** — Enable the KMS key in the AWS console or via `aws kms enable-key`.
+
+**`Requested signing algorithm is not supported by KMS key`** — The `signingAlgorithm` you passed isn't in `GetPublicKey`'s `SigningAlgorithms` list. Remove the `signingAlgorithm` option to use the default, or choose one from the supported set.
diff --git a/content/docs/guides/signatures/index.mdx b/content/docs/guides/signatures/index.mdx
index 7b2087a..e33e13f 100644
--- a/content/docs/guides/signatures/index.mdx
+++ b/content/docs/guides/signatures/index.mdx
@@ -108,7 +108,7 @@ Adds a document timestamp that covers the embedded validation data, enabling ind
## Sign with Cloud KMS
-For enterprise environments requiring HSM-backed keys, use `GoogleKmsSigner`:
+For enterprise environments requiring HSM-backed keys, use `GoogleKmsSigner` or `AwsKmsSigner`:
```ts
import { PDF, GoogleKmsSigner } from "@libpdf/core";
@@ -123,7 +123,19 @@ const signer = await GoogleKmsSigner.create({
const signed = await pdf.sign({ signer });
```
-See the [Google Cloud KMS guide](/docs/guides/signatures/google-kms) for complete setup instructions.
+```ts
+import { PDF, AwsKmsSigner } from "@libpdf/core";
+
+const signer = await AwsKmsSigner.create({
+ keyId: "arn:aws:kms:us-east-1:123456789012:key/abcd1234-...",
+ certificate: certificateDer, // DER-encoded certificate for this KMS key
+ buildChain: true, // Auto-fetch intermediate certificates
+});
+
+const signed = await pdf.sign({ signer });
+```
+
+See the [Google Cloud KMS guide](/docs/guides/signatures/google-kms) or [AWS KMS guide](/docs/guides/signatures/aws-kms) for complete setup instructions.
## Sign with Web Crypto API
diff --git a/content/docs/guides/signatures/meta.json b/content/docs/guides/signatures/meta.json
index ae7c21e..9947614 100644
--- a/content/docs/guides/signatures/meta.json
+++ b/content/docs/guides/signatures/meta.json
@@ -1,4 +1,4 @@
{
"title": "Digital Signatures",
- "pages": ["index", "google-kms"]
+ "pages": ["index", "google-kms", "aws-kms"]
}
diff --git a/package.json b/package.json
index cc26a0f..658463a 100644
--- a/package.json
+++ b/package.json
@@ -73,6 +73,8 @@
"pkijs": "^3.3.3"
},
"devDependencies": {
+ "@aws-sdk/client-kms": "^3.0.0",
+ "@aws-sdk/client-secrets-manager": "^3.0.0",
"@cantoo/pdf-lib": "^2.6.1",
"@google-cloud/kms": "^5.0.0",
"@google-cloud/secret-manager": "^6.0.0",
@@ -90,10 +92,18 @@
"vitest": "^4.0.16"
},
"peerDependencies": {
+ "@aws-sdk/client-kms": "^3.0.0",
+ "@aws-sdk/client-secrets-manager": "^3.0.0",
"@google-cloud/kms": "^5.0.0",
"@google-cloud/secret-manager": "^6.0.0"
},
"peerDependenciesMeta": {
+ "@aws-sdk/client-kms": {
+ "optional": true
+ },
+ "@aws-sdk/client-secrets-manager": {
+ "optional": true
+ },
"@google-cloud/kms": {
"optional": true
},
diff --git a/src/index.ts b/src/index.ts
index 497d5ca..fca7e83 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -118,6 +118,7 @@ export type {
TimestampAuthority,
} from "./signatures";
export {
+ AwsKmsSigner,
CertificateChainError,
CryptoKeySigner,
GoogleKmsSigner,
diff --git a/src/signatures/index.ts b/src/signatures/index.ts
index e694e64..e7642d0 100644
--- a/src/signatures/index.ts
+++ b/src/signatures/index.ts
@@ -37,7 +37,13 @@ export {
extractOcspResponderCerts,
} from "./revocation";
// Signers
-export { CryptoKeySigner, GoogleKmsSigner, P12Signer, type P12SignerOptions } from "./signers";
+export {
+ AwsKmsSigner,
+ CryptoKeySigner,
+ GoogleKmsSigner,
+ P12Signer,
+ type P12SignerOptions,
+} from "./signers";
// Timestamp
export { HttpTimestampAuthority, type HttpTimestampAuthorityOptions } from "./timestamp";
// Types
diff --git a/src/signatures/signers/aws-kms.test.ts b/src/signatures/signers/aws-kms.test.ts
new file mode 100644
index 0000000..918c641
--- /dev/null
+++ b/src/signatures/signers/aws-kms.test.ts
@@ -0,0 +1,141 @@
+/**
+ * Tests for AwsKmsSigner.
+ *
+ * Unit tests: Pure logic (algorithm mapping, predicates).
+ * Integration tests: Real AWS KMS (skipped without AWS credentials).
+ */
+
+import { describe, expect, it } from "vitest";
+
+import { KmsSignerError } from "../types";
+import { isRsaPss, mapKmsAlgorithm } from "./aws-kms";
+
+// ─────────────────────────────────────────────────────────────────────────────
+// Unit Tests: Algorithm Mapping
+// ─────────────────────────────────────────────────────────────────────────────
+
+describe("mapKmsAlgorithm", () => {
+ describe("RSA PKCS#1 v1.5 algorithms", () => {
+ it("maps RSASSA_PKCS1_V1_5_SHA_256", () => {
+ expect(mapKmsAlgorithm("RSASSA_PKCS1_V1_5_SHA_256")).toEqual({
+ keyType: "RSA",
+ signatureAlgorithm: "RSASSA-PKCS1-v1_5",
+ digestAlgorithm: "SHA-256",
+ });
+ });
+
+ it("maps RSASSA_PKCS1_V1_5_SHA_384", () => {
+ expect(mapKmsAlgorithm("RSASSA_PKCS1_V1_5_SHA_384")).toEqual({
+ keyType: "RSA",
+ signatureAlgorithm: "RSASSA-PKCS1-v1_5",
+ digestAlgorithm: "SHA-384",
+ });
+ });
+
+ it("maps RSASSA_PKCS1_V1_5_SHA_512", () => {
+ expect(mapKmsAlgorithm("RSASSA_PKCS1_V1_5_SHA_512")).toEqual({
+ keyType: "RSA",
+ signatureAlgorithm: "RSASSA-PKCS1-v1_5",
+ digestAlgorithm: "SHA-512",
+ });
+ });
+ });
+
+ describe("RSA-PSS algorithms", () => {
+ it("maps RSASSA_PSS_SHA_256", () => {
+ expect(mapKmsAlgorithm("RSASSA_PSS_SHA_256")).toEqual({
+ keyType: "RSA",
+ signatureAlgorithm: "RSA-PSS",
+ digestAlgorithm: "SHA-256",
+ });
+ });
+
+ it("maps RSASSA_PSS_SHA_384", () => {
+ expect(mapKmsAlgorithm("RSASSA_PSS_SHA_384")).toEqual({
+ keyType: "RSA",
+ signatureAlgorithm: "RSA-PSS",
+ digestAlgorithm: "SHA-384",
+ });
+ });
+
+ it("maps RSASSA_PSS_SHA_512", () => {
+ expect(mapKmsAlgorithm("RSASSA_PSS_SHA_512")).toEqual({
+ keyType: "RSA",
+ signatureAlgorithm: "RSA-PSS",
+ digestAlgorithm: "SHA-512",
+ });
+ });
+ });
+
+ describe("ECDSA algorithms", () => {
+ it("maps ECDSA_SHA_256", () => {
+ expect(mapKmsAlgorithm("ECDSA_SHA_256")).toEqual({
+ keyType: "EC",
+ signatureAlgorithm: "ECDSA",
+ digestAlgorithm: "SHA-256",
+ });
+ });
+
+ it("maps ECDSA_SHA_384", () => {
+ expect(mapKmsAlgorithm("ECDSA_SHA_384")).toEqual({
+ keyType: "EC",
+ signatureAlgorithm: "ECDSA",
+ digestAlgorithm: "SHA-384",
+ });
+ });
+
+ it("maps ECDSA_SHA_512", () => {
+ expect(mapKmsAlgorithm("ECDSA_SHA_512")).toEqual({
+ keyType: "EC",
+ signatureAlgorithm: "ECDSA",
+ digestAlgorithm: "SHA-512",
+ });
+ });
+ });
+
+ describe("error cases", () => {
+ it("throws on unknown algorithm", () => {
+ expect(() => mapKmsAlgorithm("UNKNOWN_ALGO")).toThrow(KmsSignerError);
+ expect(() => mapKmsAlgorithm("UNKNOWN_ALGO")).toThrow(
+ /Unsupported AWS KMS signing algorithm/,
+ );
+ });
+
+ it("throws on empty string", () => {
+ expect(() => mapKmsAlgorithm("")).toThrow(KmsSignerError);
+ });
+
+ it("throws on KMS encryption algorithm (not a signing algorithm)", () => {
+ expect(() => mapKmsAlgorithm("RSAES_OAEP_SHA_256")).toThrow(KmsSignerError);
+ });
+ });
+});
+
+// ─────────────────────────────────────────────────────────────────────────────
+// Unit Tests: RSA-PSS Detection
+// ─────────────────────────────────────────────────────────────────────────────
+
+describe("isRsaPss", () => {
+ it("returns true for RSA-PSS algorithms", () => {
+ expect(isRsaPss("RSASSA_PSS_SHA_256")).toBe(true);
+ expect(isRsaPss("RSASSA_PSS_SHA_384")).toBe(true);
+ expect(isRsaPss("RSASSA_PSS_SHA_512")).toBe(true);
+ });
+
+ it("returns false for PKCS#1 v1.5", () => {
+ expect(isRsaPss("RSASSA_PKCS1_V1_5_SHA_256")).toBe(false);
+ expect(isRsaPss("RSASSA_PKCS1_V1_5_SHA_384")).toBe(false);
+ expect(isRsaPss("RSASSA_PKCS1_V1_5_SHA_512")).toBe(false);
+ });
+
+ it("returns false for ECDSA", () => {
+ expect(isRsaPss("ECDSA_SHA_256")).toBe(false);
+ expect(isRsaPss("ECDSA_SHA_384")).toBe(false);
+ expect(isRsaPss("ECDSA_SHA_512")).toBe(false);
+ });
+
+ it("returns false for unknown algorithm", () => {
+ expect(isRsaPss("UNKNOWN")).toBe(false);
+ expect(isRsaPss("")).toBe(false);
+ });
+});
diff --git a/src/signatures/signers/aws-kms.ts b/src/signatures/signers/aws-kms.ts
new file mode 100644
index 0000000..d783d58
--- /dev/null
+++ b/src/signatures/signers/aws-kms.ts
@@ -0,0 +1,657 @@
+/**
+ * AWS KMS signer.
+ *
+ * Signs using asymmetric keys stored in AWS Key Management Service (KMS),
+ * including FIPS 140-2-validated HSM-backed keys. The private key never
+ * leaves KMS - only the digest is sent for signing.
+ */
+
+import { toArrayBuffer } from "#src/helpers/buffer.ts";
+import { derToPem, isPem, normalizePem, parsePem } from "#src/helpers/pem.ts";
+import { fromBER } from "asn1js";
+import * as pkijs from "pkijs";
+
+import { buildCertificateChain } from "../aia";
+import { CertificateChainError, KmsSignerError } from "../types";
+import type { DigestAlgorithm, KeyType, SignatureAlgorithm, Signer } from "../types";
+
+// ─────────────────────────────────────────────────────────────────────────────
+// Types
+// ─────────────────────────────────────────────────────────────────────────────
+
+/** Full KMSClient type - dynamically imported */
+type KMSClient = import("@aws-sdk/client-kms").KMSClient;
+/** Subset of the KMS client methods used for signing */
+type KmsClient = Pick;
+
+/** Secrets Manager client type - dynamically imported */
+type SecretsManagerClient = import("@aws-sdk/client-secrets-manager").SecretsManagerClient;
+
+/** Supported AWS KMS signing algorithm specs */
+type AwsSigningAlgorithm =
+ | "RSASSA_PKCS1_V1_5_SHA_256"
+ | "RSASSA_PKCS1_V1_5_SHA_384"
+ | "RSASSA_PKCS1_V1_5_SHA_512"
+ | "RSASSA_PSS_SHA_256"
+ | "RSASSA_PSS_SHA_384"
+ | "RSASSA_PSS_SHA_512"
+ | "ECDSA_SHA_256"
+ | "ECDSA_SHA_384"
+ | "ECDSA_SHA_512";
+
+/** Options for AwsKmsSigner.create() */
+interface AwsKmsSignerOptions {
+ /** KMS key ARN or key ID (e.g. "arn:aws:kms:us-east-1:123456789012:key/abcd-..." or "abcd-...") */
+ keyId: string;
+
+ /**
+ * AWS region of the key. Required if no `client` is supplied and the region is
+ * not available via the AWS SDK default chain (e.g. `AWS_REGION` env var).
+ */
+ region?: string;
+
+ /**
+ * Which signing algorithm to use. AWS KMS keys may support multiple algorithms
+ * (e.g. an RSA_2048 key supports PKCS1_V1_5 and PSS at SHA-256/384/512). If
+ * omitted, the first algorithm advertised by `GetPublicKey` is used.
+ */
+ signingAlgorithm?: AwsSigningAlgorithm;
+
+ /** DER-encoded X.509 signing certificate */
+ certificate: Uint8Array;
+
+ /** Certificate chain [intermediate, ..., root] (optional) */
+ certificateChain?: Uint8Array[];
+
+ /** Build certificate chain via AIA extensions (default: false) */
+ buildChain?: boolean;
+
+ /** Timeout for AIA chain building in ms (default: 15000) */
+ chainTimeout?: number;
+
+ /** Pre-configured KMS client (optional, uses default credential chain if not provided) */
+ client?: KmsClient;
+}
+
+// ─────────────────────────────────────────────────────────────────────────────
+// Algorithm Mapping
+// ─────────────────────────────────────────────────────────────────────────────
+
+/** Mapped algorithm info from AWS KMS algorithm */
+interface AlgorithmInfo {
+ keyType: KeyType;
+ signatureAlgorithm: SignatureAlgorithm;
+ digestAlgorithm: DigestAlgorithm;
+}
+
+/** KMS algorithm to our types mapping */
+const KMS_ALGORITHM_MAP: Record = {
+ // RSA PKCS#1 v1.5
+ RSASSA_PKCS1_V1_5_SHA_256: {
+ keyType: "RSA",
+ signatureAlgorithm: "RSASSA-PKCS1-v1_5",
+ digestAlgorithm: "SHA-256",
+ },
+ RSASSA_PKCS1_V1_5_SHA_384: {
+ keyType: "RSA",
+ signatureAlgorithm: "RSASSA-PKCS1-v1_5",
+ digestAlgorithm: "SHA-384",
+ },
+ RSASSA_PKCS1_V1_5_SHA_512: {
+ keyType: "RSA",
+ signatureAlgorithm: "RSASSA-PKCS1-v1_5",
+ digestAlgorithm: "SHA-512",
+ },
+ // RSA-PSS
+ RSASSA_PSS_SHA_256: {
+ keyType: "RSA",
+ signatureAlgorithm: "RSA-PSS",
+ digestAlgorithm: "SHA-256",
+ },
+ RSASSA_PSS_SHA_384: {
+ keyType: "RSA",
+ signatureAlgorithm: "RSA-PSS",
+ digestAlgorithm: "SHA-384",
+ },
+ RSASSA_PSS_SHA_512: {
+ keyType: "RSA",
+ signatureAlgorithm: "RSA-PSS",
+ digestAlgorithm: "SHA-512",
+ },
+ // ECDSA
+ ECDSA_SHA_256: {
+ keyType: "EC",
+ signatureAlgorithm: "ECDSA",
+ digestAlgorithm: "SHA-256",
+ },
+ ECDSA_SHA_384: {
+ keyType: "EC",
+ signatureAlgorithm: "ECDSA",
+ digestAlgorithm: "SHA-384",
+ },
+ ECDSA_SHA_512: {
+ keyType: "EC",
+ signatureAlgorithm: "ECDSA",
+ digestAlgorithm: "SHA-512",
+ },
+};
+
+/**
+ * Map AWS KMS algorithm name to our types.
+ *
+ * @param algorithm - The KMS `SigningAlgorithmSpec`
+ * @returns Algorithm info (keyType, signatureAlgorithm, digestAlgorithm)
+ * @throws {KmsSignerError} if algorithm is unsupported
+ *
+ * @internal Exported for testing
+ */
+export function mapKmsAlgorithm(algorithm: string): AlgorithmInfo {
+ const info = KMS_ALGORITHM_MAP[algorithm];
+
+ if (!info) {
+ throw new KmsSignerError(`Unsupported AWS KMS signing algorithm: ${algorithm}`);
+ }
+
+ return info;
+}
+
+/**
+ * Check if an algorithm uses RSA-PSS.
+ *
+ * @internal Exported for testing
+ */
+export function isRsaPss(algorithm: string): boolean {
+ return algorithm.startsWith("RSASSA_PSS_");
+}
+
+// ─────────────────────────────────────────────────────────────────────────────
+// Certificate Utilities
+// ─────────────────────────────────────────────────────────────────────────────
+
+/**
+ * Extract public key PEM from a DER-encoded certificate.
+ */
+function extractPublicKeyFromCertificate(certDer: Uint8Array): string {
+ const asn1 = fromBER(toArrayBuffer(certDer));
+
+ if (asn1.offset === -1) {
+ throw new KmsSignerError("Failed to parse certificate");
+ }
+
+ const cert = new pkijs.Certificate({ schema: asn1.result });
+ const spki = cert.subjectPublicKeyInfo.toSchema().toBER(false);
+
+ return derToPem(new Uint8Array(spki), "PUBLIC KEY");
+}
+
+/**
+ * Check if two public keys match.
+ */
+function publicKeysMatch(kmsSpkiPem: string, certPem: string): boolean {
+ return normalizePem(kmsSpkiPem) === normalizePem(certPem);
+}
+
+// ─────────────────────────────────────────────────────────────────────────────
+// Dynamic Imports
+// ─────────────────────────────────────────────────────────────────────────────
+
+/**
+ * Dynamically import @aws-sdk/client-kms.
+ */
+async function importKms(): Promise {
+ try {
+ return await import("@aws-sdk/client-kms");
+ } catch (error) {
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion
+ const code = (error as NodeJS.ErrnoException).code;
+
+ if (code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND") {
+ throw new KmsSignerError(
+ "@aws-sdk/client-kms is required. Install with: npm install @aws-sdk/client-kms",
+ );
+ }
+
+ throw error;
+ }
+}
+
+/**
+ * Dynamically import @aws-sdk/client-secrets-manager.
+ */
+async function importSecretsManager(): Promise {
+ try {
+ return await import("@aws-sdk/client-secrets-manager");
+ } catch (error) {
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion
+ const code = (error as NodeJS.ErrnoException).code;
+
+ if (code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND") {
+ throw new KmsSignerError(
+ "@aws-sdk/client-secrets-manager is required. Install with: npm install @aws-sdk/client-secrets-manager",
+ );
+ }
+
+ throw error;
+ }
+}
+
+// ─────────────────────────────────────────────────────────────────────────────
+// AWS SDK Error Handling
+// ─────────────────────────────────────────────────────────────────────────────
+
+/**
+ * Shape of service exceptions thrown by the AWS SDK v3.
+ */
+interface AwsServiceError extends Error {
+ name: string;
+ $metadata?: { httpStatusCode?: number };
+}
+
+/**
+ * Type guard for AWS SDK service exceptions.
+ */
+function isAwsServiceError(error: unknown): error is AwsServiceError {
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion
+ const e = error as AwsServiceError;
+
+ return e instanceof Error && typeof e.name === "string";
+}
+
+// ─────────────────────────────────────────────────────────────────────────────
+// AwsKmsSigner
+// ─────────────────────────────────────────────────────────────────────────────
+
+/**
+ * Signer that uses AWS KMS for signing operations.
+ *
+ * Supports RSA and ECDSA keys stored in AWS KMS, including HSM-backed keys.
+ * The private key never leaves KMS - only the digest is sent for signing.
+ *
+ * Credentials are resolved by the AWS SDK's default credential chain
+ * (env vars, IAM task role, EC2/Lambda instance profile, shared credentials file).
+ *
+ * **Performance note:** Each `sign()` call makes a network request to KMS
+ * (~50-200ms latency). For bulk signing, consider the performance implications.
+ *
+ * @example
+ * ```typescript
+ * const signer = await AwsKmsSigner.create({
+ * keyId: "arn:aws:kms:us-east-1:123456789012:key/abcd-...",
+ * region: "us-east-1",
+ * certificate: certificateDer,
+ * });
+ *
+ * const pdf = await PDF.load(pdfBytes);
+ * const { bytes } = await pdf.sign({ signer });
+ * ```
+ */
+export class AwsKmsSigner implements Signer {
+ readonly certificate: Uint8Array;
+ readonly certificateChain: Uint8Array[];
+ readonly keyType: KeyType;
+ readonly signatureAlgorithm: SignatureAlgorithm;
+
+ /** The digest algorithm derived from the chosen KMS signing algorithm */
+ readonly digestAlgorithm: DigestAlgorithm;
+
+ /** KMS key ARN or key ID (for logging/debugging) */
+ readonly keyId: string;
+
+ /** The AWS KMS signing algorithm used on each `sign()` call */
+ readonly kmsSigningAlgorithm: AwsSigningAlgorithm;
+
+ private readonly client: KmsClient;
+
+ private constructor(
+ client: KmsClient,
+ keyId: string,
+ kmsSigningAlgorithm: AwsSigningAlgorithm,
+ certificate: Uint8Array,
+ certificateChain: Uint8Array[],
+ keyType: KeyType,
+ signatureAlgorithm: SignatureAlgorithm,
+ digestAlgorithm: DigestAlgorithm,
+ ) {
+ this.client = client;
+ this.keyId = keyId;
+ this.kmsSigningAlgorithm = kmsSigningAlgorithm;
+ this.certificate = certificate;
+ this.certificateChain = certificateChain;
+ this.keyType = keyType;
+ this.signatureAlgorithm = signatureAlgorithm;
+ this.digestAlgorithm = digestAlgorithm;
+ }
+
+ /**
+ * Create an AwsKmsSigner from a KMS key reference.
+ *
+ * Calls `GetPublicKey` on the KMS key to determine which signing algorithms
+ * are supported, selects one (the requested `signingAlgorithm` if provided,
+ * otherwise the first supported), and validates that the certificate's public
+ * key matches the KMS public key.
+ *
+ * @param options - Configuration options (key reference, certificate, etc.)
+ * @returns A new AwsKmsSigner instance
+ * @throws {KmsSignerError} if the key is invalid, the requested algorithm is
+ * unsupported on the key, or the certificate public key does not match.
+ *
+ * @example
+ * ```typescript
+ * // ARN
+ * const signer = await AwsKmsSigner.create({
+ * keyId: "arn:aws:kms:us-east-1:123456789012:key/abcd-...",
+ * certificate: certificateDer,
+ * });
+ *
+ * // Alias
+ * const signer = await AwsKmsSigner.create({
+ * keyId: "alias/my-signing-key",
+ * region: "us-east-1",
+ * certificate: certificateDer,
+ * buildChain: true,
+ * });
+ * ```
+ */
+ static async create(options: AwsKmsSignerOptions): Promise {
+ // Create or use provided client.
+ // Dynamically import KMS only if client was not provided.
+ let client: KmsClient;
+
+ if (options.client) {
+ client = options.client;
+ } else {
+ const kms = await importKms();
+
+ client = new kms.KMSClient(options.region ? { region: options.region } : {});
+ }
+
+ try {
+ // Fetch the public key + list of supported signing algorithms.
+ const kms = await importKms();
+ const publicKeyResponse = await client.send(
+ new kms.GetPublicKeyCommand({ KeyId: options.keyId }),
+ );
+
+ if (publicKeyResponse.KeyUsage && publicKeyResponse.KeyUsage !== "SIGN_VERIFY") {
+ throw new KmsSignerError(
+ `KMS key ${options.keyId} has usage ${publicKeyResponse.KeyUsage}; only SIGN_VERIFY keys can sign.`,
+ );
+ }
+
+ const supportedAlgorithms = publicKeyResponse.SigningAlgorithms ?? [];
+
+ if (supportedAlgorithms.length === 0) {
+ throw new KmsSignerError(
+ `KMS key ${options.keyId} has no signing algorithms. Ensure the key usage is SIGN_VERIFY and the key spec is RSA or ECC.`,
+ );
+ }
+
+ if (options.signingAlgorithm && !supportedAlgorithms.includes(options.signingAlgorithm)) {
+ throw new KmsSignerError(
+ `Requested signing algorithm ${options.signingAlgorithm} is not supported by KMS key ${options.keyId}. Supported: ${supportedAlgorithms.join(", ")}`,
+ );
+ }
+
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion
+ const chosenAlgorithm = (options.signingAlgorithm ??
+ supportedAlgorithms[0]) as AwsSigningAlgorithm;
+
+ const algorithmInfo = mapKmsAlgorithm(chosenAlgorithm);
+
+ // Log warning for RSA-PSS
+ if (isRsaPss(chosenAlgorithm)) {
+ console.warn(
+ "Warning: RSA-PSS signatures may not verify correctly in older PDF readers " +
+ "(Adobe Acrobat < 2020). Consider using PKCS#1 v1.5 for maximum compatibility.",
+ );
+ }
+
+ // Validate that the certificate's public key matches the KMS public key.
+ if (!publicKeyResponse.PublicKey) {
+ throw new KmsSignerError(`Failed to get public key for key: ${options.keyId}`);
+ }
+
+ const kmsSpkiPem = derToPem(publicKeyResponse.PublicKey, "PUBLIC KEY");
+ const certSpkiPem = extractPublicKeyFromCertificate(options.certificate);
+
+ if (!publicKeysMatch(kmsSpkiPem, certSpkiPem)) {
+ throw new KmsSignerError(
+ "Certificate public key does not match KMS key. " +
+ "Ensure the certificate was issued for this KMS key.",
+ );
+ }
+
+ // Build certificate chain if requested
+ let chainCertsDer: Uint8Array[] = options.certificateChain ?? [];
+
+ if (options.buildChain) {
+ try {
+ chainCertsDer = await buildCertificateChain(options.certificate, {
+ existingChain: chainCertsDer,
+ timeout: options.chainTimeout,
+ });
+ } catch (error) {
+ if (error instanceof CertificateChainError) {
+ console.warn(`Could not complete certificate chain via AIA: ${error.message}`);
+ } else {
+ throw error;
+ }
+ }
+ }
+
+ return new AwsKmsSigner(
+ client,
+ options.keyId,
+ chosenAlgorithm,
+ options.certificate,
+ chainCertsDer,
+ algorithmInfo.keyType,
+ algorithmInfo.signatureAlgorithm,
+ algorithmInfo.digestAlgorithm,
+ );
+ } catch (error) {
+ if (error instanceof KmsSignerError) {
+ throw error;
+ }
+
+ if (isAwsServiceError(error)) {
+ switch (error.name) {
+ case "NotFoundException":
+ throw new KmsSignerError(
+ `Key not found: ${options.keyId}. Verify the key ID/ARN and your permissions.`,
+ error,
+ );
+
+ case "AccessDeniedException":
+ throw new KmsSignerError(
+ `Permission denied for key: ${options.keyId}. ` +
+ `Ensure the principal has 'kms:Sign' and 'kms:GetPublicKey' permissions on this key.`,
+ error,
+ );
+
+ case "DisabledException":
+ throw new KmsSignerError(
+ `Key is disabled: ${options.keyId}. Only enabled keys can sign.`,
+ error,
+ );
+
+ case "KMSInvalidStateException":
+ throw new KmsSignerError(
+ `Key is in an invalid state: ${options.keyId}. ${error.message}`,
+ error,
+ );
+ }
+ }
+
+ const message = error instanceof Error ? error.message : String(error);
+
+ throw new KmsSignerError(`Failed to initialize KMS signer: ${message}`);
+ }
+ }
+
+ /**
+ * Loads a signing certificate from AWS Secrets Manager for use with KMS-based signing.
+ *
+ * The secret should contain the certificate material in PEM (recommended) or raw
+ * DER form:
+ * - If the secret contains PEM-encoded data, all certificates will be parsed.
+ * The first is used as the signing cert (`cert`), and the remainder returned as
+ * the optional `chain` (intermediates).
+ * - If the secret contains raw DER data, it is returned as the signing cert (`cert`).
+ *
+ * Private keys must never be stored in Secrets Manager.
+ *
+ * @param secretId ARN or name of the Secrets Manager secret.
+ * @param options Optional client configuration, including a SecretsManagerClient instance.
+ * @returns An object with `cert` (main certificate bytes) and optional `chain` (intermediates).
+ * @throws {KmsSignerError} if @aws-sdk/client-secrets-manager is not installed or retrieval fails.
+ *
+ * @example
+ * const { cert, chain } = await AwsKmsSigner.getCertificateFromSecretsManager(
+ * "arn:aws:secretsmanager:us-east-1:123456789012:secret:signing-cert-AbCdEf"
+ * );
+ *
+ * const signer = await AwsKmsSigner.create({
+ * keyId: "...",
+ * certificate: cert,
+ * certificateChain: chain,
+ * });
+ */
+ static async getCertificateFromSecretsManager(
+ secretId: string,
+ options?: { region?: string; client?: SecretsManagerClient },
+ ): Promise<{
+ cert: Uint8Array;
+ chain?: Uint8Array[];
+ }> {
+ // Dynamically import Secrets Manager
+ const secretsManager = await importSecretsManager();
+
+ const client =
+ options?.client ??
+ new secretsManager.SecretsManagerClient(options?.region ? { region: options.region } : {});
+
+ try {
+ const response = await client.send(
+ new secretsManager.GetSecretValueCommand({ SecretId: secretId }),
+ );
+
+ const payload =
+ response.SecretString ??
+ (response.SecretBinary ? new TextDecoder().decode(response.SecretBinary) : undefined);
+
+ if (!payload) {
+ throw new KmsSignerError(`Secret is empty: ${secretId}`);
+ }
+
+ if (isPem(payload)) {
+ const certs = parsePem(payload).map(block => block.der);
+
+ const [first, ...rest] = certs;
+
+ return {
+ cert: first,
+ chain: rest.length > 0 ? rest : undefined,
+ };
+ }
+
+ return {
+ cert: new TextEncoder().encode(payload),
+ };
+ } catch (error) {
+ if (error instanceof KmsSignerError) {
+ throw error;
+ }
+
+ if (isAwsServiceError(error)) {
+ switch (error.name) {
+ case "ResourceNotFoundException":
+ throw new KmsSignerError(
+ `Secret not found: ${secretId}. Verify the ARN/name and your permissions.`,
+ error,
+ );
+
+ case "AccessDeniedException":
+ throw new KmsSignerError(
+ `Permission denied for secret: ${secretId}. ` +
+ `Ensure the principal has 'secretsmanager:GetSecretValue' on this secret.`,
+ error,
+ );
+ }
+ }
+
+ const message = error instanceof Error ? error.message : String(error);
+
+ throw new KmsSignerError(`Failed to fetch certificate from Secrets Manager: ${message}`);
+ }
+ }
+
+ /**
+ * Sign data using the KMS key.
+ *
+ * The data is hashed locally using the selected digest algorithm before being
+ * sent to KMS for signing.
+ *
+ * Signature format:
+ * - RSA: PKCS#1 v1.5 or PSS signature bytes (AWS KMS returns the raw signature).
+ * - ECDSA: DER-encoded SEQUENCE {INTEGER r, INTEGER s} (AWS KMS returns DER).
+ *
+ * @param data - The data to sign
+ * @param algorithm - The digest algorithm to use (must match the KMS key)
+ * @returns The signature bytes
+ * @throws {KmsSignerError} if the digest algorithm doesn't match the key's algorithm
+ */
+ async sign(data: Uint8Array, algorithm: DigestAlgorithm): Promise {
+ if (algorithm !== this.digestAlgorithm) {
+ throw new KmsSignerError(
+ `Digest algorithm mismatch: this KMS key requires ${this.digestAlgorithm}, ` +
+ `but ${algorithm} was requested`,
+ );
+ }
+
+ const digestBuffer = await crypto.subtle.digest(algorithm, toArrayBuffer(data));
+ const digest = new Uint8Array(digestBuffer);
+
+ try {
+ const kms = await importKms();
+ const response = await this.client.send(
+ new kms.SignCommand({
+ KeyId: this.keyId,
+ Message: digest,
+ MessageType: "DIGEST",
+ SigningAlgorithm: this.kmsSigningAlgorithm,
+ }),
+ );
+
+ if (!response.Signature) {
+ throw new KmsSignerError("KMS did not return a signature");
+ }
+
+ return response.Signature;
+ } catch (error) {
+ if (error instanceof KmsSignerError) {
+ throw error;
+ }
+
+ if (isAwsServiceError(error)) {
+ switch (error.name) {
+ case "DisabledException":
+ throw new KmsSignerError(
+ `Key is disabled: ${this.keyId}. Only enabled keys can sign.`,
+ error,
+ );
+
+ case "KMSInvalidStateException":
+ throw new KmsSignerError(
+ `Key is in an invalid state: ${this.keyId}. ${error.message}`,
+ error,
+ );
+ }
+ }
+
+ const message = error instanceof Error ? error.message : String(error);
+
+ throw new KmsSignerError(`Failed to sign with KMS: ${message}`);
+ }
+ }
+}
diff --git a/src/signatures/signers/index.ts b/src/signatures/signers/index.ts
index cbe76f3..a7fd323 100644
--- a/src/signatures/signers/index.ts
+++ b/src/signatures/signers/index.ts
@@ -4,6 +4,7 @@
export type { DigestAlgorithm, KeyType, SignatureAlgorithm, Signer } from "../types";
export { KmsSignerError, SignerError } from "../types";
+export { AwsKmsSigner } from "./aws-kms";
export { CryptoKeySigner } from "./crypto-key";
export { GoogleKmsSigner } from "./google-kms";
export { P12Signer, type P12SignerOptions } from "./p12";