@yielded/crypto 0.1.0-beta.28 → 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 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 batches,
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. `Hmac.importKey` snapshots a reusable,
34
- nonextractable key in the caller's Scope; closure joins native work and prevents
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;;;;;;;AASvF,IAAa,OAAb,cAA0B,QAAQ,QAQhC,CAAC,CAAC,sBAAsB,CAAC,CAAC,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"}
@@ -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>;
@@ -50,14 +50,20 @@ const PrivateKeyParameters = Schema.Union([
50
50
  qi: integer
51
51
  })
52
52
  ]);
53
- const SignInput = Schema.Struct({
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
- privateKey: Schema.Redacted(Schema.Uint8Array, { disallowJsonEncode: true }),
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
- algorithm: Algorithm,
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
@@ -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 SignInput = Schema.Struct({\n algorithm: Algorithm,\n privateKey: Schema.Redacted(Schema.Uint8Array, { disallowJsonEncode: true }),\n data: Schema.Uint8Array,\n});\n\nexport type SignInput = typeof SignInput.Type;\n\nexport const VerifyInput = Schema.Struct({\n algorithm: Algorithm,\n publicKey: Schema.Uint8Array,\n data: Schema.Uint8Array,\n signature: Schema.Uint8Array,\n});\n\nexport type VerifyInput = typeof VerifyInput.Type;\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 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,YAAY,OAAO,OAAO;CACrC,WAAW;CACX,YAAY,OAAO,SAAS,OAAO,YAAY,EAAE,oBAAoB,KAAK,CAAC;CAC3E,MAAM,OAAO;AACf,CAAC;AAID,MAAa,cAAc,OAAO,OAAO;CACvC,WAAW;CACX,WAAW,OAAO;CAClB,MAAM,OAAO;CACb,WAAW,OAAO;AACpB,CAAC;;;;;;AASD,IAAa,YAAb,cAA+B,QAAQ,QAYrC,CAAC,CAAC,2BAA2B,CAAC,CAAC,CAAC"}
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"}
@@ -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 makeAead = (subtle, xchacha) => Aead.of({
8
- encrypt: Effect.fnUntraced(function* (input) {
9
- const value = yield* decode(EncryptInput, input, "data");
10
- const nonceLength = value.algorithm === "AES-256-GCM" ? 12 : 24;
11
- yield* decode(keyBytes, Redacted.value(value.key), "key");
12
- yield* decode(Schema.Uint8Array.check(Schema.isBetweenLength(nonceLength, nonceLength)), value.nonce, "nonce");
13
- if (value.algorithm === "XChaCha20-Poly1305" && xchacha === void 0) return yield* UnsupportedAlgorithm.make({});
14
- const nonce = yield* copy(value.nonce);
15
- const additionalData = yield* copy(value.additionalData ?? /* @__PURE__ */ new Uint8Array());
16
- return yield* withSecret(value.key, (key) => withSecret(value.plaintext, (data) => {
17
- if (value.algorithm === "XChaCha20-Poly1305") return xchacha === void 0 ? Effect.fail(UnsupportedAlgorithm.make({})) : xchacha.encrypt({
18
- key,
19
- nonce,
20
- additionalData,
21
- data
22
- });
23
- return Effect.gen(function* () {
24
- const imported = yield* Effect.tryPromise({
25
- try: () => subtle.importKey("raw", key, "AES-GCM", false, ["encrypt"]),
26
- catch: importError
27
- });
28
- const encrypted = yield* Effect.tryPromise({
29
- try: () => subtle.encrypt({
30
- name: "AES-GCM",
31
- iv: nonce,
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
- tagLength: 128
34
- }, imported, data),
35
- catch: nativeError
36
- });
37
- return new Uint8Array(encrypted);
38
- }).pipe(Effect.uninterruptible);
39
- }));
40
- }),
41
- decrypt: Effect.fnUntraced(function* (input) {
42
- const value = yield* decode(DecryptInput, input, "data");
43
- const nonceLength = value.algorithm === "AES-256-GCM" ? 12 : 24;
44
- yield* decode(keyBytes, Redacted.value(value.key), "key");
45
- yield* decode(Schema.Uint8Array.check(Schema.isBetweenLength(nonceLength, nonceLength)), value.nonce, "nonce");
46
- if (value.algorithm === "XChaCha20-Poly1305" && xchacha === void 0) return yield* UnsupportedAlgorithm.make({});
47
- if (value.ciphertext.length < 16) return yield* AuthenticationFailed.make({});
48
- const nonce = yield* copy(value.nonce);
49
- const additionalData = yield* copy(value.additionalData ?? /* @__PURE__ */ new Uint8Array());
50
- const data = yield* copy(value.ciphertext);
51
- const plaintext = yield* withSecret(value.key, (key) => {
52
- if (value.algorithm === "XChaCha20-Poly1305") return xchacha === void 0 ? Effect.fail(UnsupportedAlgorithm.make({})) : xchacha.decrypt({
53
- key,
54
- nonce,
55
- additionalData,
56
- data
57
- });
58
- return Effect.gen(function* () {
59
- const imported = yield* Effect.tryPromise({
60
- try: () => subtle.importKey("raw", key, "AES-GCM", false, ["decrypt"]),
61
- catch: importError
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
- const decrypted = yield* Effect.tryPromise({
64
- try: () => subtle.decrypt({
65
- name: "AES-GCM",
66
- iv: nonce,
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
- tagLength: 128
69
- }, imported, data),
70
- catch: (cause) => nativeErrorName(cause) === "OperationError" ? AuthenticationFailed.make({}) : nativeError(cause)
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 new Uint8Array(decrypted);
73
- }).pipe(Effect.uninterruptible);
74
- });
75
- return Redacted.make(plaintext);
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":";;;;;;AAgCA,MAAM,WAAW,OAAO,WAAW,MAAM,OAAO,gBAAgB,IAAI,EAAE,CAAC;AACvE,MAAM,WAAW,OAAO,WAAW,MAAM,OAAO,gBAAgB,IAAI,EAAE,CAAC;AAEvE,MAAa,YAAY,QAAsB,YAAuC;CACpF,MAAM,cAAc,KAAgB,UAClC,OAAO,WAAW;EAChB,WACE,OAAO,QACL;GACE,MAAM;GACN,IAAI,MAAM;GACV,gBAAgB,MAAM;GACtB,WAAW;EACb,GACA,KACA,MAAM,IACR;EACF,OAAO;CACT,CAAC,CAAC,CAAC,KAAK,OAAO,KAAK,cAAc,IAAI,WAAW,SAAS,CAAC,CAAC;CAE9D,MAAM,cAAc,KAAgB,UAClC,OAAO,WAAW;EAChB,WACE,OAAO,QACL;GACE,MAAM;GACN,IAAI,MAAM;GACV,gBAAgB,MAAM;GACtB,WAAW;EACb,GACA,KACA,MAAM,IACR;EAEF,QAAQ,UACN,gBAAgB,KAAK,MAAM,mBACvB,qBAAqB,KAAK,CAAC,CAAC,IAC5B,YAAY,KAAK;CACzB,CAAC,CAAC,CAAC,KAAK,OAAO,KAAK,cAAc,IAAI,WAAW,SAAS,CAAC,CAAC;CAE9D,OAAO,KAAK,GAAG;EACb,WAAW,OAAO,WAAW,WAAW,OAAO;GAC7C,MAAM,QAAQ,OAAO,OAAO,UAAU,OAAO,KAAK;GAElD,OAAO,OAAO,UAAU,SAAS,MAAM,MAAM,GAAG,GAAG,KAAK;GAExD,MAAM,MAAM,OAAO,cACjB,WAAW,MAAM,MAAM,aACrB,OAAO,WAAW;IAChB,WAAW,OAAO,UAAU,OAAO,UAAU,WAAW,OAAO,CAAC,WAAW,SAAS,CAAC;IACrF,OAAO;GACT,CAAC,CACH,CACF;GAEA,OAAO;IACL,SAAS,OAAO,WAAW,WAAW,OAAO;KAC3C,MAAM,QAAQ,OAAO,OAAO,iBAAiB,OAAO,MAAM;KAC1D,MAAM,QAAQ,OAAO,OAAO,UAAU,MAAM,OAAO,OAAO,CAAC,CAAC,KAAK,OAAO,QAAQ,IAAI,CAAC;KACrF,MAAM,iBAAiB,OAAO,KAAK,MAAM,kCAAkB,IAAI,WAAW,CAAC;KAE3E,OAAO,OAAO,WAAW,MAAM,YAAY,SACzC,KAAK,QAAQ,WAAW,KAAK;MAAE;MAAO;MAAgB;KAAK,CAAC,CAAC,CAC/D;IACF,CAAC;IACD,SAAS,OAAO,WAAW,WAAW,OAAO;KAC3C,MAAM,QAAQ,OAAO,OAAO,iBAAiB,OAAO,MAAM;KAC1D,MAAM,QAAQ,OAAO,OAAO,UAAU,MAAM,OAAO,OAAO,CAAC,CAAC,KAAK,OAAO,QAAQ,IAAI,CAAC;KACrF,MAAM,iBAAiB,OAAO,KAAK,MAAM,kCAAkB,IAAI,WAAW,CAAC;KAC3E,MAAM,OAAO,OAAO,KAAK,MAAM,UAAU;KAEzC,MAAM,YAAY,OAAO,KAAK,QAC5B,KAAK,SAAS,KACV,OAAO,KAAK,qBAAqB,KAAK,CAAC,CAAC,CAAC,IACzC,WAAW,KAAK;MAAE;MAAO;MAAgB;KAAK,CAAC,CACrD;KAEA,OAAO,SAAS,KAAK,SAAS;IAChC,CAAC;GACH;EACF,CAAC;EACD,SAAS,OAAO,WAAW,WAAW,OAAO;GAC3C,MAAM,QAAQ,OAAO,OAAO,cAAc,OAAO,MAAM;GACvD,MAAM,cAAc,MAAM,cAAc,gBAAgB,KAAK;GAE7D,OAAO,OAAO,UAAU,SAAS,MAAM,MAAM,GAAG,GAAG,KAAK;GACxD,OAAO,OACL,OAAO,WAAW,MAAM,OAAO,gBAAgB,aAAa,WAAW,CAAC,GACxE,MAAM,OACN,OACF;GACA,IAAI,MAAM,cAAc,wBAAwB,YAAY,KAAA,GAC1D,OAAO,OAAO,qBAAqB,KAAK,CAAC,CAAC;GAC5C,MAAM,QAAQ,OAAO,KAAK,MAAM,KAAK;GACrC,MAAM,iBAAiB,OAAO,KAAK,MAAM,kCAAkB,IAAI,WAAW,CAAC;GAE3E,OAAO,OAAO,WAAW,MAAM,MAAM,QACnC,WAAW,MAAM,YAAY,SAAS;IACpC,IAAI,MAAM,cAAc,sBACtB,OAAO,YAAY,KAAA,IACf,OAAO,KAAK,qBAAqB,KAAK,CAAC,CAAC,CAAC,IACzC,QAAQ,QAAQ;KAAE;KAAK;KAAO;KAAgB;IAAK,CAAC;IAG1D,OAAO,OAAO,IAAI,aAAa;KAC7B,MAAM,WAAW,OAAO,OAAO,WAAW;MACxC,WAAW,OAAO,UAAU,OAAO,KAAK,WAAW,OAAO,CAAC,SAAS,CAAC;MACrE,OAAO;KACT,CAAC;KAED,OAAO,OAAO,WAAW,UAAU;MAAE;MAAO;MAAgB;KAAK,CAAC;IACpE,CAAC,CAAC,CAAC,KAAK,OAAO,eAAe;GAChC,CAAC,CACH;EACF,CAAC;EACD,SAAS,OAAO,WAAW,WAAW,OAAO;GAC3C,MAAM,QAAQ,OAAO,OAAO,cAAc,OAAO,MAAM;GACvD,MAAM,cAAc,MAAM,cAAc,gBAAgB,KAAK;GAE7D,OAAO,OAAO,UAAU,SAAS,MAAM,MAAM,GAAG,GAAG,KAAK;GACxD,OAAO,OACL,OAAO,WAAW,MAAM,OAAO,gBAAgB,aAAa,WAAW,CAAC,GACxE,MAAM,OACN,OACF;GACA,IAAI,MAAM,cAAc,wBAAwB,YAAY,KAAA,GAC1D,OAAO,OAAO,qBAAqB,KAAK,CAAC,CAAC;GAC5C,IAAI,MAAM,WAAW,SAAS,IAAI,OAAO,OAAO,qBAAqB,KAAK,CAAC,CAAC;GAC5E,MAAM,QAAQ,OAAO,KAAK,MAAM,KAAK;GACrC,MAAM,iBAAiB,OAAO,KAAK,MAAM,kCAAkB,IAAI,WAAW,CAAC;GAC3E,MAAM,OAAO,OAAO,KAAK,MAAM,UAAU;GAEzC,MAAM,YAAY,OAAO,WAAW,MAAM,MAAM,QAAQ;IACtD,IAAI,MAAM,cAAc,sBACtB,OAAO,YAAY,KAAA,IACf,OAAO,KAAK,qBAAqB,KAAK,CAAC,CAAC,CAAC,IACzC,QAAQ,QAAQ;KAAE;KAAK;KAAO;KAAgB;IAAK,CAAC;IAG1D,OAAO,OAAO,IAAI,aAAa;KAC7B,MAAM,WAAW,OAAO,OAAO,WAAW;MACxC,WAAW,OAAO,UAAU,OAAO,KAAK,WAAW,OAAO,CAAC,SAAS,CAAC;MACrE,OAAO;KACT,CAAC;KAED,OAAO,OAAO,WAAW,UAAU;MAAE;MAAO;MAAgB;KAAK,CAAC;IACpE,CAAC,CAAC,CAAC,KAAK,OAAO,eAAe;GAChC,CAAC;GAED,OAAO,SAAS,KAAK,SAAS;EAChC,CAAC;CACH,CAAC;AACH"}
@@ -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 bounded batches; admission remains held through buffer cleanup. */
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
- for (let batch = 0; batch < 256; batch++) if (blocks.next().done) return output(state, input);
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 bounded batches; 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 initialize(state, input);\n const blocks = fill(state, input);\n\n for (;;) {\n for (let batch = 0; batch < 256; batch++) {\n if (blocks.next().done) return output(state, input);\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,WAAW,OAAO,KAAK;CACvB,MAAM,SAAS,KAAK,OAAO,KAAK;CAEhC,SAAS;EACP,KAAK,IAAI,QAAQ,GAAG,QAAQ,KAAK,SAC/B,IAAI,OAAO,KAAK,CAAC,CAAC,MAAM,OAAO,OAAO,OAAO,KAAK;EAEpD,OAAO,OAAO;CAChB;AACF,CAAC,IACF,UAAU,OAAO,WAAW,QAAQ,KAAK,CAAC,CAC7C"}
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"}
@@ -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 { Effect, Exit, Fiber, Redacted, Schema, Scope } from "effect";
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 scope = yield* Scope.fork(yield* Effect.scope, "sequential");
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
- }).pipe(Effect.tap((key) => Effect.sync(() => {
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) => subtle.sign("HMAC", key, data)));
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) => subtle.verify("HMAC", key, tag, data));
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, Exit, Fiber, Redacted, Schema, Scope } from \"effect\";\n\nimport { CryptoUnavailable } from \"../Errors\";\nimport { Hmac, Input, type Key, KeyInput, VerifyInput } from \"../Hmac\";\nimport { copy, decode, importError, nativeError, withSecret } from \"./common\";\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 // A sequential child joins every native call before dropping the key, even\n // when the caller's Scope runs its other finalizers in parallel.\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(Effect.andThen(Effect.forkIn(operation, scope, { uninterruptible: false }))),\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 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 }).pipe(\n Effect.tap((key) =>\n Effect.sync(() => {\n imported = key;\n }),\n ),\n Effect.uninterruptible,\n ),\n ),\n ).pipe(\n Effect.onExit((exit) => (Exit.isFailure(exit) ? Scope.close(scope, exit) : Effect.void)),\n );\n\n const use = <A>(operation: (key: CryptoKey) => Promise<A>) =>\n run(\n Effect.suspend(() => {\n const key = imported;\n\n return key === undefined\n ? Effect.fail(CryptoUnavailable.make({}))\n : Effect.tryPromise({ try: () => operation(key), catch: nativeError });\n }).pipe(Effect.uninterruptible),\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(yield* use((key) => subtle.sign(\"HMAC\", key, data)));\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) => subtle.verify(\"HMAC\", key, tag, data));\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":"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"}
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"}