Skip to content

Commit

Permalink
Merge pull request #8 from lemo-nade-room/feature/on-lost-key-default…
Browse files Browse the repository at this point in the history
…-value

鍵を紛失した場合のデフォルト値を設定できるよう変更
  • Loading branch information
lemo-nade-room authored Feb 1, 2025
2 parents d9c1273 + 87b72d8 commit 6f4fc81
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 58 deletions.
22 changes: 11 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,11 @@
## サポート

- macOS >= 13
- 6.0 > Swift >= 5.10
- Swift >= 6.0

## 特徴
- AES-GCMでJSONプロパティを暗号化・復号
- 暗号化対象のプロパティは`Sendable``Codable`、および`Hashable`に準拠
- 暗号化対象プロパティは`Optional`型である必要がある
- 暗号化対象のプロパティは`Sendable``CryptoFieldable`に準拠している必要がある

## インストール

Expand Down Expand Up @@ -67,12 +66,13 @@ import Foundation

struct Event: Hashable, Codable, Sendable {
var id: UUID
@CryptoField var 個人情報: Self.個人情報?
struct 個人情報: Hashable, Codable, Sendable {
var 氏名: String
var 誕生日: Date
var 年齢: Int
}
var 職業: String
@CryptoField var 氏名: String
@CryptoField var LINEやってる: Bool
@CryptoField var 誕生日: Date
@CryptoField var 年齢: Int
@CryptoField var 身長: Double
@CryptoField var 体重: Double?
}
```

Expand Down Expand Up @@ -103,8 +103,8 @@ let event: Event = try CryptoConfigContainer.$key.withValue(key) {
```

## 暗号化可能なプロパティの条件
- プロパティの型は`Sendable``Codable``Hashable`に準拠している必要があります。
- プロパティは`Optional`型である必要があります。
- プロパティの型は`Sendable``CryptoFieldable`に準拠している必要があります。
- Int, String, Double, Bool, Date, Optional型はデフォルトで準拠している

## 暗号鍵が設定されていない場合
暗号鍵が設定されていない場合、プロパティには`nil`が設定されますが、デコード自体は成功します。
Expand Down
2 changes: 1 addition & 1 deletion Sources/CryptoCodable/CryptoConfigContainer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Foundation

/// 暗号鍵等の設定を保持するための名前空間
public struct CryptoConfigContainer: Sendable {
/// CryptoFieldのエンコード/デコード時に使用する暗号鍵
/// ``CryptoField``のエンコード/デコード時に使用する暗号鍵
///
/// 暗号化・復号時に事前にセットする必要がある
///
Expand Down
34 changes: 18 additions & 16 deletions Sources/CryptoCodable/CryptoField.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,21 @@ import Foundation
///
/// ## 使用方法
///
/// 1. CryptoFieldプロパティラッパーを使ったCodableな型を定義する
/// 1. ``CryptoField``プロパティラッパーを使った``Codable``な型を定義する
///
/// ```swift
/// import CryptoCodable
/// import Foundation
///
/// struct Event: Hashable, Codable, Sendable {
/// var id: UUID
/// @CryptoField var 個人情報: Self.個人情報?
/// struct 個人情報: Hashable, Codable, Sendable {
/// var 氏名: String
/// var 誕生日: Date
/// var 年齢: Int
/// }
/// var id: UUID
/// var 職業: String
/// @CryptoField var 氏名: String
/// @CryptoField var LINEやってる: Bool
/// @CryptoField var 誕生日: Date
/// @CryptoField var 年齢: Int
/// @CryptoField var 身長: Double
/// @CryptoField var 体重: Double?
/// }
/// ```
///
Expand Down Expand Up @@ -48,20 +49,21 @@ import Foundation
///
/// ## 暗号化可能なプロパティの条件
///
/// - プロパティの型はSendable, Codable, Hashbleに準拠している
/// - プロパティはOptional型である
/// - プロパティの型は``Sendable``, ``CryptoFieldable``に準拠している
/// - Int, String, Double, Bool, Date, Optional型はデフォルトで``CryptoFieldable``準拠している
///
/// ## 暗号鍵が設定されていない場合
///
/// 暗号鍵が存在しない場合、プロパティにnilが設定され、デコード自体は成功します。
/// 暗号鍵が存在しない場合、プロパティにonLostKeyValue値が設定され、デコード自体は成功します。
///
/// - throws: `DecryptFailure` 暗号鍵が異なる場合
/// - throws: ``DecryptFailure`` 暗号鍵が異なる場合
///
@propertyWrapper
public struct CryptoField<T>: Codable, Sendable, Hashable where T: Sendable & Codable & Hashable {
public var wrappedValue: T?
public struct CryptoField<T>: Codable, Sendable, Hashable
where T: Sendable & Codable & Hashable & CryptoFieldable {
public var wrappedValue: T

public init(wrappedValue: T?) {
public init(wrappedValue: T) {
self.wrappedValue = wrappedValue
}

Expand All @@ -72,7 +74,7 @@ public struct CryptoField<T>: Codable, Sendable, Hashable where T: Sendable & Co

public init(from decoder: any Decoder) throws {
guard let key = CryptoConfigContainer.key else {
wrappedValue = nil
wrappedValue = T.onLostKeyValue
return
}
let container = try decoder.singleValueContainer()
Expand Down
30 changes: 30 additions & 0 deletions Sources/CryptoCodable/CryptoFieldable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import Foundation

/// 鍵が無い場合のデフォルト値を持つことができるプロトコル
public protocol CryptoFieldable: Hashable, Codable {
static var onLostKeyValue: Self { get }
}
extension Int: CryptoFieldable {
/// 鍵が紛失した場合のデフォルト値
@TaskLocal public static var onLostKeyValue = 1
}
extension String: CryptoFieldable {
/// 鍵が紛失した場合のデフォルト値
@TaskLocal public static var onLostKeyValue = "Lost"
}
extension Double: CryptoFieldable {
/// 鍵が紛失した場合のデフォルト値
@TaskLocal public static var onLostKeyValue = 1.0
}
extension Bool: CryptoFieldable {
/// 鍵が紛失した場合のデフォルト値
@TaskLocal public static var onLostKeyValue = false
}
extension Date: CryptoFieldable {
/// 鍵が紛失した場合のデフォルト値
@TaskLocal public static var onLostKeyValue = Date(timeIntervalSince1970: 0)
}
extension Optional: CryptoFieldable where Wrapped: Hashable & Codable {
/// 鍵が紛失した場合のデフォルト値
public static var onLostKeyValue: Self { nil }
}
4 changes: 2 additions & 2 deletions Sources/CryptoCodable/SymmetricKey+data.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Crypto
import Foundation

extension SymmetricKey {
/// `SymmetricKey`を`Data`に変換する
/// ``SymmetricKey``を``Data``に変換する
///
/// この`Data`を保存することで後から再度デコード・復号することが可能
///
Expand All @@ -14,7 +14,7 @@ extension SymmetricKey {
/// let data = key.data // このデータを永続化する
/// ```
///
/// dataは標準イニシャライザで`SymmetricKey`に戻すことができる
/// dataは標準イニシャライザで``SymmetricKey``に戻すことができる
///
/// ```swift
/// import Crypto
Expand Down
65 changes: 37 additions & 28 deletions Tests/CryptoCodableTests/CryptoFieldTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,25 @@ import Testing
struct Event: Hashable, Codable, Sendable {
var id: UUID
var 職業: String
@CryptoField var 個人情報: Self.個人情報?
struct 個人情報: Hashable, Codable, Sendable {
var 氏名: String
var 誕生日: Date
var 年齢: Int
}
@CryptoField var 氏名: String
@CryptoField var LINEやってる: Bool
@CryptoField var 誕生日: Date
@CryptoField var 年齢: Int
@CryptoField var 身長: Double
@CryptoField var 体重: Double?
}

@Test func 暗号化・復号できる() throws {
// Arrange
let event = Event(
id: UUID(),
職業: "暗号専門家",
個人情報: .init(
氏名: "アリス",
誕生日: Date(timeIntervalSince1970: 54),
年齢: 777
)
氏名: "佐藤",
LINEやってる: true,
誕生日: ISO8601DateFormatter().date(from: "2001-06-01T00:00:00Z")!,
年齢: 24,
身長: 168.3,
体重: 32.5
)

try CryptoConfigContainer.$key.withValue(.init(size: .bits256)) {
Expand All @@ -37,16 +38,17 @@ import Testing
}
}

@Test func 鍵が存在しない場合にデコードするとnilが入る() throws {
@Test func 鍵が存在しない場合にデコードするとonLostKeyValue値が入る() throws {
// Arrange
let event = Event(
id: UUID(uuidString: "C09B74E3-1BEF-4F34-994C-FAE04390FBA8")!,
id: UUID(),
職業: "暗号専門家",
個人情報: .init(
氏名: "アリス",
誕生日: Date(timeIntervalSince1970: 54),
年齢: 777
)
氏名: "佐藤",
LINEやってる: true,
誕生日: ISO8601DateFormatter().date(from: "2001-06-01T00:00:00Z")!,
年齢: 24,
身長: 168.3,
体重: 32.5
)

let encrypted = try CryptoConfigContainer.$key.withValue(.init(size: .bits256)) {
Expand All @@ -59,23 +61,30 @@ import Testing
// Assert
#expect(
decrypted
== .init(
id: UUID(uuidString: "C09B74E3-1BEF-4F34-994C-FAE04390FBA8")!,
== Event(
id: event.id,
職業: "暗号専門家",
個人情報: nil
))
氏名: "Lost",
LINEやってる: false,
誕生日: Date(timeIntervalSince1970: 0),
年齢: 1,
身長: 1,
体重: nil
)
)
}

@Test func 異なる鍵でデコードするとエラーが投げられる() throws {
// Arrange
let event = Event(
id: UUID(uuidString: "C09B74E3-1BEF-4F34-994C-FAE04390FBA8")!,
id: UUID(),
職業: "暗号専門家",
個人情報: .init(
氏名: "アリス",
誕生日: Date(timeIntervalSince1970: 54),
年齢: 777
)
氏名: "佐藤",
LINEやってる: true,
誕生日: ISO8601DateFormatter().date(from: "2001-06-01T00:00:00Z")!,
年齢: 24,
身長: 168.3,
体重: 32.5
)

let encrypted = try CryptoConfigContainer.$key.withValue(.init(size: .bits256)) {
Expand Down

0 comments on commit 6f4fc81

Please sign in to comment.