@yielded/crypto 0.1.0-beta.27 → 0.1.0-beta.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/Aead.d.mts +26 -1
- package/dist/Aead.mjs +16 -1
- package/dist/Aead.mjs.map +1 -1
- package/dist/Signature.d.mts +23 -1
- package/dist/Signature.mjs +11 -5
- package/dist/Signature.mjs.map +1 -1
- package/dist/internal/aead.mjs +112 -68
- package/dist/internal/aead.mjs.map +1 -1
- package/dist/internal/argon2.mjs +8 -3
- package/dist/internal/argon2.mjs.map +1 -1
- package/dist/internal/hmac.mjs +12 -26
- package/dist/internal/hmac.mjs.map +1 -1
- package/dist/internal/scoped-key.mjs +31 -0
- package/dist/internal/scoped-key.mjs.map +1 -0
- package/dist/internal/signature.mjs +32 -1
- package/dist/internal/signature.mjs.map +1 -1
- package/dist/internal/xchacha-core.mjs +4 -2
- package/dist/internal/xchacha-core.mjs.map +1 -1
- package/package.json +1 -1
- package/src/Aead.ts +35 -1
- package/src/Signature.ts +37 -4
- package/src/internal/aead.ts +97 -31
- package/src/internal/argon2.ts +13 -4
- package/src/internal/hmac.ts +15 -56
- package/src/internal/scoped-key.ts +60 -0
- package/src/internal/signature.ts +54 -0
- package/src/internal/xchacha-core.ts +6 -2
package/README.md
CHANGED
|
@@ -23,16 +23,16 @@ Full backend Layers require one shared `KdfAdmission` Layer. The focused
|
|
|
23
23
|
`WebCrypto.layerHmac(subtle)` and global `layerWebCrypto` adapters need no KDF
|
|
24
24
|
admission. Waiting for derivation is bounded;
|
|
25
25
|
once admitted, derivation retains its permit until actual work and cleanup finish,
|
|
26
|
-
including interruption. Portable Argon2id yields through Effect between
|
|
26
|
+
including interruption. Portable Argon2id yields through Effect between time slices,
|
|
27
27
|
accepts interruption, and clears its work buffers before releasing admission. It
|
|
28
28
|
still runs on the calling thread. JavaScript cannot guarantee zeroization or
|
|
29
29
|
constant-time execution.
|
|
30
30
|
|
|
31
31
|
Secret inputs, derived bytes and decrypted plaintext use `Redacted`. Backend
|
|
32
32
|
errors contain classifications without native causes or secret payloads. Keep
|
|
33
|
-
each AEAD nonce unique for its key.
|
|
34
|
-
|
|
35
|
-
further use. Signature operations use PKCS8/SPKI DER keys;
|
|
33
|
+
each AEAD nonce unique for its key. HMAC, AES-GCM and signature services offer
|
|
34
|
+
scoped imports for repeated key use. Handles snapshot nonextractable native keys;
|
|
35
|
+
closure joins native work and prevents further use. Signature operations use PKCS8/SPKI DER keys;
|
|
36
36
|
raw key components can be encoded through the native key parser.
|
|
37
37
|
[JOSE](../jose/README.md) and password-hash serialization belong to other layers.
|
|
38
38
|
|
package/dist/Aead.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AuthenticationFailed, OperationError } from "./Errors.mjs";
|
|
2
|
-
import { Context, Effect, Redacted, Schema } from "effect";
|
|
2
|
+
import { Context, Effect, Redacted, Schema, Scope } from "effect";
|
|
3
3
|
//#region src/Aead.d.ts
|
|
4
4
|
export declare const Algorithm: Schema.Literals<readonly ["AES-256-GCM", "XChaCha20-Poly1305"]>;
|
|
5
5
|
export type Algorithm = typeof Algorithm.Type;
|
|
@@ -19,7 +19,32 @@ export declare const DecryptInput: Schema.Struct<{
|
|
|
19
19
|
readonly ciphertext: Schema.Uint8Array;
|
|
20
20
|
}>;
|
|
21
21
|
export type DecryptInput = typeof DecryptInput.Type;
|
|
22
|
+
/** Native AES keys can be imported once for repeated encryption and decryption. */
|
|
23
|
+
export declare const KeyInput: Schema.Struct<{
|
|
24
|
+
readonly algorithm: Schema.Literal<"AES-256-GCM">;
|
|
25
|
+
readonly key: Schema.Redacted<Schema.Uint8Array>;
|
|
26
|
+
}>;
|
|
27
|
+
export type KeyInput = typeof KeyInput.Type;
|
|
28
|
+
export declare const KeyEncryptInput: Schema.Struct<{
|
|
29
|
+
readonly nonce: Schema.Uint8Array;
|
|
30
|
+
readonly additionalData: Schema.optionalKey<Schema.Uint8Array>;
|
|
31
|
+
readonly plaintext: Schema.Redacted<Schema.Uint8Array>;
|
|
32
|
+
}>;
|
|
33
|
+
export type KeyEncryptInput = typeof KeyEncryptInput.Type;
|
|
34
|
+
export declare const KeyDecryptInput: Schema.Struct<{
|
|
35
|
+
readonly nonce: Schema.Uint8Array;
|
|
36
|
+
readonly additionalData: Schema.optionalKey<Schema.Uint8Array>;
|
|
37
|
+
readonly ciphertext: Schema.Uint8Array;
|
|
38
|
+
}>;
|
|
39
|
+
export type KeyDecryptInput = typeof KeyDecryptInput.Type;
|
|
40
|
+
/** A nonextractable AES-256-GCM key owned by the import's Scope. */
|
|
41
|
+
export interface Key {
|
|
42
|
+
readonly encrypt: (input: KeyEncryptInput) => Effect.Effect<Uint8Array, OperationError>;
|
|
43
|
+
readonly decrypt: (input: KeyDecryptInput) => Effect.Effect<Redacted.Redacted<Uint8Array>, OperationError | AuthenticationFailed>;
|
|
44
|
+
}
|
|
22
45
|
declare const Aead_base: Context.ServiceClass<Aead, "@yielded/crypto/Aead", {
|
|
46
|
+
/** Snapshot once; scope closure joins native work and rejects later use. */
|
|
47
|
+
readonly importKey: (input: KeyInput) => Effect.Effect<Key, OperationError, Scope.Scope>;
|
|
23
48
|
readonly encrypt: (input: EncryptInput) => Effect.Effect<Uint8Array, OperationError>;
|
|
24
49
|
readonly decrypt: (input: DecryptInput) => Effect.Effect<Redacted.Redacted<Uint8Array>, OperationError | AuthenticationFailed>;
|
|
25
50
|
}>;
|
package/dist/Aead.mjs
CHANGED
|
@@ -15,6 +15,21 @@ const DecryptInput = Schema.Struct({
|
|
|
15
15
|
...context,
|
|
16
16
|
ciphertext: Schema.Uint8Array
|
|
17
17
|
});
|
|
18
|
+
/** Native AES keys can be imported once for repeated encryption and decryption. */
|
|
19
|
+
const KeyInput = Schema.Struct({
|
|
20
|
+
algorithm: Schema.Literal("AES-256-GCM"),
|
|
21
|
+
key: context.key
|
|
22
|
+
});
|
|
23
|
+
const KeyEncryptInput = Schema.Struct({
|
|
24
|
+
nonce: context.nonce,
|
|
25
|
+
additionalData: context.additionalData,
|
|
26
|
+
plaintext: EncryptInput.fields.plaintext
|
|
27
|
+
});
|
|
28
|
+
const KeyDecryptInput = Schema.Struct({
|
|
29
|
+
nonce: context.nonce,
|
|
30
|
+
additionalData: context.additionalData,
|
|
31
|
+
ciphertext: DecryptInput.fields.ciphertext
|
|
32
|
+
});
|
|
18
33
|
/**
|
|
19
34
|
* Both algorithms use a 32-byte key and append a 16-byte tag to ciphertext.
|
|
20
35
|
* AES-GCM uses a 12-byte nonce; XChaCha uses 24. The caller MUST supply a unique
|
|
@@ -23,6 +38,6 @@ const DecryptInput = Schema.Struct({
|
|
|
23
38
|
*/
|
|
24
39
|
var Aead = class extends Context.Service()("@yielded/crypto/Aead") {};
|
|
25
40
|
//#endregion
|
|
26
|
-
export { Aead, Algorithm, DecryptInput, EncryptInput };
|
|
41
|
+
export { Aead, Algorithm, DecryptInput, EncryptInput, KeyDecryptInput, KeyEncryptInput, KeyInput };
|
|
27
42
|
|
|
28
43
|
//# sourceMappingURL=Aead.mjs.map
|
package/dist/Aead.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Aead.mjs","names":[],"sources":["../src/Aead.ts"],"sourcesContent":["import { Context, type Effect, type Redacted, Schema } from \"effect\";\n\nimport type { AuthenticationFailed, OperationError } from \"./Errors\";\n\nexport const Algorithm = Schema.Literals([\"AES-256-GCM\", \"XChaCha20-Poly1305\"]);\nexport type Algorithm = typeof Algorithm.Type;\n\nconst context = {\n algorithm: Algorithm,\n key: Schema.Redacted(Schema.Uint8Array, { disallowJsonEncode: true }),\n nonce: Schema.Uint8Array,\n additionalData: Schema.optionalKey(Schema.Uint8Array),\n};\n\nexport const EncryptInput = Schema.Struct({\n ...context,\n plaintext: Schema.Redacted(Schema.Uint8Array, { disallowJsonEncode: true }),\n});\n\nexport type EncryptInput = typeof EncryptInput.Type;\n\nexport const DecryptInput = Schema.Struct({ ...context, ciphertext: Schema.Uint8Array });\nexport type DecryptInput = typeof DecryptInput.Type;\n\n/**\n * Both algorithms use a 32-byte key and append a 16-byte tag to ciphertext.\n * AES-GCM uses a 12-byte nonce; XChaCha uses 24. The caller MUST supply a unique\n * nonce for each encryption with a key. Obtain entropy from Effect Crypto.\n * Envelope serialization and key selection belong to the caller.\n */\nexport class Aead extends Context.Service<\n Aead,\n {\n readonly encrypt: (input: EncryptInput) => Effect.Effect<Uint8Array, OperationError>;\n readonly decrypt: (\n input: DecryptInput,\n ) => Effect.Effect<Redacted.Redacted<Uint8Array>, OperationError | AuthenticationFailed>;\n }\n>()(\"@yielded/crypto/Aead\") {}\n"],"mappings":";;AAIA,MAAa,YAAY,OAAO,SAAS,CAAC,eAAe,oBAAoB,CAAC;AAG9E,MAAM,UAAU;CACd,WAAW;CACX,KAAK,OAAO,SAAS,OAAO,YAAY,EAAE,oBAAoB,KAAK,CAAC;CACpE,OAAO,OAAO;CACd,gBAAgB,OAAO,YAAY,OAAO,UAAU;AACtD;AAEA,MAAa,eAAe,OAAO,OAAO;CACxC,GAAG;CACH,WAAW,OAAO,SAAS,OAAO,YAAY,EAAE,oBAAoB,KAAK,CAAC;AAC5E,CAAC;AAID,MAAa,eAAe,OAAO,OAAO;CAAE,GAAG;CAAS,YAAY,OAAO;AAAW,CAAC;;;;;;;
|
|
1
|
+
{"version":3,"file":"Aead.mjs","names":[],"sources":["../src/Aead.ts"],"sourcesContent":["import { Context, type Effect, type Redacted, Schema, type Scope } from \"effect\";\n\nimport type { AuthenticationFailed, OperationError } from \"./Errors\";\n\nexport const Algorithm = Schema.Literals([\"AES-256-GCM\", \"XChaCha20-Poly1305\"]);\nexport type Algorithm = typeof Algorithm.Type;\n\nconst context = {\n algorithm: Algorithm,\n key: Schema.Redacted(Schema.Uint8Array, { disallowJsonEncode: true }),\n nonce: Schema.Uint8Array,\n additionalData: Schema.optionalKey(Schema.Uint8Array),\n};\n\nexport const EncryptInput = Schema.Struct({\n ...context,\n plaintext: Schema.Redacted(Schema.Uint8Array, { disallowJsonEncode: true }),\n});\n\nexport type EncryptInput = typeof EncryptInput.Type;\n\nexport const DecryptInput = Schema.Struct({ ...context, ciphertext: Schema.Uint8Array });\nexport type DecryptInput = typeof DecryptInput.Type;\n\n/** Native AES keys can be imported once for repeated encryption and decryption. */\nexport const KeyInput = Schema.Struct({\n algorithm: Schema.Literal(\"AES-256-GCM\"),\n key: context.key,\n});\n\nexport type KeyInput = typeof KeyInput.Type;\n\nexport const KeyEncryptInput = Schema.Struct({\n nonce: context.nonce,\n additionalData: context.additionalData,\n plaintext: EncryptInput.fields.plaintext,\n});\n\nexport type KeyEncryptInput = typeof KeyEncryptInput.Type;\n\nexport const KeyDecryptInput = Schema.Struct({\n nonce: context.nonce,\n additionalData: context.additionalData,\n ciphertext: DecryptInput.fields.ciphertext,\n});\n\nexport type KeyDecryptInput = typeof KeyDecryptInput.Type;\n\n/** A nonextractable AES-256-GCM key owned by the import's Scope. */\nexport interface Key {\n readonly encrypt: (input: KeyEncryptInput) => Effect.Effect<Uint8Array, OperationError>;\n readonly decrypt: (\n input: KeyDecryptInput,\n ) => Effect.Effect<Redacted.Redacted<Uint8Array>, OperationError | AuthenticationFailed>;\n}\n\n/**\n * Both algorithms use a 32-byte key and append a 16-byte tag to ciphertext.\n * AES-GCM uses a 12-byte nonce; XChaCha uses 24. The caller MUST supply a unique\n * nonce for each encryption with a key. Obtain entropy from Effect Crypto.\n * Envelope serialization and key selection belong to the caller.\n */\nexport class Aead extends Context.Service<\n Aead,\n {\n /** Snapshot once; scope closure joins native work and rejects later use. */\n readonly importKey: (input: KeyInput) => Effect.Effect<Key, OperationError, Scope.Scope>;\n readonly encrypt: (input: EncryptInput) => Effect.Effect<Uint8Array, OperationError>;\n readonly decrypt: (\n input: DecryptInput,\n ) => Effect.Effect<Redacted.Redacted<Uint8Array>, OperationError | AuthenticationFailed>;\n }\n>()(\"@yielded/crypto/Aead\") {}\n"],"mappings":";;AAIA,MAAa,YAAY,OAAO,SAAS,CAAC,eAAe,oBAAoB,CAAC;AAG9E,MAAM,UAAU;CACd,WAAW;CACX,KAAK,OAAO,SAAS,OAAO,YAAY,EAAE,oBAAoB,KAAK,CAAC;CACpE,OAAO,OAAO;CACd,gBAAgB,OAAO,YAAY,OAAO,UAAU;AACtD;AAEA,MAAa,eAAe,OAAO,OAAO;CACxC,GAAG;CACH,WAAW,OAAO,SAAS,OAAO,YAAY,EAAE,oBAAoB,KAAK,CAAC;AAC5E,CAAC;AAID,MAAa,eAAe,OAAO,OAAO;CAAE,GAAG;CAAS,YAAY,OAAO;AAAW,CAAC;;AAIvF,MAAa,WAAW,OAAO,OAAO;CACpC,WAAW,OAAO,QAAQ,aAAa;CACvC,KAAK,QAAQ;AACf,CAAC;AAID,MAAa,kBAAkB,OAAO,OAAO;CAC3C,OAAO,QAAQ;CACf,gBAAgB,QAAQ;CACxB,WAAW,aAAa,OAAO;AACjC,CAAC;AAID,MAAa,kBAAkB,OAAO,OAAO;CAC3C,OAAO,QAAQ;CACf,gBAAgB,QAAQ;CACxB,YAAY,aAAa,OAAO;AAClC,CAAC;;;;;;;AAkBD,IAAa,OAAb,cAA0B,QAAQ,QAUhC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC"}
|
package/dist/Signature.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { OperationError } from "./Errors.mjs";
|
|
2
|
-
import { Context, Effect, Redacted, Schema } from "effect";
|
|
2
|
+
import { Context, Effect, Redacted, Schema, Scope } from "effect";
|
|
3
3
|
//#region src/Signature.d.ts
|
|
4
4
|
export declare const Algorithm: Schema.Literals<readonly ["ECDSA-P256-SHA256", "RSASSA-PKCS1-v1_5-SHA256", "RSA-PSS-SHA256", "Ed25519"]>;
|
|
5
5
|
export type Algorithm = typeof Algorithm.Type;
|
|
@@ -41,6 +41,16 @@ export declare const PrivateKeyParameters: Schema.Union<readonly [Schema.Struct<
|
|
|
41
41
|
readonly qi: Schema.Uint8Array;
|
|
42
42
|
}>]>;
|
|
43
43
|
export type PrivateKeyParameters = typeof PrivateKeyParameters.Type;
|
|
44
|
+
export declare const PrivateKeyInput: Schema.Struct<{
|
|
45
|
+
readonly algorithm: Schema.Literals<readonly ["ECDSA-P256-SHA256", "RSASSA-PKCS1-v1_5-SHA256", "RSA-PSS-SHA256", "Ed25519"]>;
|
|
46
|
+
readonly privateKey: Schema.Redacted<Schema.Uint8Array>;
|
|
47
|
+
}>;
|
|
48
|
+
export type PrivateKeyInput = typeof PrivateKeyInput.Type;
|
|
49
|
+
export declare const PublicKeyInput: Schema.Struct<{
|
|
50
|
+
readonly algorithm: Schema.Literals<readonly ["ECDSA-P256-SHA256", "RSASSA-PKCS1-v1_5-SHA256", "RSA-PSS-SHA256", "Ed25519"]>;
|
|
51
|
+
readonly publicKey: Schema.Uint8Array;
|
|
52
|
+
}>;
|
|
53
|
+
export type PublicKeyInput = typeof PublicKeyInput.Type;
|
|
44
54
|
export declare const SignInput: Schema.Struct<{
|
|
45
55
|
readonly algorithm: Schema.Literals<readonly ["ECDSA-P256-SHA256", "RSASSA-PKCS1-v1_5-SHA256", "RSA-PSS-SHA256", "Ed25519"]>;
|
|
46
56
|
readonly privateKey: Schema.Redacted<Schema.Uint8Array>;
|
|
@@ -54,7 +64,19 @@ export declare const VerifyInput: Schema.Struct<{
|
|
|
54
64
|
readonly signature: Schema.Uint8Array;
|
|
55
65
|
}>;
|
|
56
66
|
export type VerifyInput = typeof VerifyInput.Type;
|
|
67
|
+
/** A nonextractable signing key with its algorithm fixed at import. */
|
|
68
|
+
export interface PrivateKey {
|
|
69
|
+
readonly sign: (data: Uint8Array) => Effect.Effect<Uint8Array, OperationError>;
|
|
70
|
+
}
|
|
71
|
+
/** A verification key with its algorithm fixed at import. */
|
|
72
|
+
export interface PublicKey {
|
|
73
|
+
readonly verify: (data: Uint8Array, signature: Uint8Array) => Effect.Effect<boolean, OperationError>;
|
|
74
|
+
}
|
|
57
75
|
declare const Signature_base: Context.ServiceClass<Signature, "@yielded/crypto/Signature", {
|
|
76
|
+
/** Snapshot PKCS8 once; the imported key is usable only inside its owning Scope. */
|
|
77
|
+
readonly importPrivateKey: (input: PrivateKeyInput) => Effect.Effect<PrivateKey, OperationError, Scope.Scope>;
|
|
78
|
+
/** Snapshot SPKI once; scope closure joins native work and rejects later use. */
|
|
79
|
+
readonly importPublicKey: (input: PublicKeyInput) => Effect.Effect<PublicKey, OperationError, Scope.Scope>;
|
|
58
80
|
readonly encodePublicKey: (input: PublicKeyParameters) => Effect.Effect<Uint8Array, OperationError>;
|
|
59
81
|
readonly encodePrivateKey: (input: Redacted.Redacted<PrivateKeyParameters>) => Effect.Effect<Redacted.Redacted<Uint8Array>, OperationError>;
|
|
60
82
|
readonly sign: (input: SignInput) => Effect.Effect<Uint8Array, OperationError>;
|
package/dist/Signature.mjs
CHANGED
|
@@ -50,14 +50,20 @@ const PrivateKeyParameters = Schema.Union([
|
|
|
50
50
|
qi: integer
|
|
51
51
|
})
|
|
52
52
|
]);
|
|
53
|
-
const
|
|
53
|
+
const PrivateKeyInput = Schema.Struct({
|
|
54
|
+
algorithm: Algorithm,
|
|
55
|
+
privateKey: Schema.Redacted(Schema.Uint8Array, { disallowJsonEncode: true })
|
|
56
|
+
});
|
|
57
|
+
const PublicKeyInput = Schema.Struct({
|
|
54
58
|
algorithm: Algorithm,
|
|
55
|
-
|
|
59
|
+
publicKey: Schema.Uint8Array
|
|
60
|
+
});
|
|
61
|
+
const SignInput = Schema.Struct({
|
|
62
|
+
...PrivateKeyInput.fields,
|
|
56
63
|
data: Schema.Uint8Array
|
|
57
64
|
});
|
|
58
65
|
const VerifyInput = Schema.Struct({
|
|
59
|
-
|
|
60
|
-
publicKey: Schema.Uint8Array,
|
|
66
|
+
...PublicKeyInput.fields,
|
|
61
67
|
data: Schema.Uint8Array,
|
|
62
68
|
signature: Schema.Uint8Array
|
|
63
69
|
});
|
|
@@ -68,6 +74,6 @@ const VerifyInput = Schema.Struct({
|
|
|
68
74
|
*/
|
|
69
75
|
var Signature = class extends Context.Service()("@yielded/crypto/Signature") {};
|
|
70
76
|
//#endregion
|
|
71
|
-
export { Algorithm, PrivateKeyParameters, PublicKeyParameters, SignInput, Signature, VerifyInput };
|
|
77
|
+
export { Algorithm, PrivateKeyInput, PrivateKeyParameters, PublicKeyInput, PublicKeyParameters, SignInput, Signature, VerifyInput };
|
|
72
78
|
|
|
73
79
|
//# sourceMappingURL=Signature.mjs.map
|
package/dist/Signature.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Signature.mjs","names":[],"sources":["../src/Signature.ts"],"sourcesContent":["import { Context, type Effect, type Redacted, Schema } from \"effect\";\n\nimport type { OperationError } from \"./Errors\";\n\nexport const Algorithm = Schema.Literals([\n \"ECDSA-P256-SHA256\",\n \"RSASSA-PKCS1-v1_5-SHA256\",\n \"RSA-PSS-SHA256\",\n \"Ed25519\",\n]);\n\nexport type Algorithm = typeof Algorithm.Type;\n\nconst coordinate = Schema.Uint8Array.check(Schema.isMinLength(32), Schema.isMaxLength(32));\nconst integer = Schema.Uint8Array.check(Schema.isMinLength(1), Schema.isMaxLength(1024));\nconst ec = { algorithm: Schema.Literal(\"ECDSA-P256-SHA256\"), x: coordinate, y: coordinate };\nconst ed = { algorithm: Schema.Literal(\"Ed25519\"), x: coordinate };\n\nconst rsa = {\n algorithm: Schema.Literals([\"RSASSA-PKCS1-v1_5-SHA256\", \"RSA-PSS-SHA256\"]),\n n: integer,\n e: integer,\n};\n\n/**\n * Unsigned big-endian components: 32-byte curve values and RSA components up to\n * 1024 bytes. Native import validates the actual key, including RSA's minimum.\n */\nexport const PublicKeyParameters = Schema.Union([\n Schema.Struct(ec),\n Schema.Struct(ed),\n Schema.Struct(rsa),\n]);\n\nexport type PublicKeyParameters = typeof PublicKeyParameters.Type;\n\nexport const PrivateKeyParameters = Schema.Union([\n Schema.Struct({ ...ec, d: coordinate }),\n Schema.Struct({ ...ed, d: coordinate }),\n Schema.Struct({\n ...rsa,\n d: integer,\n p: integer,\n q: integer,\n dp: integer,\n dq: integer,\n qi: integer,\n }),\n]);\n\nexport type PrivateKeyParameters = typeof PrivateKeyParameters.Type;\n\nexport const
|
|
1
|
+
{"version":3,"file":"Signature.mjs","names":[],"sources":["../src/Signature.ts"],"sourcesContent":["import { Context, type Effect, type Redacted, Schema, type Scope } from \"effect\";\n\nimport type { OperationError } from \"./Errors\";\n\nexport const Algorithm = Schema.Literals([\n \"ECDSA-P256-SHA256\",\n \"RSASSA-PKCS1-v1_5-SHA256\",\n \"RSA-PSS-SHA256\",\n \"Ed25519\",\n]);\n\nexport type Algorithm = typeof Algorithm.Type;\n\nconst coordinate = Schema.Uint8Array.check(Schema.isMinLength(32), Schema.isMaxLength(32));\nconst integer = Schema.Uint8Array.check(Schema.isMinLength(1), Schema.isMaxLength(1024));\nconst ec = { algorithm: Schema.Literal(\"ECDSA-P256-SHA256\"), x: coordinate, y: coordinate };\nconst ed = { algorithm: Schema.Literal(\"Ed25519\"), x: coordinate };\n\nconst rsa = {\n algorithm: Schema.Literals([\"RSASSA-PKCS1-v1_5-SHA256\", \"RSA-PSS-SHA256\"]),\n n: integer,\n e: integer,\n};\n\n/**\n * Unsigned big-endian components: 32-byte curve values and RSA components up to\n * 1024 bytes. Native import validates the actual key, including RSA's minimum.\n */\nexport const PublicKeyParameters = Schema.Union([\n Schema.Struct(ec),\n Schema.Struct(ed),\n Schema.Struct(rsa),\n]);\n\nexport type PublicKeyParameters = typeof PublicKeyParameters.Type;\n\nexport const PrivateKeyParameters = Schema.Union([\n Schema.Struct({ ...ec, d: coordinate }),\n Schema.Struct({ ...ed, d: coordinate }),\n Schema.Struct({\n ...rsa,\n d: integer,\n p: integer,\n q: integer,\n dp: integer,\n dq: integer,\n qi: integer,\n }),\n]);\n\nexport type PrivateKeyParameters = typeof PrivateKeyParameters.Type;\n\nexport const PrivateKeyInput = Schema.Struct({\n algorithm: Algorithm,\n privateKey: Schema.Redacted(Schema.Uint8Array, { disallowJsonEncode: true }),\n});\n\nexport type PrivateKeyInput = typeof PrivateKeyInput.Type;\n\nexport const PublicKeyInput = Schema.Struct({\n algorithm: Algorithm,\n publicKey: Schema.Uint8Array,\n});\n\nexport type PublicKeyInput = typeof PublicKeyInput.Type;\n\nexport const SignInput = Schema.Struct({\n ...PrivateKeyInput.fields,\n data: Schema.Uint8Array,\n});\n\nexport type SignInput = typeof SignInput.Type;\n\nexport const VerifyInput = Schema.Struct({\n ...PublicKeyInput.fields,\n data: Schema.Uint8Array,\n signature: Schema.Uint8Array,\n});\n\nexport type VerifyInput = typeof VerifyInput.Type;\n\n/** A nonextractable signing key with its algorithm fixed at import. */\nexport interface PrivateKey {\n readonly sign: (data: Uint8Array) => Effect.Effect<Uint8Array, OperationError>;\n}\n\n/** A verification key with its algorithm fixed at import. */\nexport interface PublicKey {\n readonly verify: (\n data: Uint8Array,\n signature: Uint8Array,\n ) => Effect.Effect<boolean, OperationError>;\n}\n\n/**\n * Private keys are PKCS8 DER; public keys are SPKI DER. ECDSA signatures are\n * 64-byte IEEE P1363 (r || s), not ASN.1 DER. RSA requires at least 2048 bits;\n * PSS uses a 32-byte salt. Key serialization/selection and JOSE policy stay outside.\n */\nexport class Signature extends Context.Service<\n Signature,\n {\n /** Snapshot PKCS8 once; the imported key is usable only inside its owning Scope. */\n readonly importPrivateKey: (\n input: PrivateKeyInput,\n ) => Effect.Effect<PrivateKey, OperationError, Scope.Scope>;\n /** Snapshot SPKI once; scope closure joins native work and rejects later use. */\n readonly importPublicKey: (\n input: PublicKeyInput,\n ) => Effect.Effect<PublicKey, OperationError, Scope.Scope>;\n readonly encodePublicKey: (\n input: PublicKeyParameters,\n ) => Effect.Effect<Uint8Array, OperationError>;\n readonly encodePrivateKey: (\n input: Redacted.Redacted<PrivateKeyParameters>,\n ) => Effect.Effect<Redacted.Redacted<Uint8Array>, OperationError>;\n readonly sign: (input: SignInput) => Effect.Effect<Uint8Array, OperationError>;\n readonly verify: (input: VerifyInput) => Effect.Effect<boolean, OperationError>;\n }\n>()(\"@yielded/crypto/Signature\") {}\n"],"mappings":";;AAIA,MAAa,YAAY,OAAO,SAAS;CACvC;CACA;CACA;CACA;AACF,CAAC;AAID,MAAM,aAAa,OAAO,WAAW,MAAM,OAAO,YAAY,EAAE,GAAG,OAAO,YAAY,EAAE,CAAC;AACzF,MAAM,UAAU,OAAO,WAAW,MAAM,OAAO,YAAY,CAAC,GAAG,OAAO,YAAY,IAAI,CAAC;AACvF,MAAM,KAAK;CAAE,WAAW,OAAO,QAAQ,mBAAmB;CAAG,GAAG;CAAY,GAAG;AAAW;AAC1F,MAAM,KAAK;CAAE,WAAW,OAAO,QAAQ,SAAS;CAAG,GAAG;AAAW;AAEjE,MAAM,MAAM;CACV,WAAW,OAAO,SAAS,CAAC,4BAA4B,gBAAgB,CAAC;CACzE,GAAG;CACH,GAAG;AACL;;;;;AAMA,MAAa,sBAAsB,OAAO,MAAM;CAC9C,OAAO,OAAO,EAAE;CAChB,OAAO,OAAO,EAAE;CAChB,OAAO,OAAO,GAAG;AACnB,CAAC;AAID,MAAa,uBAAuB,OAAO,MAAM;CAC/C,OAAO,OAAO;EAAE,GAAG;EAAI,GAAG;CAAW,CAAC;CACtC,OAAO,OAAO;EAAE,GAAG;EAAI,GAAG;CAAW,CAAC;CACtC,OAAO,OAAO;EACZ,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,IAAI;EACJ,IAAI;EACJ,IAAI;CACN,CAAC;AACH,CAAC;AAID,MAAa,kBAAkB,OAAO,OAAO;CAC3C,WAAW;CACX,YAAY,OAAO,SAAS,OAAO,YAAY,EAAE,oBAAoB,KAAK,CAAC;AAC7E,CAAC;AAID,MAAa,iBAAiB,OAAO,OAAO;CAC1C,WAAW;CACX,WAAW,OAAO;AACpB,CAAC;AAID,MAAa,YAAY,OAAO,OAAO;CACrC,GAAG,gBAAgB;CACnB,MAAM,OAAO;AACf,CAAC;AAID,MAAa,cAAc,OAAO,OAAO;CACvC,GAAG,eAAe;CAClB,MAAM,OAAO;CACb,WAAW,OAAO;AACpB,CAAC;;;;;;AAsBD,IAAa,YAAb,cAA+B,QAAQ,QAoBrC,CAAC,CAAC,2BAA2B,CAAC,CAAC,CAAC"}
|
package/dist/internal/aead.mjs
CHANGED
|
@@ -1,80 +1,124 @@
|
|
|
1
|
-
import { Aead, DecryptInput, EncryptInput } from "../Aead.mjs";
|
|
1
|
+
import { Aead, DecryptInput, EncryptInput, KeyDecryptInput, KeyEncryptInput, KeyInput } from "../Aead.mjs";
|
|
2
2
|
import { AuthenticationFailed, UnsupportedAlgorithm } from "../Errors.mjs";
|
|
3
3
|
import { copy, decode, importError, nativeError, nativeErrorName, withSecret } from "./common.mjs";
|
|
4
|
+
import { makeScopedKey } from "./scoped-key.mjs";
|
|
4
5
|
import { Effect, Redacted, Schema } from "effect";
|
|
5
6
|
//#region src/internal/aead.ts
|
|
6
7
|
const keyBytes = Schema.Uint8Array.check(Schema.isBetweenLength(32, 32));
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
8
|
+
const aesNonce = Schema.Uint8Array.check(Schema.isBetweenLength(12, 12));
|
|
9
|
+
const makeAead = (subtle, xchacha) => {
|
|
10
|
+
const encryptAes = (key, input) => Effect.tryPromise({
|
|
11
|
+
try: () => subtle.encrypt({
|
|
12
|
+
name: "AES-GCM",
|
|
13
|
+
iv: input.nonce,
|
|
14
|
+
additionalData: input.additionalData,
|
|
15
|
+
tagLength: 128
|
|
16
|
+
}, key, input.data),
|
|
17
|
+
catch: nativeError
|
|
18
|
+
}).pipe(Effect.map((encrypted) => new Uint8Array(encrypted)));
|
|
19
|
+
const decryptAes = (key, input) => Effect.tryPromise({
|
|
20
|
+
try: () => subtle.decrypt({
|
|
21
|
+
name: "AES-GCM",
|
|
22
|
+
iv: input.nonce,
|
|
23
|
+
additionalData: input.additionalData,
|
|
24
|
+
tagLength: 128
|
|
25
|
+
}, key, input.data),
|
|
26
|
+
catch: (cause) => nativeErrorName(cause) === "OperationError" ? AuthenticationFailed.make({}) : nativeError(cause)
|
|
27
|
+
}).pipe(Effect.map((decrypted) => new Uint8Array(decrypted)));
|
|
28
|
+
return Aead.of({
|
|
29
|
+
importKey: Effect.fnUntraced(function* (input) {
|
|
30
|
+
const value = yield* decode(KeyInput, input, "key");
|
|
31
|
+
yield* decode(keyBytes, Redacted.value(value.key), "key");
|
|
32
|
+
const use = yield* makeScopedKey(withSecret(value.key, (material) => Effect.tryPromise({
|
|
33
|
+
try: () => subtle.importKey("raw", material, "AES-GCM", false, ["encrypt", "decrypt"]),
|
|
34
|
+
catch: importError
|
|
35
|
+
})));
|
|
36
|
+
return {
|
|
37
|
+
encrypt: Effect.fnUntraced(function* (input) {
|
|
38
|
+
const value = yield* decode(KeyEncryptInput, input, "data");
|
|
39
|
+
const nonce = yield* decode(aesNonce, value.nonce, "nonce").pipe(Effect.flatMap(copy));
|
|
40
|
+
const additionalData = yield* copy(value.additionalData ?? /* @__PURE__ */ new Uint8Array());
|
|
41
|
+
return yield* withSecret(value.plaintext, (data) => use((key) => encryptAes(key, {
|
|
42
|
+
nonce,
|
|
32
43
|
additionalData,
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
return
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
44
|
+
data
|
|
45
|
+
})));
|
|
46
|
+
}),
|
|
47
|
+
decrypt: Effect.fnUntraced(function* (input) {
|
|
48
|
+
const value = yield* decode(KeyDecryptInput, input, "data");
|
|
49
|
+
const nonce = yield* decode(aesNonce, value.nonce, "nonce").pipe(Effect.flatMap(copy));
|
|
50
|
+
const additionalData = yield* copy(value.additionalData ?? /* @__PURE__ */ new Uint8Array());
|
|
51
|
+
const data = yield* copy(value.ciphertext);
|
|
52
|
+
const plaintext = yield* use((key) => data.length < 16 ? Effect.fail(AuthenticationFailed.make({})) : decryptAes(key, {
|
|
53
|
+
nonce,
|
|
54
|
+
additionalData,
|
|
55
|
+
data
|
|
56
|
+
}));
|
|
57
|
+
return Redacted.make(plaintext);
|
|
58
|
+
})
|
|
59
|
+
};
|
|
60
|
+
}),
|
|
61
|
+
encrypt: Effect.fnUntraced(function* (input) {
|
|
62
|
+
const value = yield* decode(EncryptInput, input, "data");
|
|
63
|
+
const nonceLength = value.algorithm === "AES-256-GCM" ? 12 : 24;
|
|
64
|
+
yield* decode(keyBytes, Redacted.value(value.key), "key");
|
|
65
|
+
yield* decode(Schema.Uint8Array.check(Schema.isBetweenLength(nonceLength, nonceLength)), value.nonce, "nonce");
|
|
66
|
+
if (value.algorithm === "XChaCha20-Poly1305" && xchacha === void 0) return yield* UnsupportedAlgorithm.make({});
|
|
67
|
+
const nonce = yield* copy(value.nonce);
|
|
68
|
+
const additionalData = yield* copy(value.additionalData ?? /* @__PURE__ */ new Uint8Array());
|
|
69
|
+
return yield* withSecret(value.key, (key) => withSecret(value.plaintext, (data) => {
|
|
70
|
+
if (value.algorithm === "XChaCha20-Poly1305") return xchacha === void 0 ? Effect.fail(UnsupportedAlgorithm.make({})) : xchacha.encrypt({
|
|
71
|
+
key,
|
|
72
|
+
nonce,
|
|
73
|
+
additionalData,
|
|
74
|
+
data
|
|
62
75
|
});
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
76
|
+
return Effect.gen(function* () {
|
|
77
|
+
const imported = yield* Effect.tryPromise({
|
|
78
|
+
try: () => subtle.importKey("raw", key, "AES-GCM", false, ["encrypt"]),
|
|
79
|
+
catch: importError
|
|
80
|
+
});
|
|
81
|
+
return yield* encryptAes(imported, {
|
|
82
|
+
nonce,
|
|
67
83
|
additionalData,
|
|
68
|
-
|
|
69
|
-
}
|
|
70
|
-
|
|
84
|
+
data
|
|
85
|
+
});
|
|
86
|
+
}).pipe(Effect.uninterruptible);
|
|
87
|
+
}));
|
|
88
|
+
}),
|
|
89
|
+
decrypt: Effect.fnUntraced(function* (input) {
|
|
90
|
+
const value = yield* decode(DecryptInput, input, "data");
|
|
91
|
+
const nonceLength = value.algorithm === "AES-256-GCM" ? 12 : 24;
|
|
92
|
+
yield* decode(keyBytes, Redacted.value(value.key), "key");
|
|
93
|
+
yield* decode(Schema.Uint8Array.check(Schema.isBetweenLength(nonceLength, nonceLength)), value.nonce, "nonce");
|
|
94
|
+
if (value.algorithm === "XChaCha20-Poly1305" && xchacha === void 0) return yield* UnsupportedAlgorithm.make({});
|
|
95
|
+
if (value.ciphertext.length < 16) return yield* AuthenticationFailed.make({});
|
|
96
|
+
const nonce = yield* copy(value.nonce);
|
|
97
|
+
const additionalData = yield* copy(value.additionalData ?? /* @__PURE__ */ new Uint8Array());
|
|
98
|
+
const data = yield* copy(value.ciphertext);
|
|
99
|
+
const plaintext = yield* withSecret(value.key, (key) => {
|
|
100
|
+
if (value.algorithm === "XChaCha20-Poly1305") return xchacha === void 0 ? Effect.fail(UnsupportedAlgorithm.make({})) : xchacha.decrypt({
|
|
101
|
+
key,
|
|
102
|
+
nonce,
|
|
103
|
+
additionalData,
|
|
104
|
+
data
|
|
71
105
|
});
|
|
72
|
-
return
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
106
|
+
return Effect.gen(function* () {
|
|
107
|
+
const imported = yield* Effect.tryPromise({
|
|
108
|
+
try: () => subtle.importKey("raw", key, "AES-GCM", false, ["decrypt"]),
|
|
109
|
+
catch: importError
|
|
110
|
+
});
|
|
111
|
+
return yield* decryptAes(imported, {
|
|
112
|
+
nonce,
|
|
113
|
+
additionalData,
|
|
114
|
+
data
|
|
115
|
+
});
|
|
116
|
+
}).pipe(Effect.uninterruptible);
|
|
117
|
+
});
|
|
118
|
+
return Redacted.make(plaintext);
|
|
119
|
+
})
|
|
120
|
+
});
|
|
121
|
+
};
|
|
78
122
|
//#endregion
|
|
79
123
|
export { makeAead };
|
|
80
124
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aead.mjs","names":[],"sources":["../../src/internal/aead.ts"],"sourcesContent":["import { Effect, Redacted, Schema } from \"effect\";\n\nimport { Aead, DecryptInput, EncryptInput } from \"../Aead\";\nimport { AuthenticationFailed, type OperationError, UnsupportedAlgorithm } from \"../Errors\";\nimport { copy, decode, importError, nativeError, nativeErrorName, withSecret } from \"./common\";\n\ninterface CipherInput {\n readonly key: Uint8Array<ArrayBuffer>;\n readonly nonce: Uint8Array<ArrayBuffer>;\n readonly additionalData: Uint8Array<ArrayBuffer>;\n readonly data: Uint8Array<ArrayBuffer>;\n}\n\nexport interface XChaCha {\n readonly encrypt: (input: CipherInput) => Effect.Effect<Uint8Array, OperationError>;\n readonly decrypt: (\n input: CipherInput,\n ) => Effect.Effect<Uint8Array, OperationError | AuthenticationFailed>;\n}\n\nconst keyBytes = Schema.Uint8Array.check(Schema.isBetweenLength(32, 32));\n\nexport const makeAead = (subtle: SubtleCrypto, xchacha?: XChaCha): Aead[\"Service\"] =>\n Aead.of({\n encrypt: Effect.fnUntraced(function* (input) {\n const value = yield* decode(EncryptInput, input, \"data\");\n const nonceLength = value.algorithm === \"AES-256-GCM\" ? 12 : 24;\n\n yield* decode(keyBytes, Redacted.value(value.key), \"key\");\n yield* decode(\n Schema.Uint8Array.check(Schema.isBetweenLength(nonceLength, nonceLength)),\n value.nonce,\n \"nonce\",\n );\n if (value.algorithm === \"XChaCha20-Poly1305\" && xchacha === undefined)\n return yield* UnsupportedAlgorithm.make({});\n const nonce = yield* copy(value.nonce);\n const additionalData = yield* copy(value.additionalData ?? new Uint8Array());\n\n return yield* withSecret(value.key, (key) =>\n withSecret(value.plaintext, (data) => {\n if (value.algorithm === \"XChaCha20-Poly1305\") {\n return xchacha === undefined\n ? Effect.fail(UnsupportedAlgorithm.make({}))\n : xchacha.encrypt({ key, nonce, additionalData, data });\n }\n\n return Effect.gen(function* () {\n const imported = yield* Effect.tryPromise({\n try: () => subtle.importKey(\"raw\", key, \"AES-GCM\", false, [\"encrypt\"]),\n catch: importError,\n });\n\n const encrypted = yield* Effect.tryPromise({\n try: () =>\n subtle.encrypt(\n { name: \"AES-GCM\", iv: nonce, additionalData, tagLength: 128 },\n imported,\n data,\n ),\n catch: nativeError,\n });\n\n return new Uint8Array(encrypted);\n }).pipe(Effect.uninterruptible);\n }),\n );\n }),\n decrypt: Effect.fnUntraced(function* (input) {\n const value = yield* decode(DecryptInput, input, \"data\");\n const nonceLength = value.algorithm === \"AES-256-GCM\" ? 12 : 24;\n\n yield* decode(keyBytes, Redacted.value(value.key), \"key\");\n yield* decode(\n Schema.Uint8Array.check(Schema.isBetweenLength(nonceLength, nonceLength)),\n value.nonce,\n \"nonce\",\n );\n if (value.algorithm === \"XChaCha20-Poly1305\" && xchacha === undefined)\n return yield* UnsupportedAlgorithm.make({});\n if (value.ciphertext.length < 16) return yield* AuthenticationFailed.make({});\n const nonce = yield* copy(value.nonce);\n const additionalData = yield* copy(value.additionalData ?? new Uint8Array());\n const data = yield* copy(value.ciphertext);\n\n const plaintext = yield* withSecret(value.key, (key) => {\n if (value.algorithm === \"XChaCha20-Poly1305\") {\n return xchacha === undefined\n ? Effect.fail(UnsupportedAlgorithm.make({}))\n : xchacha.decrypt({ key, nonce, additionalData, data });\n }\n\n return Effect.gen(function* () {\n const imported = yield* Effect.tryPromise({\n try: () => subtle.importKey(\"raw\", key, \"AES-GCM\", false, [\"decrypt\"]),\n catch: importError,\n });\n\n const decrypted = yield* Effect.tryPromise({\n try: () =>\n subtle.decrypt(\n { name: \"AES-GCM\", iv: nonce, additionalData, tagLength: 128 },\n imported,\n data,\n ),\n // WebCrypto specifies OperationError for a rejected GCM tag.\n catch: (cause) =>\n nativeErrorName(cause) === \"OperationError\"\n ? AuthenticationFailed.make({})\n : nativeError(cause),\n });\n\n return new Uint8Array(decrypted);\n }).pipe(Effect.uninterruptible);\n });\n\n return Redacted.make(plaintext);\n }),\n });\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"aead.mjs","names":[],"sources":["../../src/internal/aead.ts"],"sourcesContent":["import { Effect, Redacted, Schema } from \"effect\";\n\nimport {\n Aead,\n DecryptInput,\n EncryptInput,\n type Key,\n KeyDecryptInput,\n KeyEncryptInput,\n KeyInput,\n} from \"../Aead\";\nimport { AuthenticationFailed, type OperationError, UnsupportedAlgorithm } from \"../Errors\";\nimport { copy, decode, importError, nativeError, nativeErrorName, withSecret } from \"./common\";\nimport { makeScopedKey } from \"./scoped-key\";\n\ninterface CipherData {\n readonly nonce: Uint8Array<ArrayBuffer>;\n readonly additionalData: Uint8Array<ArrayBuffer>;\n readonly data: Uint8Array<ArrayBuffer>;\n}\n\ninterface CipherInput extends CipherData {\n readonly key: Uint8Array<ArrayBuffer>;\n}\n\nexport interface XChaCha {\n readonly encrypt: (input: CipherInput) => Effect.Effect<Uint8Array, OperationError>;\n readonly decrypt: (\n input: CipherInput,\n ) => Effect.Effect<Uint8Array, OperationError | AuthenticationFailed>;\n}\n\nconst keyBytes = Schema.Uint8Array.check(Schema.isBetweenLength(32, 32));\nconst aesNonce = Schema.Uint8Array.check(Schema.isBetweenLength(12, 12));\n\nexport const makeAead = (subtle: SubtleCrypto, xchacha?: XChaCha): Aead[\"Service\"] => {\n const encryptAes = (key: CryptoKey, input: CipherData) =>\n Effect.tryPromise({\n try: () =>\n subtle.encrypt(\n {\n name: \"AES-GCM\",\n iv: input.nonce,\n additionalData: input.additionalData,\n tagLength: 128,\n },\n key,\n input.data,\n ),\n catch: nativeError,\n }).pipe(Effect.map((encrypted) => new Uint8Array(encrypted)));\n\n const decryptAes = (key: CryptoKey, input: CipherData) =>\n Effect.tryPromise({\n try: () =>\n subtle.decrypt(\n {\n name: \"AES-GCM\",\n iv: input.nonce,\n additionalData: input.additionalData,\n tagLength: 128,\n },\n key,\n input.data,\n ),\n // WebCrypto specifies OperationError for a rejected GCM tag.\n catch: (cause) =>\n nativeErrorName(cause) === \"OperationError\"\n ? AuthenticationFailed.make({})\n : nativeError(cause),\n }).pipe(Effect.map((decrypted) => new Uint8Array(decrypted)));\n\n return Aead.of({\n importKey: Effect.fnUntraced(function* (input) {\n const value = yield* decode(KeyInput, input, \"key\");\n\n yield* decode(keyBytes, Redacted.value(value.key), \"key\");\n\n const use = yield* makeScopedKey(\n withSecret(value.key, (material) =>\n Effect.tryPromise({\n try: () => subtle.importKey(\"raw\", material, \"AES-GCM\", false, [\"encrypt\", \"decrypt\"]),\n catch: importError,\n }),\n ),\n );\n\n return {\n encrypt: Effect.fnUntraced(function* (input) {\n const value = yield* decode(KeyEncryptInput, input, \"data\");\n const nonce = yield* decode(aesNonce, value.nonce, \"nonce\").pipe(Effect.flatMap(copy));\n const additionalData = yield* copy(value.additionalData ?? new Uint8Array());\n\n return yield* withSecret(value.plaintext, (data) =>\n use((key) => encryptAes(key, { nonce, additionalData, data })),\n );\n }),\n decrypt: Effect.fnUntraced(function* (input) {\n const value = yield* decode(KeyDecryptInput, input, \"data\");\n const nonce = yield* decode(aesNonce, value.nonce, \"nonce\").pipe(Effect.flatMap(copy));\n const additionalData = yield* copy(value.additionalData ?? new Uint8Array());\n const data = yield* copy(value.ciphertext);\n\n const plaintext = yield* use((key) =>\n data.length < 16\n ? Effect.fail(AuthenticationFailed.make({}))\n : decryptAes(key, { nonce, additionalData, data }),\n );\n\n return Redacted.make(plaintext);\n }),\n } satisfies Key;\n }),\n encrypt: Effect.fnUntraced(function* (input) {\n const value = yield* decode(EncryptInput, input, \"data\");\n const nonceLength = value.algorithm === \"AES-256-GCM\" ? 12 : 24;\n\n yield* decode(keyBytes, Redacted.value(value.key), \"key\");\n yield* decode(\n Schema.Uint8Array.check(Schema.isBetweenLength(nonceLength, nonceLength)),\n value.nonce,\n \"nonce\",\n );\n if (value.algorithm === \"XChaCha20-Poly1305\" && xchacha === undefined)\n return yield* UnsupportedAlgorithm.make({});\n const nonce = yield* copy(value.nonce);\n const additionalData = yield* copy(value.additionalData ?? new Uint8Array());\n\n return yield* withSecret(value.key, (key) =>\n withSecret(value.plaintext, (data) => {\n if (value.algorithm === \"XChaCha20-Poly1305\") {\n return xchacha === undefined\n ? Effect.fail(UnsupportedAlgorithm.make({}))\n : xchacha.encrypt({ key, nonce, additionalData, data });\n }\n\n return Effect.gen(function* () {\n const imported = yield* Effect.tryPromise({\n try: () => subtle.importKey(\"raw\", key, \"AES-GCM\", false, [\"encrypt\"]),\n catch: importError,\n });\n\n return yield* encryptAes(imported, { nonce, additionalData, data });\n }).pipe(Effect.uninterruptible);\n }),\n );\n }),\n decrypt: Effect.fnUntraced(function* (input) {\n const value = yield* decode(DecryptInput, input, \"data\");\n const nonceLength = value.algorithm === \"AES-256-GCM\" ? 12 : 24;\n\n yield* decode(keyBytes, Redacted.value(value.key), \"key\");\n yield* decode(\n Schema.Uint8Array.check(Schema.isBetweenLength(nonceLength, nonceLength)),\n value.nonce,\n \"nonce\",\n );\n if (value.algorithm === \"XChaCha20-Poly1305\" && xchacha === undefined)\n return yield* UnsupportedAlgorithm.make({});\n if (value.ciphertext.length < 16) return yield* AuthenticationFailed.make({});\n const nonce = yield* copy(value.nonce);\n const additionalData = yield* copy(value.additionalData ?? new Uint8Array());\n const data = yield* copy(value.ciphertext);\n\n const plaintext = yield* withSecret(value.key, (key) => {\n if (value.algorithm === \"XChaCha20-Poly1305\") {\n return xchacha === undefined\n ? Effect.fail(UnsupportedAlgorithm.make({}))\n : xchacha.decrypt({ key, nonce, additionalData, data });\n }\n\n return Effect.gen(function* () {\n const imported = yield* Effect.tryPromise({\n try: () => subtle.importKey(\"raw\", key, \"AES-GCM\", false, [\"decrypt\"]),\n catch: importError,\n });\n\n return yield* decryptAes(imported, { nonce, additionalData, data });\n }).pipe(Effect.uninterruptible);\n });\n\n return Redacted.make(plaintext);\n }),\n });\n};\n"],"mappings":"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"}
|
package/dist/internal/argon2.mjs
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import { allocate, destroy, fill, initialize, output } from "./argon2-core.mjs";
|
|
2
|
-
import { Effect } from "effect";
|
|
2
|
+
import { Clock, Effect } from "effect";
|
|
3
3
|
//#region src/internal/argon2.ts
|
|
4
|
-
/** Effect schedules
|
|
4
|
+
/** Effect schedules time slices; admission remains held through buffer cleanup. */
|
|
5
5
|
const argon2 = (input) => Effect.acquireUseRelease(Effect.sync(() => allocate(input)), (state) => Effect.gen(function* () {
|
|
6
|
+
const clock = yield* Clock.Clock;
|
|
6
7
|
initialize(state, input);
|
|
7
8
|
const blocks = fill(state, input);
|
|
8
9
|
for (;;) {
|
|
9
|
-
|
|
10
|
+
const started = clock.monotonicTimeNanosUnsafe();
|
|
11
|
+
for (let processed = 0; processed < 2048; processed += 256) {
|
|
12
|
+
for (let batch = 0; batch < 256; batch++) if (blocks.next().done) return output(state, input);
|
|
13
|
+
if (clock.monotonicTimeNanosUnsafe() - started >= 8000000n) break;
|
|
14
|
+
}
|
|
10
15
|
yield* Effect.yieldNow;
|
|
11
16
|
}
|
|
12
17
|
}), (state) => Effect.sync(() => destroy(state)));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"argon2.mjs","names":[],"sources":["../../src/internal/argon2.ts"],"sourcesContent":["import { Effect } from \"effect\";\n\nimport { allocate, destroy, fill, initialize, output } from \"./argon2-core\";\nimport type { Argon2 } from \"./kdf\";\n\n/** Effect schedules
|
|
1
|
+
{"version":3,"file":"argon2.mjs","names":[],"sources":["../../src/internal/argon2.ts"],"sourcesContent":["import { Clock, Effect } from \"effect\";\n\nimport { allocate, destroy, fill, initialize, output } from \"./argon2-core\";\nimport type { Argon2 } from \"./kdf\";\n\n/** Effect schedules time slices; admission remains held through buffer cleanup. */\nexport const argon2: Argon2 = (input) =>\n Effect.acquireUseRelease(\n Effect.sync(() => allocate(input)),\n (state) =>\n Effect.gen(function* () {\n const clock = yield* Clock.Clock;\n\n initialize(state, input);\n const blocks = fill(state, input);\n\n for (;;) {\n const started = clock.monotonicTimeNanosUnsafe();\n\n // Workers freeze clocks during CPU work. Cap each slice as well so\n // interruption and other fibers still get a turn on those runtimes.\n for (let processed = 0; processed < 2048; processed += 256) {\n for (let batch = 0; batch < 256; batch++) {\n if (blocks.next().done) return output(state, input);\n }\n if (clock.monotonicTimeNanosUnsafe() - started >= 8_000_000n) break;\n }\n yield* Effect.yieldNow;\n }\n }),\n (state) => Effect.sync(() => destroy(state)),\n );\n"],"mappings":";;;;AAMA,MAAa,UAAkB,UAC7B,OAAO,kBACL,OAAO,WAAW,SAAS,KAAK,CAAC,IAChC,UACC,OAAO,IAAI,aAAa;CACtB,MAAM,QAAQ,OAAO,MAAM;CAE3B,WAAW,OAAO,KAAK;CACvB,MAAM,SAAS,KAAK,OAAO,KAAK;CAEhC,SAAS;EACP,MAAM,UAAU,MAAM,yBAAyB;EAI/C,KAAK,IAAI,YAAY,GAAG,YAAY,MAAM,aAAa,KAAK;GAC1D,KAAK,IAAI,QAAQ,GAAG,QAAQ,KAAK,SAC/B,IAAI,OAAO,KAAK,CAAC,CAAC,MAAM,OAAO,OAAO,OAAO,KAAK;GAEpD,IAAI,MAAM,yBAAyB,IAAI,WAAW,UAAY;EAChE;EACA,OAAO,OAAO;CAChB;AACF,CAAC,IACF,UAAU,OAAO,WAAW,QAAQ,KAAK,CAAC,CAC7C"}
|
package/dist/internal/hmac.mjs
CHANGED
|
@@ -1,49 +1,35 @@
|
|
|
1
|
-
import { CryptoUnavailable } from "../Errors.mjs";
|
|
2
1
|
import { Hmac, Input, KeyInput, VerifyInput } from "../Hmac.mjs";
|
|
3
2
|
import { copy, decode, importError, nativeError, withSecret } from "./common.mjs";
|
|
4
|
-
import {
|
|
3
|
+
import { makeScopedKey } from "./scoped-key.mjs";
|
|
4
|
+
import { Effect, Redacted, Schema } from "effect";
|
|
5
5
|
//#region src/internal/hmac.ts
|
|
6
6
|
const keyBytes = Schema.Uint8Array.check(Schema.isMinLength(1));
|
|
7
7
|
const makeHmac = (subtle) => {
|
|
8
8
|
const importKey = Effect.fnUntraced(function* (input) {
|
|
9
9
|
const value = yield* decode(KeyInput, input, "key");
|
|
10
10
|
yield* decode(keyBytes, Redacted.value(value.key), "key");
|
|
11
|
-
const
|
|
12
|
-
let imported;
|
|
13
|
-
yield* Scope.addFinalizer(scope, Effect.sync(() => {
|
|
14
|
-
imported = void 0;
|
|
15
|
-
}));
|
|
16
|
-
const available = Effect.suspend(() => scope.state._tag === "Closed" ? Effect.fail(CryptoUnavailable.make({})) : Effect.void);
|
|
17
|
-
const run = (operation) => Effect.acquireUseRelease(available.pipe(Effect.andThen(Effect.forkIn(operation, scope, { uninterruptible: false }))), (fiber) => Effect.gen(function* () {
|
|
18
|
-
const exit = yield* Fiber.await(fiber);
|
|
19
|
-
yield* available;
|
|
20
|
-
return yield* exit;
|
|
21
|
-
}), Fiber.interrupt);
|
|
22
|
-
yield* run(withSecret(value.key, (material) => Effect.tryPromise({
|
|
11
|
+
const use = yield* makeScopedKey(withSecret(value.key, (material) => Effect.tryPromise({
|
|
23
12
|
try: () => subtle.importKey("raw", material, {
|
|
24
13
|
name: "HMAC",
|
|
25
14
|
hash: value.algorithm
|
|
26
15
|
}, false, ["sign", "verify"]),
|
|
27
16
|
catch: importError
|
|
28
|
-
})
|
|
29
|
-
imported = key;
|
|
30
|
-
})), Effect.uninterruptible))).pipe(Effect.onExit((exit) => Exit.isFailure(exit) ? Scope.close(scope, exit) : Effect.void));
|
|
31
|
-
const use = (operation) => run(Effect.suspend(() => {
|
|
32
|
-
const key = imported;
|
|
33
|
-
return key === void 0 ? Effect.fail(CryptoUnavailable.make({})) : Effect.tryPromise({
|
|
34
|
-
try: () => operation(key),
|
|
35
|
-
catch: nativeError
|
|
36
|
-
});
|
|
37
|
-
}).pipe(Effect.uninterruptible));
|
|
17
|
+
})));
|
|
38
18
|
return {
|
|
39
19
|
sign: Effect.fnUntraced(function* (input) {
|
|
40
20
|
const data = yield* decode(Schema.Uint8Array, input, "data").pipe(Effect.flatMap(copy));
|
|
41
|
-
return new Uint8Array(yield* use((key) =>
|
|
21
|
+
return new Uint8Array(yield* use((key) => Effect.tryPromise({
|
|
22
|
+
try: () => subtle.sign("HMAC", key, data),
|
|
23
|
+
catch: nativeError
|
|
24
|
+
})));
|
|
42
25
|
}),
|
|
43
26
|
verify: Effect.fnUntraced(function* (input, signature) {
|
|
44
27
|
const data = yield* decode(Schema.Uint8Array, input, "data").pipe(Effect.flatMap(copy));
|
|
45
28
|
const tag = yield* decode(Schema.Uint8Array, signature, "data").pipe(Effect.flatMap(copy));
|
|
46
|
-
return yield* use((key) =>
|
|
29
|
+
return yield* use((key) => Effect.tryPromise({
|
|
30
|
+
try: () => subtle.verify("HMAC", key, tag, data),
|
|
31
|
+
catch: nativeError
|
|
32
|
+
}));
|
|
47
33
|
})
|
|
48
34
|
};
|
|
49
35
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hmac.mjs","names":[],"sources":["../../src/internal/hmac.ts"],"sourcesContent":["import { Effect,
|
|
1
|
+
{"version":3,"file":"hmac.mjs","names":[],"sources":["../../src/internal/hmac.ts"],"sourcesContent":["import { Effect, Redacted, Schema } from \"effect\";\n\nimport { Hmac, Input, type Key, KeyInput, VerifyInput } from \"../Hmac\";\nimport { copy, decode, importError, nativeError, withSecret } from \"./common\";\nimport { makeScopedKey } from \"./scoped-key\";\n\nconst keyBytes = Schema.Uint8Array.check(Schema.isMinLength(1));\n\nexport const makeHmac = (subtle: SubtleCrypto): Hmac[\"Service\"] => {\n const importKey = Effect.fnUntraced(function* (input: KeyInput) {\n const value = yield* decode(KeyInput, input, \"key\");\n\n yield* decode(keyBytes, Redacted.value(value.key), \"key\");\n\n const use = yield* makeScopedKey(\n withSecret(value.key, (material) =>\n Effect.tryPromise({\n try: () =>\n subtle.importKey(\"raw\", material, { name: \"HMAC\", hash: value.algorithm }, false, [\n \"sign\",\n \"verify\",\n ]),\n catch: importError,\n }),\n ),\n );\n\n return {\n sign: Effect.fnUntraced(function* (input) {\n const data = yield* decode(Schema.Uint8Array, input, \"data\").pipe(Effect.flatMap(copy));\n\n return new Uint8Array(\n yield* use((key) =>\n Effect.tryPromise({ try: () => subtle.sign(\"HMAC\", key, data), catch: nativeError }),\n ),\n );\n }),\n verify: Effect.fnUntraced(function* (input, signature) {\n const data = yield* decode(Schema.Uint8Array, input, \"data\").pipe(Effect.flatMap(copy));\n const tag = yield* decode(Schema.Uint8Array, signature, \"data\").pipe(Effect.flatMap(copy));\n\n return yield* use((key) =>\n Effect.tryPromise({\n try: () => subtle.verify(\"HMAC\", key, tag, data),\n catch: nativeError,\n }),\n );\n }),\n } satisfies Key;\n });\n\n return Hmac.of({\n importKey,\n sign: Effect.fnUntraced(function* (input) {\n const value = yield* decode(Input, input, \"data\");\n const data = yield* copy(value.data);\n const key = yield* importKey(value);\n\n return yield* key.sign(data);\n }, Effect.scoped),\n verify: Effect.fnUntraced(function* (input) {\n const value = yield* decode(VerifyInput, input, \"data\");\n const data = yield* copy(value.data);\n const tag = yield* copy(value.tag);\n const key = yield* importKey(value);\n\n return yield* key.verify(data, tag);\n }, Effect.scoped),\n });\n};\n"],"mappings":";;;;;AAMA,MAAM,WAAW,OAAO,WAAW,MAAM,OAAO,YAAY,CAAC,CAAC;AAE9D,MAAa,YAAY,WAA0C;CACjE,MAAM,YAAY,OAAO,WAAW,WAAW,OAAiB;EAC9D,MAAM,QAAQ,OAAO,OAAO,UAAU,OAAO,KAAK;EAElD,OAAO,OAAO,UAAU,SAAS,MAAM,MAAM,GAAG,GAAG,KAAK;EAExD,MAAM,MAAM,OAAO,cACjB,WAAW,MAAM,MAAM,aACrB,OAAO,WAAW;GAChB,WACE,OAAO,UAAU,OAAO,UAAU;IAAE,MAAM;IAAQ,MAAM,MAAM;GAAU,GAAG,OAAO,CAChF,QACA,QACF,CAAC;GACH,OAAO;EACT,CAAC,CACH,CACF;EAEA,OAAO;GACL,MAAM,OAAO,WAAW,WAAW,OAAO;IACxC,MAAM,OAAO,OAAO,OAAO,OAAO,YAAY,OAAO,MAAM,CAAC,CAAC,KAAK,OAAO,QAAQ,IAAI,CAAC;IAEtF,OAAO,IAAI,WACT,OAAO,KAAK,QACV,OAAO,WAAW;KAAE,WAAW,OAAO,KAAK,QAAQ,KAAK,IAAI;KAAG,OAAO;IAAY,CAAC,CACrF,CACF;GACF,CAAC;GACD,QAAQ,OAAO,WAAW,WAAW,OAAO,WAAW;IACrD,MAAM,OAAO,OAAO,OAAO,OAAO,YAAY,OAAO,MAAM,CAAC,CAAC,KAAK,OAAO,QAAQ,IAAI,CAAC;IACtF,MAAM,MAAM,OAAO,OAAO,OAAO,YAAY,WAAW,MAAM,CAAC,CAAC,KAAK,OAAO,QAAQ,IAAI,CAAC;IAEzF,OAAO,OAAO,KAAK,QACjB,OAAO,WAAW;KAChB,WAAW,OAAO,OAAO,QAAQ,KAAK,KAAK,IAAI;KAC/C,OAAO;IACT,CAAC,CACH;GACF,CAAC;EACH;CACF,CAAC;CAED,OAAO,KAAK,GAAG;EACb;EACA,MAAM,OAAO,WAAW,WAAW,OAAO;GACxC,MAAM,QAAQ,OAAO,OAAO,OAAO,OAAO,MAAM;GAChD,MAAM,OAAO,OAAO,KAAK,MAAM,IAAI;GAGnC,OAAO,QAAO,OAFK,UAAU,KAAK,EAAA,CAEhB,KAAK,IAAI;EAC7B,GAAG,OAAO,MAAM;EAChB,QAAQ,OAAO,WAAW,WAAW,OAAO;GAC1C,MAAM,QAAQ,OAAO,OAAO,aAAa,OAAO,MAAM;GACtD,MAAM,OAAO,OAAO,KAAK,MAAM,IAAI;GACnC,MAAM,MAAM,OAAO,KAAK,MAAM,GAAG;GAGjC,OAAO,QAAO,OAFK,UAAU,KAAK,EAAA,CAEhB,OAAO,MAAM,GAAG;EACpC,GAAG,OAAO,MAAM;CAClB,CAAC;AACH"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { CryptoUnavailable } from "../Errors.mjs";
|
|
2
|
+
import { Effect, Exit, Fiber, Scope } from "effect";
|
|
3
|
+
//#region src/internal/scoped-key.ts
|
|
4
|
+
/** Join native calls before releasing a key, including parallel parent closure. */
|
|
5
|
+
const makeScopedKey = Effect.fnUntraced(function* (acquire) {
|
|
6
|
+
const scope = yield* Scope.fork(yield* Effect.scope, "sequential");
|
|
7
|
+
let imported;
|
|
8
|
+
yield* Scope.addFinalizer(scope, Effect.sync(() => {
|
|
9
|
+
imported = void 0;
|
|
10
|
+
}));
|
|
11
|
+
const available = Effect.suspend(() => scope.state._tag === "Closed" ? Effect.fail(CryptoUnavailable.make({})) : Effect.void);
|
|
12
|
+
const run = (operation) => Effect.acquireUseRelease(available.pipe(Effect.andThen(Effect.forkIn(operation, scope, {
|
|
13
|
+
uninterruptible: false,
|
|
14
|
+
startImmediately: true
|
|
15
|
+
}))), (fiber) => Effect.gen(function* () {
|
|
16
|
+
const exit = yield* Fiber.await(fiber);
|
|
17
|
+
yield* available;
|
|
18
|
+
return yield* exit;
|
|
19
|
+
}), Fiber.interrupt);
|
|
20
|
+
yield* run(acquire.pipe(Effect.tap((key) => Effect.sync(() => {
|
|
21
|
+
imported = key;
|
|
22
|
+
})), Effect.uninterruptible)).pipe(Effect.onExit((exit) => Exit.isFailure(exit) ? Scope.close(scope, exit) : Effect.void));
|
|
23
|
+
return (operation) => run(Effect.suspend(() => {
|
|
24
|
+
const key = imported;
|
|
25
|
+
return key === void 0 ? Effect.fail(CryptoUnavailable.make({})) : operation(key);
|
|
26
|
+
}).pipe(Effect.uninterruptible));
|
|
27
|
+
});
|
|
28
|
+
//#endregion
|
|
29
|
+
export { makeScopedKey };
|
|
30
|
+
|
|
31
|
+
//# sourceMappingURL=scoped-key.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scoped-key.mjs","names":[],"sources":["../../src/internal/scoped-key.ts"],"sourcesContent":["import { Effect, Exit, Fiber, Scope } from \"effect\";\n\nimport { CryptoUnavailable, type OperationError } from \"../Errors\";\n\n/** Join native calls before releasing a key, including parallel parent closure. */\nexport const makeScopedKey = Effect.fnUntraced(function* (\n acquire: Effect.Effect<CryptoKey, OperationError>,\n) {\n const scope = yield* Scope.fork(yield* Effect.scope, \"sequential\");\n let imported: CryptoKey | undefined;\n\n yield* Scope.addFinalizer(\n scope,\n Effect.sync(() => {\n imported = undefined;\n }),\n );\n\n const available = Effect.suspend(() =>\n scope.state._tag === \"Closed\" ? Effect.fail(CryptoUnavailable.make({})) : Effect.void,\n );\n\n const run = <A, E>(operation: Effect.Effect<A, E>) =>\n Effect.acquireUseRelease(\n available.pipe(\n Effect.andThen(\n Effect.forkIn(operation, scope, { uninterruptible: false, startImmediately: true }),\n ),\n ),\n (fiber) =>\n Effect.gen(function* () {\n const exit = yield* Fiber.await(fiber);\n\n yield* available;\n\n return yield* exit;\n }),\n Fiber.interrupt,\n );\n\n yield* run(\n acquire.pipe(\n Effect.tap((key) =>\n Effect.sync(() => {\n imported = key;\n }),\n ),\n Effect.uninterruptible,\n ),\n ).pipe(Effect.onExit((exit) => (Exit.isFailure(exit) ? Scope.close(scope, exit) : Effect.void)));\n\n return <A, E>(operation: (key: CryptoKey) => Effect.Effect<A, E>) =>\n run(\n Effect.suspend((): Effect.Effect<A, E | CryptoUnavailable> => {\n const key = imported;\n\n return key === undefined ? Effect.fail(CryptoUnavailable.make({})) : operation(key);\n }).pipe(Effect.uninterruptible),\n );\n});\n"],"mappings":";;;;AAKA,MAAa,gBAAgB,OAAO,WAAW,WAC7C,SACA;CACA,MAAM,QAAQ,OAAO,MAAM,KAAK,OAAO,OAAO,OAAO,YAAY;CACjE,IAAI;CAEJ,OAAO,MAAM,aACX,OACA,OAAO,WAAW;EAChB,WAAW,KAAA;CACb,CAAC,CACH;CAEA,MAAM,YAAY,OAAO,cACvB,MAAM,MAAM,SAAS,WAAW,OAAO,KAAK,kBAAkB,KAAK,CAAC,CAAC,CAAC,IAAI,OAAO,IACnF;CAEA,MAAM,OAAa,cACjB,OAAO,kBACL,UAAU,KACR,OAAO,QACL,OAAO,OAAO,WAAW,OAAO;EAAE,iBAAiB;EAAO,kBAAkB;CAAK,CAAC,CACpF,CACF,IACC,UACC,OAAO,IAAI,aAAa;EACtB,MAAM,OAAO,OAAO,MAAM,MAAM,KAAK;EAErC,OAAO;EAEP,OAAO,OAAO;CAChB,CAAC,GACH,MAAM,SACR;CAEF,OAAO,IACL,QAAQ,KACN,OAAO,KAAK,QACV,OAAO,WAAW;EAChB,WAAW;CACb,CAAC,CACH,GACA,OAAO,eACT,CACF,CAAC,CAAC,KAAK,OAAO,QAAQ,SAAU,KAAK,UAAU,IAAI,IAAI,MAAM,MAAM,OAAO,IAAI,IAAI,OAAO,IAAK,CAAC;CAE/F,QAAc,cACZ,IACE,OAAO,cAAuD;EAC5D,MAAM,MAAM;EAEZ,OAAO,QAAQ,KAAA,IAAY,OAAO,KAAK,kBAAkB,KAAK,CAAC,CAAC,CAAC,IAAI,UAAU,GAAG;CACpF,CAAC,CAAC,CAAC,KAAK,OAAO,eAAe,CAChC;AACJ,CAAC"}
|