Helps you define secure storages for your properties using Swift property wrappers.

Overview

🔐 Secure Property Storage

Helps you define secure storages for your properties using Swift property wrappers.

Twitter Swift License Swift Package Manager Carthage Bitrise Quality Maintainability Coverage Documentation

🌟 Features

All keys are hashed using SHA512 and all values are encrypted using AES-GCM to keep user information safe, automagically. Symmetric key is stored in Keychain in a totally secure way.

🐒 Basic usage

@UserDefault

This property wrapper will store your property in UserDefaults using StoreKey (any String but i recommend you a String typed enum). Optionally, you can assign a default value to the property that will be secure stored at initialization.

@UserDefault(<#StoreKey#>)
var yourProperty: YourType? = yourDefaultValueIfNeeded

UserDefaultsStorage is also available, a subclass of UserDefaults with all the security provided by this library, where you can customize suite name.

@Keychain

This property wrapper will store your property in Keychain using StoreKey.

@Keychain(<#StoreKey#>)
var yourProperty: YourType? = yourDefaultValueIfNeeded

As UserDefaultsStorage, KeychainStorage is also available, where you can customize access, group and synchronize it with iCloud.

@Singleton

This property wrapper will store your property in a memory singleton, every property with the same wrapper and key can access or modify the value from wherever it is.

@Singleton(<#StoreKey#>)
var yourProperty: YourType? = yourDefaultValueIfNeeded

As KeychainStorage, SingletonStorage is also available.

@Inject

This property wrapper is similar to @Singleton but, together with @Register, will inject your dependencies. More details in Dependency Injection usage guide.

@Inject
var yourDependency: YourProtocol?

As SingletonStorage, InjectStorage is also available.

@Store

This is a custom wrapper, you can define your own Storage protocol implementation.

@Store(<#YourStorage#>, <#StoreKey#>)
var yourProperty: YourType? = yourDefaultValueIfNeeded

As InjectStorage, DelegatedStorage is also available with all the magic of this library.

🧙‍♂️ Codable usage

If your property conforms Codable protocol, just add Codable keyword as prefix of your property wrapper.

  • @CodableUserDefault
  • @CodableKeychain
  • @CodableSingleton
  • @CodableStore

🥡 Unwrapped usage

To avoid continually unwrapping your property, just add Unwrapped keyword as prefix of your property wrapper, assign a default value (mandatory except for @UnwrappedInject), and it will return stored value or default value, but your property will always be there for you.

  • @UnwrappedUserDefault
  • @UnwrappedKeychain
  • @UnwrappedSingleton
  • @UnwrappedInject
  • @UnwrappedStore

🥡 + 🧙‍♂️ Combo usage

You can also combine previous cases in case you need it, unwrapped first please.

  • @UnwrappedCodableUserDefault
  • @UnwrappedCodableKeychain
  • @UnwrappedCodableSingleton
  • @UnwrappedCodableStore

💉 Dependency Injection usage

@Register (click to expand)

This property wrapper will register the implementations of your dependencies. Register them wherever you want before inject it, but be sure to do it only once (except if you use qualifiers), for example, in an Injector class. You can register through a protocol or directly using your class implementation.

@Register
var yourDependency: YourProtocol = YourImplementation()

@Register
var yourDependency = YourImplementation()

You can also define a closure that builds your dependency. Just remember cast your dependency if you are going to inject it through a protocol.

@Register
var yourDependency = {
    YourImplementation() as YourProtocol
}

@Register
var yourDependency = {
    YourImplementation()
}
@Inject and @UnwrappedInject (click to expand)

These property wrappers injects your dependencies @Register implementations.

@Inject
var yourDependency: YourProtocol?

@Inject
var yourDependency: YourImplementation?

@UnwrappedInject
var yourUnwrappedDependency: YourProtocol

@UnwrappedInject
var yourUnwrappedDependency: YourImplementation

Scope

Because these property wrappers works similarly to @Singleton, the default scope is .singleton, but if you use builder closures on @Register, you can modify them to inject a single instance.

@Inject(.instance)
var yourDependency: YourProtocol?

@UnwrappedInject(.instance)
var yourUnwrappedDependency: YourProtocol
@InjectWith and @UnwrappedInjectWith (click to expand)

Your dependency may need parameters when injecting, you can pass them with these property wrappers. Simply define a model with your dependency parameters and pass it. It will inject a new instance built with these parameters.

@Register
var yourDependency = { parameters in
    YourImplementation(parameters) as YourProtocol
}

@Inject(YourParameters())
var yourDependency: YourProtocol?

@UnwrappedInject(YourParameters())
var yourUnwrappedDependency: YourProtocol
Qualifiers (click to expand)

You can use qualifiers to provide various implementations of a particular dependency. A qualifier is just a @objc protocol that you apply to a class.

For example, you could declare Dog and Cat qualifier protocols and apply it to another class that conforms Animal protocol. To declare this qualifier, use the following code:

protocol Animal {
  func sound()
}

@objc protocol Dog {}

@objc protocol Cat {}

You can then define multiple classes that conforms Animal protocol and uses this qualifiers:

class DogImplementation: Animal, Dog {
    func sound() { print("Woof!") }
}

class CatImplementation: Animal, Cat {
    func sound() { print("Meow!") }
}

Both implementations of the class can now be @Register:

@Register
var registerDog: Animal = DogImplementation()

@Register
var registerCat: Animal = CatImplementation()

To inject one or the other implementation, simply add the qualifier(s) to your @Inject:

@UnwrappedInject(Dog.self)
var dog: Animal

@UnwrappedInject(Cat.self)
var cat: Animal

dog.sound() // prints Woof!
cat.sound() // prints Meow!
Testing (click to expand)

One of the advantages of dependency injection is that the code can be easily testable with mock implementation. That is why there is a Mock qualifier that has priority over all, so you can have your dependencies defined in the app and create your mock in the test target simply by adding this qualifier.

// App target

class YourImplementation: YourProtocol {}

@Register
var yourDependency: YourProtocol = YourImplementation()

@Inject
var yourDependency: YourProtocol?
// Test target

class YourMock: YourProtocol, Mock {}

@Register
var yourDependency: YourProtocol = YourMock()

👀 Examples

Talk is cheap. Show me the code.

    // Securely stored in UserDefaults.
    @UserDefault("username")
    var username: String?

    // Securely stored in Keychain.
    @Keychain("password")
    var password: String?

    // Securely stored in a Singleton storage.
    @Singleton("sessionToken")
    var sessionToken: String?

    // Securely stored in a Singleton storage.
    // Always has a value, the stored or the default.
    @UnwrappedSingleton("refreshToken")
    var refreshToken: String = "B0610306-A33F"

    struct User: Codable {
        let username: String
        let password: String?
        let sessionToken: String?
    }

    // Codable model securely stored in UserDefaults.
    @CodableUserDefault("user")
    var user: User?

🛠 Compatibility

  • macOS 10.15+
  • iOS 13.0+
  • iPadOS 13.0+
  • tvOS 13.0+
  • watchOS 6.0+

⚙️ Installation

You can use the Swift Package Manager by declaring SecurePropertyStorage as a dependency in your Package.swift file:

.package(url: "https://github.com/alexruperez/SecurePropertyStorage", from: "0.3.0")

By default, all property wrappers are installed and you can import them, but if you want, you can install only some of them:

  • UserDefault: @*UserDefault property wrappers.
  • Keychain: @*Keychain property wrappers.
  • Singleton: @*Singleton property wrappers.
  • Storage: @*Store property wrappers.
  • Inject: @*Inject property wrappers.

For more information, see the Swift Package Manager documentation.

Or you can use Carthage:

github "alexruperez/SecurePropertyStorage"

🍻 Etc.

  • Featured in Dave Verwer's iOS Dev Weekly - Issue 450, thanks Dave!
  • Contributions are very welcome. Thanks Alberto Garcia and Chen!
  • Attribution is appreciated (let's spread the word!), but not mandatory.

👨‍💻 Author

Alex Rupérez – @alexruperez[email protected]

👮‍♂️ License

SecurePropertyStorage is available under the MIT license. See the LICENSE file for more info.

Comments
  • Add targets to Carthage

    Add targets to Carthage

    Carthage was only building Inject module. With this PR it'll build the following targets:

    • Inject
    • Keychain
    • Singleton
    • Storage
    • UserDefault

    This change is Reviewable

    opened by AlbGarciam 7
  • fix name conflicts

    fix name conflicts

    What is the purpose of this pull request? (put an "X" next to item)

    [ ] Bug fix.

    What changes did you make? (Give an overview) Renamed targets

    Which issue (if any) does this pull request address? I'm unable to import this package in latest xcode via SPM

    Is there anything you'd like reviewers to focus on?

    wontfix 
    opened by aehlke 5
  • Fix #15: `Unwrapped` always return defaultValue after restart app

    Fix #15: `Unwrapped` always return defaultValue after restart app

    What is the purpose of this pull request? (put an "X" next to item)

    [ ] Documentation update. [x] Bug fix. [ ] New feature. [ ] Other, please explain:

    What changes did you make? (Give an overview) Fix bug #15 Which issue (if any) does this pull request address?

    Is there anything you'd like reviewers to focus on?


    This change is Reviewable

    opened by qchenqizhi 4
  • Renaming UserDefault property wrapper

    Renaming UserDefault property wrapper

    What is the purpose of this pull request? (put an "X" next to item)

    • [ ] Documentation update.
    • [ ] Bug fix.
    • [ ] New feature.
    • [x] Other, please explain:

    What changes did you make? (Give an overview) Renamed @UserDefault (and related) property wrappers to @SecureUserDefault because the type name as well as the target name conflicts with other Swift Packages (i.e. Guillermo Muntaner's Burritos) that also provide UserDefault-related property wrappers (although with different functionality).

    Which issue (if any) does this pull request address? This does not address a specific existing issue, however it does resolve SPM import conflicts on Xcode 11+.

    Is there anything you'd like reviewers to focus on? I realize that this PR may be invalid, or moving in an undesirable direction for the project. However, I thought it was worthwhile to share not only as a fix for the aforementioned issue, but because a more verbose name makes it clearer what the function of this property wrapper is. In fact, I believe that the readability of code using this library would be greatly improved if all of the included property wrappers were prepended with Secure. Of course, that might just be a picky programmer preference I have -- to each their own!

    Regardless, I love the package and am very thankful for your hard work and contribution to the community. I'll be using it in more than a few App Store projects :) 🥂


    This change is Reviewable

    wontfix 
    opened by Sam-Spencer 4
  • Support for Cocoapods?

    Support for Cocoapods?

    I'd love to use SPM but we have other dependencies that aren't available there yet, and using multiple managers in the same project isn't a road I'd like to go down. Can you release there as well?

    enhancement help wanted good first issue question wontfix 
    opened by Vortec4800 4
  • `Unwrapped` always return defaultValue after restart app

    `Unwrapped` always return defaultValue after restart app

    Describe the bug Unwrapped always return defaultValue after restart app

    To Reproduce Steps to reproduce the behavior:

        @UnwrappedCodableUserDefault(wrappedValue: "123", "key")
        var string: String
    

    set the value for var string to "234", then restart app, it's value become to "123"

    Expected behavior defaultValue should only returned when never set the value.

    Environment

    Additional context

    bug 
    opened by qchenqizhi 3
  • Nonce reuse is bad

    Nonce reuse is bad

    For some unknown reason this library is using a fixed nonce. This is bad as AES-GCM can be trivially broken if a nonce is ever reused with the same key ( https://www.elttam.com/blog/key-recovery-attacks-on-gcm/ ).

    I suggest the following: Remove all the nonce generation and saving logic in the keychain and all the custom nonce management code. AES.GCM.seal() will generate a random nonce if none is provided. The nonce does not need to be saved/generated/handled: AES.GCM.seal will serialize the nonce with the cyphertext in the combined representation and automatically read it back in constructor : AES.GCM.SealedBox(combined: data). It is totally transparant to the user ot the AES.GCM api.

    Note: a key can be reused securely up to a certain extent with AES-GCM (IIRC 64Gb max per key), thus to have a really secure storage, a key rotation should be implemented.

    bug 
    opened by shphilippe 3
  • Xcode 12 update.

    Xcode 12 update.

    What is the purpose of this pull request? (put an "X" next to item)

    [ ] Documentation update. [x] Bug fix. [ ] New feature. [ ] Other, please explain:

    What changes did you make? (Give an overview)

    Xcode 12 update.

    Which issue (if any) does this pull request address?

    Unit tests fix.


    This change is Reviewable

    bug enhancement 
    opened by alexruperez 3
  • Allow register dependencies on demand.

    Allow register dependencies on demand.

    Is your feature request related to a problem? Please describe. SecurePropertyStorage is designed to register all dependencies at once, in application(_:didFinishLaunchingWithOptions:) for example.

    Describe the solution you'd like Register dependencies them on demand.

    Requested by @fnicola1

    enhancement help wanted question 
    opened by alexruperez 2
  • Added Dependency Injection Groups and Set support.

    Added Dependency Injection Groups and Set support.

    What is the purpose of this pull request?

    [x] New feature. [x] Documentation update.

    What changes did you make?

    • Dependency Injection Groups.
    • Set support.

    This change is Reviewable

    documentation enhancement help wanted 
    opened by alexruperez 2
  • #19 Add targets to Carthage

    #19 Add targets to Carthage

    #19 Add targets to Carthage by @AlbGarciam

    Also updated Swift Tools to 5.3 and mentioning @AlbGarciam and @qchenqizhi as contributors in the README.md 😃


    This change is Reviewable

    enhancement 
    opened by alexruperez 2
Releases(0.6.0)
Owner
Alex Rupérez
 iOS Technical Manager
Alex Rupérez
Cybr/Secure - A simple but powerful secure password generator

A simple but powerful secure password generator. You get the option of password length (10 to 20 characters) and whether you include numbers, symbols, uppercase and/or lowercase letters. Simply tap the lock icon to generate a secure password and then tap to copy the password.

Mykel Agathos 1 Feb 16, 2022
CCCryptor (AES encryption) wrappers for iOS and Mac in Swift. -- For ObjC, see RNCryptor/RNCryptor-objc

RNCryptor Cross-language AES Encryptor/Decryptor data format. The primary targets are Swift and Objective-C, but implementations are available in C, C

null 3.3k Dec 30, 2022
CryptoSwift is a growing collection of standard and secure cryptographic algorithms implemented in Swift

CryptoSwift Crypto related functions and helpers for Swift implemented in Swift. (#PureSwift) Note: The master branch follows the latest currently rel

Marcin Krzyzanowski 9.4k Jan 5, 2023
Simple and secure hashing in Swift with the SipHash algorithm

SipHash ⚠️ WARNING This package has been obsoleted by the Hasher type and the Hashable.hash(into:) requirement introduced in Swift 4.2. Using this pac

null 262 Dec 19, 2022
PassDrop is a fully-featured secure password management system, compatible with the free KeePass 1.x (Classic) and multi-platform KeePassX desktop applications.

passdrop This is a modern, updated build of Rudis Muiznieks's PassDrop application. PassDrop is a fully-featured secure password management system, co

Chad Austin 33 Sep 23, 2022
Simple, secure password and data management for individuals and teams

Padloc Simple, secure password and data management for individuals and teams (formerly known as Padlock). This repo is split into multiple packages: P

Padloc 2.1k Jan 8, 2023
The minimalistic, secure and open-source two-factor authentication app.

Einmal /ˈainmaːl/ German: once The minimalistic, secure and open-source two-factor authentication app. Features ♻️ Cross-platform — available on Andro

Incipher 75 Aug 21, 2022
PGPro can encrypt and decrypt messages as well as manage all your OpenPGP keys. It is free, simple and lightweight. Everything stays on your device. PGPro is made in Switzerland.

PGPro can encrypt and decrypt messages as well as manage all your OpenPGP keys. It is free, simple and lightweight. Everything stays on your device. P

Luca Näf 250 Jan 4, 2023
Use Apple FaceID or TouchID authentication in your app using BiometricAuthentication.

BiometricAuthentication Use Apple FaceID or TouchID authentication in your app using BiometricAuthentication. It's very simple and easy to use that ha

Rushi Sangani 804 Dec 30, 2022
A simple wrapper for the iOS Keychain to allow you to use it in a similar fashion to User Defaults. Written in Swift.

SwiftKeychainWrapper A simple wrapper for the iOS / tvOS Keychain to allow you to use it in a similar fashion to User Defaults. Written in Swift. Prov

Jason 1.5k Dec 30, 2022
Valet lets you securely store data in the iOS, tvOS, or macOS Keychain without knowing a thing about how the Keychain works.

Valet Valet lets you securely store data in the iOS, tvOS, watchOS, or macOS Keychain without knowing a thing about how the Keychain works. It’s easy.

Square 3.8k Jan 4, 2023
RSA public/private key encryption, private key signing and public key verification in Swift using the Swift Package Manager. Works on iOS, macOS, and Linux (work in progress).

BlueRSA Swift cross-platform RSA wrapper library for RSA encryption and signing. Works on supported Apple platforms (using Security framework). Linux

Kitura 122 Dec 16, 2022
RSA public/private key encryption, private key signing and public key verification in Swift using the Swift Package Manager. Works on iOS, macOS, and Linux (work in progress).

BlueRSA Swift cross-platform RSA wrapper library for RSA encryption and signing. Works on supported Apple platforms (using Security framework). Linux

Kitura 122 Dec 16, 2022
Obfuscate your strings in Swift easily

TPObfuscatedString TPObfuscatedString is a simple extension for String in Swift. It allows you to obfuscate hardcoded Strings in your compiled binary.

techprimate 18 Jan 9, 2020
Swift cross-platform crypto library using CommonCrypto/libcrypto

BlueCryptor Swift cross-platform crypto library derived from IDZSwiftCommonCrypto. IMPORTANT NOTE: This release is NOT entirely source code compatible

Kitura 183 Oct 15, 2022
Swift cross-platform crypto library using CommonCrypto/libcrypto

BlueCryptor Swift cross-platform crypto library derived from IDZSwiftCommonCrypto. IMPORTANT NOTE: This release is NOT entirely source code compatible

Kitura 183 Oct 15, 2022
A tiny and easy to use Swift class to encrypt strings using HMAC algorithms.

#Sweet HMAC SweetHMAC is a tiny and easy to use Swift class to encrypt strings using HMAC algorithms. A special thanks to jernejstrasner for shared HM

Jan Cássio 37 Jul 27, 2022
a bunch of random programs using Swift

Random-SwiftStuff (WORK IN PROGRESS) a bunch of random programs using Swift: apps mini programs calculators password generators random number generato

Kaival Shah 2 Oct 16, 2021
The IDAGIO WatchOS app using swift

IDAGIORedesignWatchOS I redesigned the IDAGIO WatchOS app as an exercise Old App

Francesco Junior Iaccarino 0 Dec 23, 2021