@h-ai/crypto 0.1.0-alpha.18 → 0.1.0-alpha.20
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 +43 -14
- package/dist/index.d.ts +126 -31
- package/dist/index.js +250 -61
- package/dist/index.js.map +1 -1
- package/package.json +6 -2
package/dist/index.js
CHANGED
|
@@ -33,7 +33,21 @@ var en_US_default = {
|
|
|
33
33
|
crypto_passwordHashEmpty: "Password and hash cannot be empty",
|
|
34
34
|
crypto_passwordHashFailed: "Password hashing failed",
|
|
35
35
|
crypto_hashFormatInvalid: "Invalid hash format",
|
|
36
|
-
crypto_passwordVerifyFailed: "Password verification failed"
|
|
36
|
+
crypto_passwordVerifyFailed: "Password verification failed",
|
|
37
|
+
crypto_transportClientKeyGenerateFailed: "Failed to generate transport client key pair",
|
|
38
|
+
crypto_transportServerKeyGenerateFailed: "Failed to generate transport server key pair",
|
|
39
|
+
crypto_transportKeyExchangeRequestFailed: "Transport key exchange request failed",
|
|
40
|
+
crypto_transportKeyExchangeHttpFailed: "Transport key exchange returned HTTP {status}",
|
|
41
|
+
crypto_transportKeyExchangeInvalidJson: "Transport key exchange response is not valid JSON",
|
|
42
|
+
crypto_transportKeyExchangeMissingFields: "Transport key exchange response is missing required fields",
|
|
43
|
+
crypto_transportClientNotInitialized: "Transport client has not completed key exchange",
|
|
44
|
+
crypto_transportClientNotRegistered: "Transport client is not registered or has expired",
|
|
45
|
+
crypto_transportRequestEncryptFailed: "Failed to encrypt transport request body",
|
|
46
|
+
crypto_transportRequestDecryptFailed: "Failed to decrypt transport request body",
|
|
47
|
+
crypto_transportResponseEncryptFailed: "Failed to encrypt transport response body",
|
|
48
|
+
crypto_transportResponseDecryptFailed: "Failed to decrypt transport response body",
|
|
49
|
+
crypto_transportSessionKeyEncryptFailed: "Failed to encrypt transport session key",
|
|
50
|
+
crypto_transportSessionKeyDecryptFailed: "Failed to decrypt transport session key"
|
|
37
51
|
};
|
|
38
52
|
|
|
39
53
|
// messages/zh-CN.json
|
|
@@ -66,7 +80,21 @@ var zh_CN_default = {
|
|
|
66
80
|
crypto_passwordHashEmpty: "\u5BC6\u7801\u548C\u54C8\u5E0C\u503C\u4E0D\u80FD\u4E3A\u7A7A",
|
|
67
81
|
crypto_passwordHashFailed: "\u5BC6\u7801\u54C8\u5E0C\u5931\u8D25",
|
|
68
82
|
crypto_hashFormatInvalid: "\u65E0\u6548\u7684\u54C8\u5E0C\u683C\u5F0F",
|
|
69
|
-
crypto_passwordVerifyFailed: "\u5BC6\u7801\u9A8C\u8BC1\u5931\u8D25"
|
|
83
|
+
crypto_passwordVerifyFailed: "\u5BC6\u7801\u9A8C\u8BC1\u5931\u8D25",
|
|
84
|
+
crypto_transportClientKeyGenerateFailed: "\u4F20\u8F93\u52A0\u5BC6\u5BA2\u6237\u7AEF\u5BC6\u94A5\u751F\u6210\u5931\u8D25",
|
|
85
|
+
crypto_transportServerKeyGenerateFailed: "\u4F20\u8F93\u52A0\u5BC6\u670D\u52A1\u7AEF\u5BC6\u94A5\u751F\u6210\u5931\u8D25",
|
|
86
|
+
crypto_transportKeyExchangeRequestFailed: "\u4F20\u8F93\u52A0\u5BC6\u5BC6\u94A5\u534F\u5546\u8BF7\u6C42\u5931\u8D25",
|
|
87
|
+
crypto_transportKeyExchangeHttpFailed: "\u4F20\u8F93\u52A0\u5BC6\u5BC6\u94A5\u534F\u5546\u8FD4\u56DE HTTP {status}",
|
|
88
|
+
crypto_transportKeyExchangeInvalidJson: "\u4F20\u8F93\u52A0\u5BC6\u5BC6\u94A5\u534F\u5546\u54CD\u5E94\u4E0D\u662F\u6709\u6548 JSON",
|
|
89
|
+
crypto_transportKeyExchangeMissingFields: "\u4F20\u8F93\u52A0\u5BC6\u5BC6\u94A5\u534F\u5546\u54CD\u5E94\u7F3A\u5C11\u5FC5\u8981\u5B57\u6BB5",
|
|
90
|
+
crypto_transportClientNotInitialized: "\u4F20\u8F93\u52A0\u5BC6\u5BA2\u6237\u7AEF\u5C1A\u672A\u5B8C\u6210\u5BC6\u94A5\u534F\u5546",
|
|
91
|
+
crypto_transportClientNotRegistered: "\u4F20\u8F93\u52A0\u5BC6\u5BA2\u6237\u7AEF\u672A\u6CE8\u518C\u6216\u5DF2\u8FC7\u671F",
|
|
92
|
+
crypto_transportRequestEncryptFailed: "\u4F20\u8F93\u52A0\u5BC6\u8BF7\u6C42\u4F53\u52A0\u5BC6\u5931\u8D25",
|
|
93
|
+
crypto_transportRequestDecryptFailed: "\u4F20\u8F93\u52A0\u5BC6\u8BF7\u6C42\u4F53\u89E3\u5BC6\u5931\u8D25",
|
|
94
|
+
crypto_transportResponseEncryptFailed: "\u4F20\u8F93\u52A0\u5BC6\u54CD\u5E94\u4F53\u52A0\u5BC6\u5931\u8D25",
|
|
95
|
+
crypto_transportResponseDecryptFailed: "\u4F20\u8F93\u52A0\u5BC6\u54CD\u5E94\u4F53\u89E3\u5BC6\u5931\u8D25",
|
|
96
|
+
crypto_transportSessionKeyEncryptFailed: "\u4F20\u8F93\u52A0\u5BC6\u4F1A\u8BDD\u5BC6\u94A5\u52A0\u5BC6\u5931\u8D25",
|
|
97
|
+
crypto_transportSessionKeyDecryptFailed: "\u4F20\u8F93\u52A0\u5BC6\u4F1A\u8BDD\u5BC6\u94A5\u89E3\u5BC6\u5931\u8D25"
|
|
70
98
|
};
|
|
71
99
|
|
|
72
100
|
// src/crypto-i18n.ts
|
|
@@ -560,49 +588,44 @@ function createSM4() {
|
|
|
560
588
|
/**
|
|
561
589
|
* SM4 对称加密
|
|
562
590
|
*
|
|
563
|
-
*
|
|
564
|
-
* CBC 模式需提供合法 IV。
|
|
591
|
+
* 默认使用 CBC 并自动生成随机 IV,返回结构化字段。
|
|
565
592
|
*
|
|
566
|
-
* ⚠️
|
|
567
|
-
*
|
|
568
|
-
* `{ mode: 'cbc', iv }`。ECB 模式仅保留兼容性,未来版本可能移除默认值。
|
|
593
|
+
* ⚠️ 安全警告:ECB 模式会让相同明文块产生相同密文块,泄漏结构信息。
|
|
594
|
+
* 生产场景请使用默认 CBC,或显式传入 `{ mode: 'cbc', iv }`。
|
|
569
595
|
*
|
|
570
596
|
* @param data - 待加密明文
|
|
571
597
|
* @param key - 密钥(32 字符十六进制)
|
|
572
598
|
* @param options - 加密模式/IV/输出格式
|
|
573
|
-
* @returns
|
|
599
|
+
* @returns 成功时返回结构化密文;失败时返回 INVALID_KEY/INVALID_IV/ENCRYPTION_FAILED
|
|
574
600
|
*/
|
|
575
601
|
encrypt(data, key, options = {}) {
|
|
576
602
|
const {
|
|
577
|
-
mode = "
|
|
603
|
+
mode = "cbc",
|
|
578
604
|
iv,
|
|
579
605
|
outputFormat = "hex"
|
|
580
606
|
} = options;
|
|
607
|
+
const actualIv = mode === "cbc" ? iv ?? this.generateIV() : void 0;
|
|
581
608
|
if (!this.isValidKey(key)) {
|
|
582
609
|
return err(
|
|
583
610
|
HaiCryptoError.INVALID_KEY,
|
|
584
611
|
cryptoM("crypto_sm4KeyInvalid")
|
|
585
612
|
);
|
|
586
613
|
}
|
|
587
|
-
if (mode === "cbc"
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
return err(
|
|
595
|
-
HaiCryptoError.INVALID_IV,
|
|
596
|
-
cryptoM("crypto_sm4IvInvalid")
|
|
597
|
-
);
|
|
614
|
+
if (mode === "cbc") {
|
|
615
|
+
if (!actualIv || !this.isValidIV(actualIv)) {
|
|
616
|
+
return err(
|
|
617
|
+
HaiCryptoError.INVALID_IV,
|
|
618
|
+
cryptoM("crypto_sm4IvInvalid")
|
|
619
|
+
);
|
|
620
|
+
}
|
|
598
621
|
}
|
|
599
622
|
try {
|
|
600
623
|
const sm4Options = {
|
|
601
624
|
mode,
|
|
602
625
|
padding: "pkcs#7"
|
|
603
626
|
};
|
|
604
|
-
if (mode === "cbc" &&
|
|
605
|
-
sm4Options.iv =
|
|
627
|
+
if (mode === "cbc" && actualIv) {
|
|
628
|
+
sm4Options.iv = actualIv;
|
|
606
629
|
}
|
|
607
630
|
const encrypted = sm4.encrypt(data, key, sm4Options);
|
|
608
631
|
if (!encrypted) {
|
|
@@ -611,10 +634,12 @@ function createSM4() {
|
|
|
611
634
|
cryptoM("crypto_sm4EncryptEmpty")
|
|
612
635
|
);
|
|
613
636
|
}
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
637
|
+
return ok({
|
|
638
|
+
mode,
|
|
639
|
+
ciphertext: encodeCiphertext(encrypted, outputFormat),
|
|
640
|
+
...actualIv ? { iv: actualIv } : {},
|
|
641
|
+
encoding: outputFormat
|
|
642
|
+
});
|
|
618
643
|
} catch (error) {
|
|
619
644
|
return err(
|
|
620
645
|
HaiCryptoError.ENCRYPTION_FAILED,
|
|
@@ -626,33 +651,28 @@ function createSM4() {
|
|
|
626
651
|
/**
|
|
627
652
|
* SM4 对称解密
|
|
628
653
|
*
|
|
629
|
-
*
|
|
630
|
-
* 解密模式和 IV 需与加密时一致。
|
|
654
|
+
* 使用结构化字段解密;解密模式、IV 与密文编码均来自 payload。
|
|
631
655
|
*
|
|
632
|
-
* @param
|
|
656
|
+
* @param payload - 结构化密文
|
|
633
657
|
* @param key - 密钥(32 字符十六进制)
|
|
634
|
-
* @param options - 解密模式/IV
|
|
635
658
|
* @returns 成功时返回明文;失败时返回 INVALID_KEY/INVALID_IV/DECRYPTION_FAILED
|
|
636
659
|
*/
|
|
637
|
-
decrypt(
|
|
638
|
-
const { mode
|
|
660
|
+
decrypt(payload, key) {
|
|
661
|
+
const { mode, iv } = payload;
|
|
639
662
|
if (!this.isValidKey(key)) {
|
|
640
663
|
return err(
|
|
641
664
|
HaiCryptoError.INVALID_KEY,
|
|
642
665
|
cryptoM("crypto_sm4KeyInvalid")
|
|
643
666
|
);
|
|
644
667
|
}
|
|
645
|
-
if (mode === "cbc" && !iv) {
|
|
668
|
+
if (mode === "cbc" && (!iv || !this.isValidIV(iv))) {
|
|
646
669
|
return err(
|
|
647
670
|
HaiCryptoError.INVALID_IV,
|
|
648
|
-
cryptoM("
|
|
671
|
+
cryptoM("crypto_sm4IvInvalid")
|
|
649
672
|
);
|
|
650
673
|
}
|
|
651
674
|
try {
|
|
652
|
-
|
|
653
|
-
if (isBase64(ciphertext)) {
|
|
654
|
-
input = base64ToHex(ciphertext);
|
|
655
|
-
}
|
|
675
|
+
const input = decodeCiphertext(payload);
|
|
656
676
|
const sm4Options = {
|
|
657
677
|
mode,
|
|
658
678
|
padding: "pkcs#7"
|
|
@@ -689,7 +709,12 @@ function createSM4() {
|
|
|
689
709
|
if (!result.success) {
|
|
690
710
|
return result;
|
|
691
711
|
}
|
|
692
|
-
return ok({
|
|
712
|
+
return ok({
|
|
713
|
+
mode: "cbc",
|
|
714
|
+
ciphertext: result.data.ciphertext,
|
|
715
|
+
iv,
|
|
716
|
+
encoding: result.data.encoding
|
|
717
|
+
});
|
|
693
718
|
},
|
|
694
719
|
/**
|
|
695
720
|
* 带 IV 解密(CBC 模式)
|
|
@@ -700,7 +725,7 @@ function createSM4() {
|
|
|
700
725
|
* @returns 成功时返回明文;失败时同 decrypt
|
|
701
726
|
*/
|
|
702
727
|
decryptWithIV(ciphertext, key, iv) {
|
|
703
|
-
return this.decrypt(
|
|
728
|
+
return this.decrypt({ mode: "cbc", ciphertext, iv, encoding: "hex" }, key);
|
|
704
729
|
},
|
|
705
730
|
/**
|
|
706
731
|
* 从密码和盐值派生密钥
|
|
@@ -737,6 +762,12 @@ function generateRandomHex(byteLength) {
|
|
|
737
762
|
crypto.getRandomValues(bytes);
|
|
738
763
|
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
739
764
|
}
|
|
765
|
+
function encodeCiphertext(ciphertext, encoding) {
|
|
766
|
+
return encoding === "base64" ? hexToBase64(ciphertext) : ciphertext;
|
|
767
|
+
}
|
|
768
|
+
function decodeCiphertext(payload) {
|
|
769
|
+
return payload.encoding === "base64" ? base64ToHex(payload.ciphertext) : payload.ciphertext;
|
|
770
|
+
}
|
|
740
771
|
|
|
741
772
|
// src/transport/crypto-transport-types.ts
|
|
742
773
|
var TRANSPORT_PROTOCOL = {
|
|
@@ -760,7 +791,7 @@ function createTransportClient(options) {
|
|
|
760
791
|
async function doInit() {
|
|
761
792
|
const kpResult = options.crypto.asymmetric.generateKeyPair();
|
|
762
793
|
if (!kpResult.success)
|
|
763
|
-
return err(HaiCommonError.INTERNAL_ERROR, "
|
|
794
|
+
return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportClientKeyGenerateFailed"), kpResult.error);
|
|
764
795
|
const localKeyPair = kpResult.data;
|
|
765
796
|
let response;
|
|
766
797
|
try {
|
|
@@ -770,18 +801,18 @@ function createTransportClient(options) {
|
|
|
770
801
|
body: JSON.stringify({ clientPublicKey: localKeyPair.publicKey })
|
|
771
802
|
});
|
|
772
803
|
} catch (cause) {
|
|
773
|
-
return err(HaiCommonError.INTERNAL_ERROR, "
|
|
804
|
+
return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportKeyExchangeRequestFailed"), cause);
|
|
774
805
|
}
|
|
775
806
|
if (!response.ok)
|
|
776
|
-
return err(HaiCommonError.INTERNAL_ERROR,
|
|
807
|
+
return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportKeyExchangeHttpFailed", { params: { status: response.status } }));
|
|
777
808
|
let body;
|
|
778
809
|
try {
|
|
779
810
|
body = await response.json();
|
|
780
811
|
} catch (cause) {
|
|
781
|
-
return err(HaiCommonError.INTERNAL_ERROR, "
|
|
812
|
+
return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportKeyExchangeInvalidJson"), cause);
|
|
782
813
|
}
|
|
783
814
|
if (!body.serverPublicKey || !body.clientId)
|
|
784
|
-
return err(HaiCommonError.INTERNAL_ERROR, "
|
|
815
|
+
return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportKeyExchangeMissingFields"));
|
|
785
816
|
keyPair = localKeyPair;
|
|
786
817
|
serverPublicKey = body.serverPublicKey;
|
|
787
818
|
clientId = body.clientId;
|
|
@@ -801,14 +832,14 @@ function createTransportClient(options) {
|
|
|
801
832
|
}
|
|
802
833
|
function encryptBody(plaintext) {
|
|
803
834
|
if (!serverPublicKey)
|
|
804
|
-
return err(HaiCommonError.INTERNAL_ERROR, "
|
|
835
|
+
return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportClientNotInitialized"));
|
|
805
836
|
const symKey = options.crypto.symmetric.generateKey();
|
|
806
837
|
const encResult = options.crypto.symmetric.encryptWithIV(plaintext, symKey);
|
|
807
838
|
if (!encResult.success)
|
|
808
|
-
return err(HaiCommonError.INTERNAL_ERROR, "
|
|
839
|
+
return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportRequestEncryptFailed"), encResult.error);
|
|
809
840
|
const keyEncResult = options.crypto.asymmetric.encrypt(symKey, serverPublicKey);
|
|
810
841
|
if (!keyEncResult.success)
|
|
811
|
-
return err(HaiCommonError.INTERNAL_ERROR, "
|
|
842
|
+
return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportSessionKeyEncryptFailed"), keyEncResult.error);
|
|
812
843
|
return ok({
|
|
813
844
|
encryptedKey: keyEncResult.data,
|
|
814
845
|
ciphertext: encResult.data.ciphertext,
|
|
@@ -817,13 +848,13 @@ function createTransportClient(options) {
|
|
|
817
848
|
}
|
|
818
849
|
function decryptPayload(payload) {
|
|
819
850
|
if (!keyPair)
|
|
820
|
-
return err(HaiCommonError.INTERNAL_ERROR, "
|
|
851
|
+
return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportClientNotInitialized"));
|
|
821
852
|
const keyDec = options.crypto.asymmetric.decrypt(payload.encryptedKey, keyPair.privateKey);
|
|
822
853
|
if (!keyDec.success)
|
|
823
|
-
return err(HaiCommonError.INTERNAL_ERROR, "
|
|
854
|
+
return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportSessionKeyDecryptFailed"), keyDec.error);
|
|
824
855
|
const dec = options.crypto.symmetric.decryptWithIV(payload.ciphertext, keyDec.data, payload.iv);
|
|
825
856
|
if (!dec.success)
|
|
826
|
-
return err(HaiCommonError.INTERNAL_ERROR, "
|
|
857
|
+
return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportResponseDecryptFailed"), dec.error);
|
|
827
858
|
return ok(dec.data);
|
|
828
859
|
}
|
|
829
860
|
const encryptedFetch = async (input, init) => {
|
|
@@ -933,14 +964,27 @@ function isEncryptedPayload(payload) {
|
|
|
933
964
|
const p = payload;
|
|
934
965
|
return typeof p.encryptedKey === "string" && typeof p.ciphertext === "string" && typeof p.iv === "string";
|
|
935
966
|
}
|
|
936
|
-
|
|
967
|
+
|
|
968
|
+
// src/transport/store-provider/crypto-transport-store-memory.ts
|
|
969
|
+
var DEFAULT_MAX_CLIENTS = 1e4;
|
|
970
|
+
function normalizeMaxClients(maxClients) {
|
|
971
|
+
if (!Number.isFinite(maxClients))
|
|
972
|
+
return DEFAULT_MAX_CLIENTS;
|
|
973
|
+
return Math.max(1, Math.floor(maxClients));
|
|
974
|
+
}
|
|
975
|
+
function createClientId(counter) {
|
|
976
|
+
const entropy = typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? crypto.randomUUID().replace(/-/g, "") : Math.random().toString(36).slice(2, 12);
|
|
977
|
+
return `c_${counter}_${Date.now()}_${entropy}`;
|
|
978
|
+
}
|
|
979
|
+
function createInMemoryKeyStore(maxClients = DEFAULT_MAX_CLIENTS) {
|
|
937
980
|
const clientKeys = /* @__PURE__ */ new Map();
|
|
981
|
+
const capacity = normalizeMaxClients(maxClients);
|
|
938
982
|
let counter = 0;
|
|
939
983
|
return {
|
|
940
984
|
async register(publicKey) {
|
|
941
985
|
counter++;
|
|
942
|
-
const clientId =
|
|
943
|
-
if (clientKeys.size >=
|
|
986
|
+
const clientId = createClientId(counter);
|
|
987
|
+
if (clientKeys.size >= capacity) {
|
|
944
988
|
const oldest = clientKeys.keys().next().value;
|
|
945
989
|
if (oldest !== void 0)
|
|
946
990
|
clientKeys.delete(oldest);
|
|
@@ -959,10 +1003,12 @@ function createInMemoryKeyStore(maxClients = 1e4) {
|
|
|
959
1003
|
}
|
|
960
1004
|
};
|
|
961
1005
|
}
|
|
1006
|
+
|
|
1007
|
+
// src/transport/crypto-transport-server.ts
|
|
962
1008
|
function createTransportEncryption(cryptoService, options = {}) {
|
|
963
1009
|
const keyPairResult = cryptoService.asymmetric.generateKeyPair();
|
|
964
1010
|
if (!keyPairResult.success)
|
|
965
|
-
return err(HaiCommonError.INTERNAL_ERROR, "
|
|
1011
|
+
return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportServerKeyGenerateFailed"), keyPairResult.error);
|
|
966
1012
|
const serverKeyPair = keyPairResult.data;
|
|
967
1013
|
const keyStore = options.keyStore ?? createInMemoryKeyStore(options.maxClients);
|
|
968
1014
|
const manager = {
|
|
@@ -978,14 +1024,14 @@ function createTransportEncryption(cryptoService, options = {}) {
|
|
|
978
1024
|
async encryptResponse(clientId, data) {
|
|
979
1025
|
const clientPublicKey = await keyStore.get(clientId);
|
|
980
1026
|
if (!clientPublicKey)
|
|
981
|
-
return err(HaiCommonError.NOT_FOUND,
|
|
1027
|
+
return err(HaiCommonError.NOT_FOUND, cryptoM("crypto_transportClientNotRegistered"));
|
|
982
1028
|
const symmetricKey = cryptoService.symmetric.generateKey();
|
|
983
1029
|
const encResult = cryptoService.symmetric.encryptWithIV(data, symmetricKey);
|
|
984
1030
|
if (!encResult.success)
|
|
985
|
-
return err(HaiCommonError.INTERNAL_ERROR, "
|
|
1031
|
+
return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportResponseEncryptFailed"), encResult.error);
|
|
986
1032
|
const keyEncResult = cryptoService.asymmetric.encrypt(symmetricKey, clientPublicKey);
|
|
987
1033
|
if (!keyEncResult.success)
|
|
988
|
-
return err(HaiCommonError.INTERNAL_ERROR, "
|
|
1034
|
+
return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportSessionKeyEncryptFailed"), keyEncResult.error);
|
|
989
1035
|
const payload = {
|
|
990
1036
|
encryptedKey: keyEncResult.data,
|
|
991
1037
|
ciphertext: encResult.data.ciphertext,
|
|
@@ -996,10 +1042,10 @@ function createTransportEncryption(cryptoService, options = {}) {
|
|
|
996
1042
|
decryptRequest(payload) {
|
|
997
1043
|
const keyDecResult = cryptoService.asymmetric.decrypt(payload.encryptedKey, serverKeyPair.privateKey);
|
|
998
1044
|
if (!keyDecResult.success)
|
|
999
|
-
return err(HaiCommonError.INTERNAL_ERROR, "
|
|
1045
|
+
return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportSessionKeyDecryptFailed"), keyDecResult.error);
|
|
1000
1046
|
const decResult = cryptoService.symmetric.decryptWithIV(payload.ciphertext, keyDecResult.data, payload.iv);
|
|
1001
1047
|
if (!decResult.success)
|
|
1002
|
-
return err(HaiCommonError.INTERNAL_ERROR, "
|
|
1048
|
+
return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportRequestDecryptFailed"), decResult.error);
|
|
1003
1049
|
return ok(decResult.data);
|
|
1004
1050
|
},
|
|
1005
1051
|
async close() {
|
|
@@ -1052,6 +1098,7 @@ var crypto2 = {
|
|
|
1052
1098
|
}
|
|
1053
1099
|
initInProgress = true;
|
|
1054
1100
|
try {
|
|
1101
|
+
await Promise.resolve();
|
|
1055
1102
|
if (initialized) {
|
|
1056
1103
|
logger.warn("Crypto module is already initialized, reinitializing");
|
|
1057
1104
|
await crypto2.close();
|
|
@@ -1129,6 +1176,148 @@ var crypto2 = {
|
|
|
1129
1176
|
}
|
|
1130
1177
|
};
|
|
1131
1178
|
|
|
1132
|
-
|
|
1179
|
+
// src/transport/store-provider/crypto-transport-store-redis.ts
|
|
1180
|
+
var TRANSPORT_KEY_CACHE_PREFIX = "hai:crypto:transport:client";
|
|
1181
|
+
function normalizeTtlSeconds(ttlSeconds) {
|
|
1182
|
+
if (ttlSeconds === void 0 || !Number.isFinite(ttlSeconds) || ttlSeconds <= 0)
|
|
1183
|
+
return void 0;
|
|
1184
|
+
return Math.floor(ttlSeconds);
|
|
1185
|
+
}
|
|
1186
|
+
function createClientId2() {
|
|
1187
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function")
|
|
1188
|
+
return `c_${crypto.randomUUID()}`;
|
|
1189
|
+
return `c_${Date.now()}_${Math.random().toString(36).slice(2, 12)}`;
|
|
1190
|
+
}
|
|
1191
|
+
function toOperationError(operation, error) {
|
|
1192
|
+
const wrapped = new Error(`${operation} failed: ${error.message}`, { cause: error });
|
|
1193
|
+
wrapped.code = error.code;
|
|
1194
|
+
return wrapped;
|
|
1195
|
+
}
|
|
1196
|
+
function unwrapResult(operation, result) {
|
|
1197
|
+
if (result.success)
|
|
1198
|
+
return result.data;
|
|
1199
|
+
throw toOperationError(operation, result.error);
|
|
1200
|
+
}
|
|
1201
|
+
function createRedisTransportKeyStore(options) {
|
|
1202
|
+
const ttlSeconds = normalizeTtlSeconds(options.ttlSeconds);
|
|
1203
|
+
function buildCacheKey(clientId) {
|
|
1204
|
+
return `${TRANSPORT_KEY_CACHE_PREFIX}:${clientId}`;
|
|
1205
|
+
}
|
|
1206
|
+
return {
|
|
1207
|
+
async register(publicKey) {
|
|
1208
|
+
const clientId = createClientId2();
|
|
1209
|
+
unwrapResult(
|
|
1210
|
+
"cache.kv.set",
|
|
1211
|
+
await options.cache.kv.set(
|
|
1212
|
+
buildCacheKey(clientId),
|
|
1213
|
+
publicKey,
|
|
1214
|
+
ttlSeconds === void 0 ? void 0 : { ex: ttlSeconds }
|
|
1215
|
+
)
|
|
1216
|
+
);
|
|
1217
|
+
return clientId;
|
|
1218
|
+
},
|
|
1219
|
+
async get(clientId) {
|
|
1220
|
+
const publicKey = unwrapResult(
|
|
1221
|
+
"cache.kv.get",
|
|
1222
|
+
await options.cache.kv.get(buildCacheKey(clientId))
|
|
1223
|
+
);
|
|
1224
|
+
return publicKey ?? void 0;
|
|
1225
|
+
},
|
|
1226
|
+
async delete(clientId) {
|
|
1227
|
+
unwrapResult("cache.kv.del", await options.cache.kv.del(buildCacheKey(clientId)));
|
|
1228
|
+
}
|
|
1229
|
+
};
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
// src/transport/store-provider/crypto-transport-store-reldb.ts
|
|
1233
|
+
var TRANSPORT_KEY_TABLE = "hai_crypto_transport_client_keys";
|
|
1234
|
+
function normalizeTtlSeconds2(ttlSeconds) {
|
|
1235
|
+
if (ttlSeconds === void 0 || !Number.isFinite(ttlSeconds) || ttlSeconds <= 0)
|
|
1236
|
+
return void 0;
|
|
1237
|
+
return Math.floor(ttlSeconds);
|
|
1238
|
+
}
|
|
1239
|
+
function createClientId3() {
|
|
1240
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function")
|
|
1241
|
+
return `c_${crypto.randomUUID()}`;
|
|
1242
|
+
return `c_${Date.now()}_${Math.random().toString(36).slice(2, 12)}`;
|
|
1243
|
+
}
|
|
1244
|
+
function toOperationError2(operation, error) {
|
|
1245
|
+
const wrapped = new Error(`${operation} failed: ${error.message}`, { cause: error });
|
|
1246
|
+
wrapped.code = error.code;
|
|
1247
|
+
return wrapped;
|
|
1248
|
+
}
|
|
1249
|
+
function unwrapResult2(operation, result) {
|
|
1250
|
+
if (result.success)
|
|
1251
|
+
return result.data;
|
|
1252
|
+
throw toOperationError2(operation, result.error);
|
|
1253
|
+
}
|
|
1254
|
+
function createReldbTransportKeyStore(options) {
|
|
1255
|
+
const ttlSeconds = normalizeTtlSeconds2(options.ttlSeconds);
|
|
1256
|
+
let ensureTablePromise = null;
|
|
1257
|
+
async function ensureTable() {
|
|
1258
|
+
if (!ensureTablePromise) {
|
|
1259
|
+
ensureTablePromise = (async () => {
|
|
1260
|
+
unwrapResult2(
|
|
1261
|
+
"reldb.ddl.createTable",
|
|
1262
|
+
await options.reldb.ddl.createTable(TRANSPORT_KEY_TABLE, {
|
|
1263
|
+
client_id: { type: "TEXT", primaryKey: true },
|
|
1264
|
+
public_key: { type: "TEXT", notNull: true },
|
|
1265
|
+
created_at: { type: "TIMESTAMP", notNull: true },
|
|
1266
|
+
expires_at: { type: "TIMESTAMP" }
|
|
1267
|
+
}, true)
|
|
1268
|
+
);
|
|
1269
|
+
})();
|
|
1270
|
+
}
|
|
1271
|
+
return ensureTablePromise;
|
|
1272
|
+
}
|
|
1273
|
+
async function deleteClientKey(clientId) {
|
|
1274
|
+
unwrapResult2(
|
|
1275
|
+
"reldb.sql.execute",
|
|
1276
|
+
await options.reldb.sql.execute(
|
|
1277
|
+
`DELETE FROM ${TRANSPORT_KEY_TABLE} WHERE client_id = ?`,
|
|
1278
|
+
[clientId]
|
|
1279
|
+
)
|
|
1280
|
+
);
|
|
1281
|
+
}
|
|
1282
|
+
return {
|
|
1283
|
+
async register(publicKey) {
|
|
1284
|
+
await ensureTable();
|
|
1285
|
+
const clientId = createClientId3();
|
|
1286
|
+
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1287
|
+
const expiresAt = ttlSeconds === void 0 ? null : new Date(Date.now() + ttlSeconds * 1e3).toISOString();
|
|
1288
|
+
unwrapResult2(
|
|
1289
|
+
"reldb.sql.execute",
|
|
1290
|
+
await options.reldb.sql.execute(
|
|
1291
|
+
`INSERT INTO ${TRANSPORT_KEY_TABLE} (client_id, public_key, created_at, expires_at) VALUES (?, ?, ?, ?)`,
|
|
1292
|
+
[clientId, publicKey, createdAt, expiresAt]
|
|
1293
|
+
)
|
|
1294
|
+
);
|
|
1295
|
+
return clientId;
|
|
1296
|
+
},
|
|
1297
|
+
async get(clientId) {
|
|
1298
|
+
await ensureTable();
|
|
1299
|
+
const row = unwrapResult2(
|
|
1300
|
+
"reldb.sql.get",
|
|
1301
|
+
await options.reldb.sql.get(
|
|
1302
|
+
`SELECT public_key, expires_at FROM ${TRANSPORT_KEY_TABLE} WHERE client_id = ?`,
|
|
1303
|
+
[clientId]
|
|
1304
|
+
)
|
|
1305
|
+
);
|
|
1306
|
+
if (!row)
|
|
1307
|
+
return void 0;
|
|
1308
|
+
if (row.expires_at && Date.parse(row.expires_at) <= Date.now()) {
|
|
1309
|
+
await deleteClientKey(clientId);
|
|
1310
|
+
return void 0;
|
|
1311
|
+
}
|
|
1312
|
+
return row.public_key;
|
|
1313
|
+
},
|
|
1314
|
+
async delete(clientId) {
|
|
1315
|
+
await ensureTable();
|
|
1316
|
+
await deleteClientKey(clientId);
|
|
1317
|
+
}
|
|
1318
|
+
};
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
export { HaiCryptoError, TRANSPORT_PROTOCOL, createInMemoryKeyStore, createRedisTransportKeyStore, createReldbTransportKeyStore, crypto2 as crypto };
|
|
1133
1322
|
//# sourceMappingURL=index.js.map
|
|
1134
1323
|
//# sourceMappingURL=index.js.map
|