Skip to content

Commit

Permalink
🧹 Make code Swiftier
Browse files Browse the repository at this point in the history
  • Loading branch information
RemiBardon committed Sep 15, 2021
1 parent d90c41b commit 14af663
Show file tree
Hide file tree
Showing 34 changed files with 964 additions and 736 deletions.
66 changes: 66 additions & 0 deletions .swiftpm/xcode/xcshareddata/xcschemes/Biscuit.xcscheme
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,62 @@
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "BiscuitDatalog"
BuildableName = "BiscuitDatalog"
BlueprintName = "BiscuitDatalog"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "BiscuitFormat"
BuildableName = "BiscuitFormat"
BlueprintName = "BiscuitFormat"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "BiscuitShared"
BuildableName = "BiscuitShared"
BlueprintName = "BiscuitShared"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "BiscuitToken"
BuildableName = "BiscuitToken"
BlueprintName = "BiscuitToken"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
Expand Down Expand Up @@ -124,6 +180,16 @@
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "BiscuitDatalogTests"
BuildableName = "BiscuitDatalogTests"
BlueprintName = "BiscuitDatalogTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
Expand Down
14 changes: 9 additions & 5 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ let package = Package(
],
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(name: "Biscuit", targets: ["Datalog", "BiscuitCrypto", "Format"]),
.library(name: "Biscuit", targets: ["BiscuitDatalog", "BiscuitCrypto", "BiscuitFormat"]),
],
dependencies: [
.package(url: "https://github.com/apple/swift-collections", .upToNextMinor(from: "0.0.2")),
Expand All @@ -27,24 +27,28 @@ let package = Package(
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(name: "BiscuitShared"),
.target(
name: "Datalog",
name: "BiscuitDatalog",
dependencies: [
.target(name: "BiscuitShared"),
.product(name: "OrderedCollections", package: "swift-collections"),
]
),
.testTarget(name: "DatalogTests", dependencies: ["Datalog"]),
.testTarget(name: "BiscuitDatalogTests", dependencies: ["BiscuitDatalog"]),
.target(
name: "BiscuitCrypto",
dependencies: [
.target(name: "BiscuitShared"),
.product(name: "Crypto", package: "swift-crypto"),
]
),
.testTarget(name: "BiscuitCryptoTests", dependencies: ["BiscuitCrypto"]),
.target(
name: "Format",
name: "BiscuitFormat",
dependencies: [
.target(name: "Datalog"),
.target(name: "BiscuitShared"),
.target(name: "BiscuitDatalog"),
.target(name: "BiscuitCrypto"),
.product(name: "SwiftProtobuf", package: "swift-protobuf"),
.product(name: "OrderedCollections", package: "swift-collections"),
Expand Down
83 changes: 40 additions & 43 deletions Sources/BiscuitCrypto/BiscuitCrypto.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//
// BiscuitCrypto.swift
// BiscuitCrypto
// Biscuit
//
// Created by Rémi Bardon on 09/09/2021.
//
Expand All @@ -26,11 +26,7 @@ public struct KeyPair {
public let privateKey: PrivateKey
public var publicKey: PublicKey { privateKey.publicKey }

public init() {
self.privateKey = .init()
}

public init(from privateKey: PrivateKey) {
public init(from privateKey: PrivateKey = PrivateKey()) {
self.privateKey = privateKey
}

Expand Down Expand Up @@ -63,6 +59,16 @@ public struct Block {
self.signature = signature
}

public func verifySignature(with publicKey: PublicKey) throws {
var toVerify = self.data
toVerify.append(self.nextKey.rawRepresentation)

// FIXME: Replace with SHA512 hashing
guard publicKey.isValidSignature(self.signature, for: toVerify) else {
throw CryptoError.signature(.invalidSignature("The block has not been signed with the correct key"))
}
}

}

public struct Token {
Expand All @@ -78,17 +84,15 @@ public struct Token {
self.next = next
}

public init(keyPair: KeyPair, nextKey: KeyPair, message: Data) throws {
let signature = try sign(keyPair: keyPair, nextKey: nextKey, message: message)
public init(with message: Data, signedBy keyPair: KeyPair, nextKey: KeyPair) throws {
let signature = try sign(message, with: keyPair, nextKey: nextKey)
let block = Block(data: message, nextKey: nextKey.publicKey, signature: signature)

self.init(root: keyPair.publicKey, blocks: [block], next: .secret(nextKey.privateKey))
}

public func append(nextKey: KeyPair, message: Data) throws -> Self {
let keyPair = try self.next.keyPair()

let signature = try sign(keyPair: keyPair, nextKey: nextKey, message: message)
public func append(_ message: Data, nextKey: KeyPair) throws -> Self {
let signature = try sign(message, with: self.next.keyPair(), nextKey: nextKey)
let block = Block(data: message, nextKey: nextKey.publicKey, signature: signature)

var newToken = Token(root: self.root, blocks: self.blocks, next: .secret(nextKey.privateKey))
Expand All @@ -97,32 +101,35 @@ public struct Token {
return newToken
}

public func verify(with root: PublicKey) throws {
// FIXME: Try batched signature verification
var currentPub = root

public func verify(with rootPublicKey: PublicKey) throws {
// Verify all blocks
for block in self.blocks {
try verifyBlockSignature(of: block, with: currentPub)
let lastPublicKey: PublicKey = try {
var currentPublicKey = rootPublicKey

currentPub = block.nextKey
}
// FIXME: Try batched signature verification
for block in self.blocks {
try block.verifySignature(with: currentPublicKey)
currentPublicKey = block.nextKey
}

return currentPublicKey
}()

switch self.next {
case let .secret(privateKey):
if currentPub.rawRepresentation != privateKey.publicKey.rawRepresentation {
throw FormatError.signature(.invalidSignature("The last public key does not match the private key"))
case .secret(let privateKey):
guard lastPublicKey.rawRepresentation == privateKey.publicKey.rawRepresentation else {
throw CryptoError.signature(.invalidSignature("The last public key does not match the private key"))
}
case let .seal(signature):
// FIXME: Replace with SHA512 hashing
case .seal(let signature):
var toVerify = Data()
for block in self.blocks {
toVerify.append(block.data)
toVerify.append(block.nextKey.rawRepresentation)
}

guard currentPub.isValidSignature(signature, for: toVerify) else {
throw FormatError.signature(.invalidSignature("Block signature is invalid"))
// FIXME: Replace with SHA512 hashing
guard lastPublicKey.isValidSignature(signature, for: toVerify) else {
throw CryptoError.signature(.invalidSignature("Block signature is invalid"))
}
}
}
Expand All @@ -136,36 +143,26 @@ public enum NextToken {
public func keyPair() throws -> KeyPair {
switch self {
case .seal:
throw TokenError.sealed
throw CryptoError.sealed
case let .secret(privateKey):
return KeyPair(from: privateKey)
}
}

}

public func sign(keyPair: KeyPair, nextKey: KeyPair, message: Data) throws -> Signature {
public func sign(_ message: Data, with keyPair: KeyPair, nextKey: KeyPair) throws -> Signature {
var toSign = message
toSign.append(nextKey.publicKey.rawRepresentation)

return try sign(keyPair: keyPair, message: toSign)
return try sign(toSign, with: keyPair)
}

public func sign(keyPair: KeyPair, message: Data) throws -> Signature {
public func sign(_ message: Data, with keyPair: KeyPair) throws -> Signature {
// FIXME: replace with SHA512 hashing
do {
return try keyPair.privateKey.signature(for: message)
} catch {
throw FormatError.signature(.invalidSignatureGeneration(error))
}
}

public func verifyBlockSignature(of block: Block, with publicKey: PublicKey) throws {
// FIXME: Replace with SHA512 hashing
var toVerify = block.data
toVerify.append(block.nextKey.rawRepresentation)

guard publicKey.isValidSignature(block.signature, for: toVerify) else {
throw FormatError.signature(.invalidSignature("The block has not been signed with the correct key"))
throw CryptoError.signature(.invalidSignatureGeneration(error))
}
}
54 changes: 54 additions & 0 deletions Sources/BiscuitCrypto/CryptoError.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//
// CryptoError.swift
// Biscuit
//
// Created by Rémi Bardon on 15/09/2021.
//

import Foundation
import BiscuitShared

extension BiscuitError {

internal static func crypto(_ error: CryptoError) -> Self {
return Self.error(prefix: "Error in the token's cryptographic signature", error: error)
}

}

/// Errors related to the token's cryptographic signature
public enum CryptoError: Error, CustomStringConvertible {

case signature(SignatureError)
case sealed

public var description: String {
switch self {
case .signature(let error):
return "Failed verifying the signature: \(error)"
case .sealed:
return "Tried to append a block to a sealed token"
}
}

}

/// Signature errors
public enum SignatureError: Error, CustomStringConvertible {

// case invalidFormat
case invalidSignature(String)
case invalidSignatureGeneration(Error)

public var description: String {
switch self {
// case .invalidFormat:
// return "Could not parse the signature elements"
case .invalidSignature(let error):
return "The signature did not match: \(error)"
case .invalidSignatureGeneration(let error):
return "Could not sign: \(error)"
}
}

}
Loading

0 comments on commit 14af663

Please sign in to comment.