Skip to content

Releases: tomisacat/Obfuscated

Obfuscated 2.1.0

14 Jun 09:36

Choose a tag to compare

Obfuscated 2.1.0

Obfuscated 2.1.0 extends #Obfuscated beyond strings to Int, Bool, Data, and enums. This is an additive minor release — existing string macro usage, built-in methods, and custom step wiring are unchanged.

Requirements

  • macOS 15+, iOS 14+, tvOS 14+, watchOS 7+, macCatalyst 14+
  • Swift 6.2+
  • Xcode 16+ (macro plugin support)

Installation

Add Obfuscated via Swift Package Manager:

dependencies: [
    .package(url: "https://github.com/tomisacat/Obfuscated.git", from: "2.1.0"),
],
targets: [
    .target(
        name: "<YourAppTarget>",
        dependencies: ["Obfuscated"]
    ),
]

For an Xcode app target, use File → Add Package Dependencies and enter https://github.com/tomisacat/Obfuscated.git.

Products

Product Use when
Obfuscated Default — built-in methods only; #Obfuscated via ObfuscatedMacros
ObfuscatedCore Direct pipeline access, tests, or custom step libraries
ObfuscatedMacroSupport Building a user-owned macro plugin with custom ObfuscationStep types

Highlights

Typed value obfuscation

#Obfuscated now accepts compile-time literals for multiple types and returns ordinary values at runtime — no wrapper types, no manual decode:

Type Example
String #Obfuscated("secret", methods: [...])
Int #Obfuscated(443, methods: [...])
Bool #Obfuscated(true, methods: [...])
Data #Obfuscated([0xDE, 0xAD], methods: [...])
Enum case #Obfuscated(Environment.production, methods: [...]) — requires CaseIterable
RawRepresentable #Obfuscated(1, as: Color.self, methods: [...])
let port: Int = #Obfuscated(443, methods: [.xor(key: 0x11)])
let enabled: Bool = #Obfuscated(true, methods: [.xor(key: 1)])
let token: Data = #Obfuscated([0xDE, 0xAD, 0xBE, 0xEF], methods: [.xor(key: 0x5A)])

enum Environment: CaseIterable { case production, staging }
let env: Environment = #Obfuscated(Environment.production, methods: [.xor(key: 0x3C)])

enum Role: String { case admin, guest }
let role: Role = #Obfuscated("admin", as: Role.self, methods: [.xor(key: 0x2A)])

Enum semantics:

  • Type.case obfuscates the case name as UTF-8. Requires CaseIterable and Sendable. Applies to any CaseIterable enum — even when it also has an Int or String raw value.
  • as: Type.self obfuscates the raw value (Int → 8-byte big-endian Int64; String → UTF-8). Requires RawRepresentable. Use when you want the stored raw value hidden.
  • Both forms are valid when an enum conforms to CaseIterable and RawRepresentable: #Obfuscated(Color.red, ...) hides "red"; #Obfuscated(1, as: Color.self, ...) hides 1.
  • Enums without a raw value (e.g. Environment) only support the Type.case form.

See README — Limitations for the full caveat list.

All types share the same obfuscation method pipeline (XOR, crypto, custom steps, etc.) after per-type serialization to plaintext bytes.

New public types

Type Role
ObfuscatedValue Protocol for serializing values to/from plaintext bytes before/after the pipeline
ObfuscatedEnumSupport CaseIterable case lookup by decoded case name
ObfuscatedRawRepresentableSupport Encode/decode helpers for RawRepresentable raw values

Built-in ObfuscatedValue conformances: String, Int, Bool, Data.

Re-exported from Obfuscated as public typealias ObfuscatedValue.

Pipeline and runtime extensions

API Role
ObfuscationPipeline.encode(bytes:methods:) Byte-level encode entry point
ObfuscationPipeline.encode<T: ObfuscatedValue>(_:methods:) Generic value encode
ObfuscationPipeline.decodeBytes(_:methods:) Reverse pipeline to plaintext bytes
ObfuscationPipeline.decode<T: ObfuscatedValue>(_:methods:as:) Generic value decode
ObfuscatedRuntime._decode(bytes:methods:material:as:) Typed runtime decode for macro expansions
ObfuscatedRuntime._decodeRawRepresentable(...) Raw-representable enum decode
ObfuscatedRuntime._decodeCaseIterable(...) Case-name enum decode

Existing ObfuscationPipeline.encode(_:methods:) / decode(_:methods:) -> String and ObfuscatedRuntime._decode(bytes:methods:material:) -> String are unchanged.

Demo updates

The demo app adds a Typed Values section covering Int, Bool, Data, CaseIterable enum cases, and RawRepresentable enums. ObfuscatedDemoKit mirrors the new macro overloads.

Migration from 2.0.0

  1. Typical app (strings only) — bump the dependency and rebuild:

    .package(url: "https://github.com/tomisacat/Obfuscated.git", from: "2.1.0")

    No source changes expected.

  2. Adopting typed values — use the new overloads where compile-time literals fit your use case. Variables and runtime expressions are still not supported.

  3. Custom macro plugin authors — rebuild your plugin. If you exhaustively switch on ObfuscatedMacroError, add cases for missingValueLiteral and invalidTypeExpression.

  4. Pin to 2.0.0 if you are not ready to upgrade:

    .package(url: "https://github.com/tomisacat/Obfuscated.git", exact: "2.0.0")

Unchanged from 2.0.0

  • String #Obfuscated overload, expansion shape, and default plugin (ObfuscatedMacros)
  • All built-in and custom obfuscation methods
  • ObfuscationMethod, ObfuscationError, and CryptoMaterial shapes
  • Static string interpolation (\("...") literals folded at compile time)
  • Custom step setup via ObfuscationStep and user-owned macro plugins
  • Platform and Swift version requirements

Limitations

See also the canonical list in README — Limitations.

  • Compile-time literals only — not variables or runtime expressions
  • Enum cases via Type.case require CaseIterable and Sendable; associated values are not supported
  • Type.case works for any CaseIterable enum, including Int- or String-backed enums that also conform to RawRepresentable — it obfuscates the case name, not the raw value
  • When an enum is both CaseIterable and RawRepresentable, choose the overload by what you want hidden: #Obfuscated(Color.red, ...) → name "red"; #Obfuscated(1, as: Color.self, ...) → raw value 1
  • Enums without RawRepresentable (e.g. Environment.production) only support the Type.case form

Documentation

Testing

60 unit tests cover encode/decode round-trips, typed values (ObfuscatedValueTests), built-in and custom step pipelines, macro parsing, and expansion. CI runs swift test and demo app builds on every push and pull request to main.

License

MIT License — see LICENSE.


Tag: 2.1.0

Obfuscated 2.0.0

14 Jun 07:37

Choose a tag to compare

Obfuscated 2.0.0

Obfuscated 2.0.0 adds user-defined obfuscation steps, extracts shared macro infrastructure into ObfuscatedMacroSupport, and publishes ObfuscatedCore as a standalone product. Built-in #Obfuscated usage is unchanged for typical apps; this is a major release because public enums and CryptoMaterial gained new cases and fields.

Requirements

  • macOS 15+, iOS 14+, tvOS 14+, watchOS 7+, macCatalyst 14+
  • Swift 6.2+
  • Xcode 16+ (macro plugin support)

Installation

Add Obfuscated via Swift Package Manager:

dependencies: [
    .package(url: "https://github.com/tomisacat/Obfuscated.git", from: "2.0.0"),
],
targets: [
    .target(
        name: "<YourAppTarget>",
        dependencies: ["Obfuscated"]
    ),
]

For an Xcode app target, use File → Add Package Dependencies and enter https://github.com/tomisacat/Obfuscated.git.

Products

Product Use when
Obfuscated Default — built-in methods only; #Obfuscated via ObfuscatedMacros
ObfuscatedCore Direct pipeline access, tests, or custom step libraries
ObfuscatedMacroSupport Building a user-owned macro plugin with custom ObfuscationStep types

Highlights

Custom obfuscation steps

Define your own transforms with the ObfuscationStep protocol and use them in #Obfuscated via .custom(id:parameters:):

let secret = #Obfuscated(
    "Custom protected secret",
    methods: [.custom(id: "rot13", parameters: ObfuscationParameters(bytes: [13]))]
)

Custom steps require a user-owned macro plugin target that links ObfuscatedMacroSupport and registers step types before expansion. Macro expansion runs in a plugin process that cannot import your app module.

Full setup guide: docs/CUSTOM_OBFUSCATION_STEPS.md

Working example in this repo: Demo/ObfuscatedDemoSupport (ObfuscatedDemoKit + ObfuscatedDemoMacros + DemoRot13Step).

ObfuscatedMacroSupport extraction

Macro parsing, expansion building, and ObfuscatedMacro now live in the ObfuscatedMacroSupport library. ObfuscatedMacros is a thin default compiler plugin (built-in methods only). Advanced users link ObfuscatedMacroSupport when building custom plugins.

New public types

Type Role
ObfuscationStep Protocol for user-defined encode/decode transforms
ObfuscationParameters Literal byte parameters embedded in macro expansions
ObfuscationStepRegistry Registers custom steps for pipeline + macro encode
CustomMaterialEntry Per-step persisted state in CryptoMaterial
ObfuscationMacroConfiguration Plugin init hook to run step registration

Re-exported from Obfuscated alongside existing ObfuscationMethod, ObfuscatedKey, etc.

Demo package

The demo app's custom-step wiring moved out of the root package into a local SPM package at Demo/ObfuscatedDemoSupport/. The Xcode demo imports ObfuscatedDemoKit from that package instead of depending on the root Obfuscated product directly.

Breaking changes (from 1.0.0)

ObfuscationMethod — new case

case custom(id: String, parameters: ObfuscationParameters)

Impact: Exhaustive switch statements over ObfuscationMethod must handle .custom or add @unknown default.

No impact for typical usage: #Obfuscated("...", methods: [.xor(key: 0x5A), .base64]) with built-in method literals.

ObfuscationError — new cases

  • unknownCustomStep(String)
  • missingCustomMaterial

Impact: Exhaustive switch statements over ObfuscationError must handle the new cases.

No impact for typical #Obfuscated callers — ObfuscatedRuntime._decode catches errors internally.

CryptoMaterial — new field

public var customEntries: [CustomMaterialEntry]
public init(entries: [CryptoEntry] = [], customEntries: [CustomMaterialEntry] = [])

Impact: Code that pattern-matches CryptoMaterial exhaustively or assumes a fixed shape may need updates.

No impact for CryptoMaterial(entries: [...]) call sites — customEntries defaults to [].

Macro module layout

Macro implementation code moved from ObfuscatedMacros to ObfuscatedMacroSupport. The default #Obfuscated declaration still points at ObfuscatedMacros.ObfuscatedMacro.

Impact: Projects that imported or @testable imported ObfuscatedMacros internals directly must migrate to ObfuscatedMacroSupport.

No impact for import Obfuscated consumers.

Migration from 1.0.0

  1. Typical app (built-in methods only) — bump the dependency and rebuild:

    .package(url: "https://github.com/tomisacat/Obfuscated.git", from: "2.0.0")

    No source changes expected.

  2. Exhaustive enum switches — add cases for .custom, unknownCustomStep, and missingCustomMaterial, or use @unknown default.

  3. Custom steps (new in 2.0) — follow CUSTOM_OBFUSCATION_STEPS.md:

    • Implement ObfuscationStep
    • Add a .macro target with ObfuscatedMacroSupport
    • Register steps in the plugin init and at app launch for runtime decode
    • Point #Obfuscated at your plugin with #externalMacro
  4. Pin to 1.0.0 if you are not ready to upgrade:

    .package(url: "https://github.com/tomisacat/Obfuscated.git", exact: "1.0.0")

Unchanged from 1.0.0

  • #Obfuscated macro signature and default plugin module (ObfuscatedMacros)
  • All built-in obfuscation methods (XOR, bit shift, bit OR, Base64, AES-GCM, ChaCha, HMAC, HKDF, ECIES)
  • Static string interpolation (\("...") literals folded at compile time)
  • ObfuscatedRuntime._decode(bytes:methods:material:) entry point
  • Platform and Swift version requirements

Documentation

Testing

48 unit tests cover encode/decode round-trips, built-in and custom step pipelines, macro parsing, and expansion. CI runs swift test and demo app builds on every push and pull request to main.

License

MIT License — see LICENSE.


Tag: 2.0.0

Obfuscated 1.0.0

12 Jun 05:53

Choose a tag to compare

Obfuscated 1.0.0

Obfuscated is a compile-time Swift macro that obfuscates string literals at build time and returns ordinary String values at runtime.

Requirements

  • macOS 15+, iOS 14+, tvOS 14+, watchOS 7+, macCatalyst 14+
  • Swift 6.2+
  • Xcode 16+ (macro plugin support)

Installation

Add Obfuscated via Swift Package Manager:

dependencies: [
    .package(url: "https://github.com/tomisacat/Obfuscated.git", from: "1.0.0"),
],
targets: [
    .target(
        name: "<YourAppTarget>",
        dependencies: ["Obfuscated"]
    ),
]

For an Xcode app target, use File → Add Package Dependencies and enter https://github.com/tomisacat/Obfuscated.git.

Highlights

#Obfuscated macro

Use #Obfuscated("secret", methods: [...]) to embed obfuscated bytes and crypto material in your binary. The macro expands to a hidden decode call; callers receive a normal String with no wrapper type or manual decryption.

Obfuscation pipeline

Chain lightweight transforms (XOR, bit shift, bit OR, Base64) with CryptoKit-backed methods (AES-GCM, ChaCha20-Poly1305, HMAC keystream, HKDF + AEAD, Curve25519/P-256 ECIES). Pass nil for crypto key material to generate random values at compile time, or supply explicit ObfuscatedKey, ObfuscatedNonce, ObfuscatedSalt, and ObfuscatedInfo for reproducible output.

Static string interpolation

String literals with static \("...") segments are folded at compile time and obfuscated as one value. Runtime variables inside \(...) are not supported.

Documentation

Testing

Unit tests cover encode/decode round-trips, crypto edge cases, macro parsing, and expansion. CI runs swift test and demo app builds on every push and pull request to main.

License

MIT License — see LICENSE.


Tag: v1.0.0