Hyperlinkv0.8.0-beta.28

Crypto

Crypto.makeconsteffect/Crypto.ts:214
(impl: {
  readonly randomBytes: (size: number) => Uint8Array
  readonly digest: (
    algorithm: DigestAlgorithm,
    data: Uint8Array
  ) => Effect.Effect<Uint8Array, PlatformError.PlatformError>
}): Crypto

Creates a Crypto service from the primitive implementation, deriving the random generator helpers and UUID generation from those primitives.

When to use

Use to build a Crypto service for a platform integration, test layer, or custom runtime from primitive random-byte and digest operations.

Details

The constructor derives random numbers, booleans, integer ranges, shuffling, and UUID generation from impl.randomBytes. Digest operations delegate to impl.digest.

Gotchas

impl.randomBytes must return cryptographically secure bytes of the requested length. UUID formatting mutates the byte array returned for UUID generation, so the implementation should return a fresh array for each call.

Example (Creating a Crypto service)

import { Crypto, Effect, Layer } from "effect"

const TestCrypto = Layer.succeed(
  Crypto.Crypto,
  Crypto.make({
    randomBytes: (size) => new Uint8Array(size),
    digest: (_algorithm, data) => Effect.succeed(data)
  })
)
constructors
Source effect/Crypto.ts:21458 lines
export const make = (
  impl: {
    readonly randomBytes: (size: number) => Uint8Array
    readonly digest: (
      algorithm: DigestAlgorithm,
      data: Uint8Array
    ) => Effect.Effect<Uint8Array, PlatformError.PlatformError>
  }
): Crypto => {
  const randomBytesUnsafe = impl.randomBytes

  const randomBytes: Crypto["randomBytes"] = (size) => Effect.map(validateSize("randomBytes", size), randomBytesUnsafe)

  const nextDoubleUnsafe = (): number => {
    const bytes = randomBytesUnsafe(7)
    const value = ((bytes[0] & 0x1f) * 2 ** 48) + (bytes[1] * 2 ** 40) + (bytes[2] * 2 ** 32) +
      (bytes[3] * 2 ** 24) + (bytes[4] * 2 ** 16) + (bytes[5] * 2 ** 8) + bytes[6]
    return value / 2 ** 53
  }

  const nextIntUnsafe = (): number =>
    Math.floor(nextDoubleUnsafe() * (Number.MAX_SAFE_INTEGER - Number.MIN_SAFE_INTEGER + 1)) + Number.MIN_SAFE_INTEGER

  return Crypto.of({
    [TypeId]: TypeId,
    randomBytes,
    nextDoubleUnsafe,
    nextIntUnsafe,
    digest: impl.digest,
    random: Effect.sync(() => nextDoubleUnsafe()),
    randomBoolean: Effect.sync(() => nextDoubleUnsafe() > 0.5),
    randomInt: Effect.sync(() => nextIntUnsafe()),
    randomBetween: (min, max) => Effect.sync(() => nextDoubleUnsafe() * (max - min) + min),
    randomIntBetween(min, max, options) {
      const extra = options?.halfOpen === true ? 0 : 1
      return Effect.sync(() => {
        const minInt = Math.ceil(min)
        const maxInt = Math.floor(max)
        return Math.floor(nextDoubleUnsafe() * (maxInt - minInt + extra)) + minInt
      })
    },
    randomShuffle: (elements) =>
      Effect.sync(() => {
        const buffer = Array.from(elements)
        for (let i = buffer.length - 1; i >= 1; i = i - 1) {
          const index = Math.min(i, Math.floor(nextDoubleUnsafe() * (i + 1)))
          const value = buffer[i]!
          buffer[i] = buffer[index]!
          buffer[index] = value
        }
        return buffer
      }),
    randomUUIDv4: Effect.sync(() => formatUUIDv4(randomBytesUnsafe(16))),
    randomUUIDv7: Effect.clockWith((clock) =>
      Effect.succeed(formatUUIDv7(clock.currentTimeMillisUnsafe(), randomBytesUnsafe(16)))
    )
  })
}