Skip to content

Commit

Permalink
Support Bluetooth (#118)
Browse files Browse the repository at this point in the history
Signed-off-by: conanoc <conanoc@gmail.com>
  • Loading branch information
conanoc authored Jul 9, 2024
1 parent 101cac6 commit ad20d84
Show file tree
Hide file tree
Showing 24 changed files with 461 additions and 20 deletions.
27 changes: 27 additions & 0 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@
"version" : "3.1.0"
}
},
{
"identity" : "blueswift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/conanoc/BlueSwift",
"state" : {
"revision" : "5c9cc238396061bf7f3d6056dc8fdfe060a1add3",
"version" : "1.1.7"
}
},
{
"identity" : "cocoaasyncsocket",
"kind" : "remoteSourceControl",
Expand Down Expand Up @@ -99,6 +108,15 @@
"version" : "1.2.2"
}
},
{
"identity" : "swift-algorithms",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-algorithms",
"state" : {
"revision" : "f6919dfc309e7f1b56224378b11e28bab5bccc42",
"version" : "1.2.0"
}
},
{
"identity" : "swift-bases",
"kind" : "remoteSourceControl",
Expand All @@ -116,6 +134,15 @@
"revision" : "45f3cf2844477b9d211e1d3e793d0853134fd942",
"version" : "0.0.2"
}
},
{
"identity" : "swift-numerics",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-numerics.git",
"state" : {
"revision" : "0a5bc04095a675662cf24757cc0640aa2204253b",
"version" : "1.0.2"
}
}
],
"version" : 2
Expand Down
8 changes: 6 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ let package = Package(
.package(url: "https://github.com/keefertaylor/Base58Swift", exact: "2.1.7"),
.package(url: "https://github.com/thecatalinstan/Criollo", exact: "1.1.0"),
.package(url: "https://github.com/groue/Semaphore", exact: "0.0.8"),
.package(url: "https://github.com/beatt83/peerdid-swift", exact: "3.0.0")
.package(url: "https://github.com/beatt83/peerdid-swift", exact: "3.0.0"),
.package(url: "https://github.com/apple/swift-algorithms", exact: "1.2.0"),
.package(url: "https://github.com/conanoc/BlueSwift", exact: "1.1.7")
],
targets: [
.target(
Expand All @@ -30,9 +32,11 @@ let package = Package(
.product(name: "IndyVdr", package: "aries-uniffi-wrappers"),
.product(name: "WebSockets", package: "concurrent-ws"),
.product(name: "PeerDID", package: "peerdid-swift"),
.product(name: "Algorithms", package: "swift-algorithms"),
"CollectionConcurrencyKit",
"Base58Swift",
"Semaphore"
"Semaphore",
"BlueSwift"
]),
.testTarget(
name: "AriesFrameworkTests",
Expand Down
25 changes: 23 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Aries Framework Swift supports most of [AIP 1.0](https://github.com/hyperledger/
- ✅ ([RFC 0036](https://github.com/hyperledger/aries-rfcs/blob/master/features/0036-issue-credential/README.md)) Issue Credential Protocol
- ✅ ([RFC 0037](https://github.com/hyperledger/aries-rfcs/tree/master/features/0037-present-proof/README.md)) Present Proof Protocol
- Does not implement alternate begining (Prover begins with proposal)
- ✅ HTTP & WebSocket Transport
- ✅ HTTP, WebSocket and Bluetooth Transport
- ✅ ([RFC 0434](https://github.com/hyperledger/aries-rfcs/blob/main/features/0434-outofband/README.md)) Out of Band Protocol (AIP 2.0)
- ✅ ([RFC 0035](https://github.com/hyperledger/aries-rfcs/blob/main/features/0035-report-problem/README.md)) Report Problem Protocol
- ✅ ([RFC 0023](https://github.com/hyperledger/aries-rfcs/tree/main/features/0023-did-exchange)) DID Exchange Protocol (AIP 2.0)
Expand All @@ -28,7 +28,7 @@ Aries Framework Swift requires iOS 15.0+ and distributed as a Swift package.
Add a dependency to your `Package.swift` file:
```swift
dependencies: [
.package(url: "https://github.com/hyperledger/aries-framework-swift", from: "2.3.0")
.package(url: "https://github.com/hyperledger/aries-framework-swift", from: "2.5.0")
]
```

Expand Down Expand Up @@ -140,6 +140,27 @@ Another way to handle those requests is to implement your own `MessageHandler` c
agent.dispatcher.registerHandler(handler: messageHandler)
```

## Bluetooth support

Aries Framework Swift supports phone to phone communication over Bluetooth.
You will need to add `NSBluetoothAlwaysUsageDescription` key to the info.plist of your app to use Bluetooth.

### How to use

Verifier side:
1. Call `try await agent.startBLE()` to create an endpoint over BLE. The endpoint has the form of "ble://aries/endpoint?uuid={uuid}".
2. Create an oob-invitation and create a QR code with the invitation url. This invitation will use the endpoint created above even though the agent has a mediator connection. You should create an oob-invitation attaching a proof request message without handshake option. This allows the prover sends the proof directly to the verifier without preparing any endpoint.
```swift
let oob = try await agent!.oob.createInvitation(config: CreateOutOfBandInvitationConfig(handshake: false, messages: [message]))
let invitationUrl = oob.outOfBandInvitation.toUrl(domain: "http://example.com")
```
3. Call `try? await agent.stopBLE()` after you finish verification.

Prover side:
- There is nothing you need to do to communicate over BLE on prover side. The agent will recognize the `ble://` scheme and connect to the verifier's device over BLE. The connection will be closed automatically after the message is sent.

The sample app has sample codes that demonstrates proof exchange over Bluetooth.

## Sample App

`Sample` directory contains an iOS sample app that demonstrates how to use Aries Framework Swift. The app receives a connection invitation from a QR code or from a URL input and handles credential offers and proof requests.
Expand Down
18 changes: 12 additions & 6 deletions Sample/wallet-app-ios.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
BB90F26828F92DA20066AE87 /* local-genesis.txn in Resources */ = {isa = PBXBuildFile; fileRef = BB90F26628F92DA10066AE87 /* local-genesis.txn */; };
BBB509D628ED680700A6405A /* WalletOpener.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBB509D528ED680700A6405A /* WalletOpener.swift */; };
BBB509D828ED682000A6405A /* OpenWalletView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBB509D728ED681F00A6405A /* OpenWalletView.swift */; };
BBB9FD232C2D4F1B008FD38D /* RequestProofView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBB9FD222C2D4F1B008FD38D /* RequestProofView.swift */; };
BBD7B25127181C4300C3FB6C /* QRCodeHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBD7B25027181C4300C3FB6C /* QRCodeHandler.swift */; };
/* End PBXBuildFile section */

Expand All @@ -37,6 +38,7 @@
BB90F26628F92DA10066AE87 /* local-genesis.txn */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "local-genesis.txn"; sourceTree = "<group>"; };
BBB509D528ED680700A6405A /* WalletOpener.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletOpener.swift; sourceTree = "<group>"; };
BBB509D728ED681F00A6405A /* OpenWalletView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenWalletView.swift; sourceTree = "<group>"; };
BBB9FD222C2D4F1B008FD38D /* RequestProofView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestProofView.swift; sourceTree = "<group>"; };
BBD7B25027181C4300C3FB6C /* QRCodeHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeHandler.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

Expand Down Expand Up @@ -94,6 +96,7 @@
BB5C4680270C1501008D77AE /* WalletMainView.swift */,
BBB509D528ED680700A6405A /* WalletOpener.swift */,
BBB509D728ED681F00A6405A /* OpenWalletView.swift */,
BBB9FD222C2D4F1B008FD38D /* RequestProofView.swift */,
BB5C468C270C390E008D77AE /* CredentialListView.swift */,
BB5C468E270C4181008D77AE /* CredentialDetailView.swift */,
BBD7B25027181C4300C3FB6C /* QRCodeHandler.swift */,
Expand Down Expand Up @@ -202,6 +205,7 @@
files = (
BBB509D828ED682000A6405A /* OpenWalletView.swift in Sources */,
BBD7B25127181C4300C3FB6C /* QRCodeHandler.swift in Sources */,
BBB9FD232C2D4F1B008FD38D /* RequestProofView.swift in Sources */,
BB5C468D270C390E008D77AE /* CredentialListView.swift in Sources */,
BB5C4681270C1501008D77AE /* WalletMainView.swift in Sources */,
BB5C468F270C4181008D77AE /* CredentialDetailView.swift in Sources */,
Expand Down Expand Up @@ -336,13 +340,14 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Manual;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"wallet-app-ios/Preview Content\"";
DEVELOPMENT_TEAM = "";
DEVELOPMENT_TEAM = RAZ4DT76QQ;
ENABLE_BITCODE = NO;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = "Need bluetooth for proof exchange";
INFOPLIST_KEY_NSCameraUsageDescription = "Use camera to scan QR Code";
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
Expand All @@ -356,7 +361,7 @@
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = "org.hyperledger.aries.demo.wallet-app-ios";
PRODUCT_BUNDLE_IDENTIFIER = "org.hyperledger.aries.demo.wallet-app-ios2";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_EMIT_LOC_STRINGS = YES;
Expand All @@ -371,13 +376,14 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Manual;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"wallet-app-ios/Preview Content\"";
DEVELOPMENT_TEAM = "";
DEVELOPMENT_TEAM = RAZ4DT76QQ;
ENABLE_BITCODE = NO;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = "Need bluetooth for proof exchange";
INFOPLIST_KEY_NSCameraUsageDescription = "Use camera to scan QR Code";
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
Expand All @@ -391,7 +397,7 @@
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = "org.hyperledger.aries.demo.wallet-app-ios";
PRODUCT_BUNDLE_IDENTIFIER = "org.hyperledger.aries.demo.wallet-app-ios2";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_EMIT_LOC_STRINGS = YES;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/hyperledger/aries-uniffi-wrappers",
"state" : {
"revision" : "f335e392d6c5b8bd4dfa65746f214b80cdc60551",
"version" : "0.2.0"
"revision" : "01def706b44a4095032cd42f0480a5bdfb809961",
"version" : "0.2.1"
}
},
{
Expand All @@ -27,6 +27,15 @@
"version" : "3.1.0"
}
},
{
"identity" : "blueswift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/conanoc/BlueSwift",
"state" : {
"revision" : "5c9cc238396061bf7f3d6056dc8fdfe060a1add3",
"version" : "1.1.7"
}
},
{
"identity" : "cocoaasyncsocket",
"kind" : "remoteSourceControl",
Expand Down Expand Up @@ -72,6 +81,24 @@
"version" : "1.1.0"
}
},
{
"identity" : "didcore-swift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/beatt83/didcore-swift.git",
"state" : {
"revision" : "2503b1690e11b16a0cdc8f492e370bbd9dcbe08b",
"version" : "2.0.0"
}
},
{
"identity" : "peerdid-swift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/beatt83/peerdid-swift",
"state" : {
"revision" : "4e82ff42aa2b53b2d361f482b607763d79ccfdbb",
"version" : "3.0.0"
}
},
{
"identity" : "semaphore",
"kind" : "remoteSourceControl",
Expand All @@ -89,6 +116,42 @@
"revision" : "e325083424688055363bbfcb7f1a440d7d7a1bae",
"version" : "1.2.2"
}
},
{
"identity" : "swift-algorithms",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-algorithms",
"state" : {
"revision" : "f6919dfc309e7f1b56224378b11e28bab5bccc42",
"version" : "1.2.0"
}
},
{
"identity" : "swift-bases",
"kind" : "remoteSourceControl",
"location" : "https://github.com/swift-libp2p/swift-bases.git",
"state" : {
"revision" : "3cf27cf95d70248b0a1d99eee06cdf8b235241a8",
"version" : "0.0.3"
}
},
{
"identity" : "swift-multibase",
"kind" : "remoteSourceControl",
"location" : "https://github.com/swift-libp2p/swift-multibase.git",
"state" : {
"revision" : "45f3cf2844477b9d211e1d3e793d0853134fd942",
"version" : "0.0.2"
}
},
{
"identity" : "swift-numerics",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-numerics.git",
"state" : {
"revision" : "0a5bc04095a675662cf24757cc0640aa2204253b",
"version" : "1.0.2"
}
}
],
"version" : 2
Expand Down
19 changes: 18 additions & 1 deletion Sample/wallet-app-ios/CredentialHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ extension CredentialHandler: AgentDelegate {
} else if proofRecord.state == .Done {
menu = nil
showSimpleAlert(message: "Proof done")
} else if proofRecord.state == .PresentationReceived {
menu = nil
showSimpleAlert(message: "Proof.isVerified: \(proofRecord.isVerified!)")
} else if proofRecord.state == .PresentationSent {
menu = nil
showSimpleAlert(message: "Proof sent")
}
}
}
Expand Down Expand Up @@ -88,7 +94,7 @@ extension CredentialHandler: AgentDelegate {
_ = try await agent!.proofs.acceptRequest(proofRecordId: proofRecordId, requestedCredentials: requestedCredentials)
} catch {
menu = nil
showSimpleAlert(message: "Failed to present proof")
showSimpleAlert(message: "Failed to present proof: \(error)")
print(error)
}
}
Expand All @@ -110,4 +116,15 @@ extension CredentialHandler: AgentDelegate {
self?.showAlert = true
}
}

func createProofInvitation() async throws -> String {
let attributes = ["attrbutes1": ProofAttributeInfo(names: ["name", "degree"])]
let nonce = try ProofService.generateProofRequestNonce()
let proofRequest = ProofRequest(nonce: nonce, requestedAttributes: attributes, requestedPredicates: [:])
let (message, _) = try await agent!.proofService.createRequest(proofRequest: proofRequest)
let outOfBandRecord = try await agent!.oob.createInvitation(
config: CreateOutOfBandInvitationConfig(handshake: false, messages: [message]))
let invitation = outOfBandRecord.outOfBandInvitation
return try invitation.toUrl(domain: "http://example.com")
}
}
2 changes: 1 addition & 1 deletion Sample/wallet-app-ios/QRCodeHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class QRCodeHandler {
Task {
do {
let (_, connection) = try await agent!.oob.receiveInvitationFromUrl(url)
await credentialHandler.showSimpleAlert(message: "Connected with \(connection?.theirLabel ?? "unknown agent")")
print("Connected with \(connection?.theirLabel ?? "unknown agent")")
} catch {
print(error)
await credentialHandler.reportError()
Expand Down
Loading

0 comments on commit ad20d84

Please sign in to comment.