Skip to content

Commit dd33b8c

Browse files
authored
Feat/hardware signing (#11)
* Add Secure Enclave hardware signing and test signing server
1 parent bbc0e4d commit dd33b8c

23 files changed

+1595
-462
lines changed

.github/workflows/test.yml

Lines changed: 103 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,68 @@ jobs:
4040
- name: Build iOS Framework
4141
run: make ios-framework
4242

43-
- name: Build & test
43+
- name: Setup Signing Server
4444
run: |
45-
set -eo pipefail
45+
echo "Setting up C2PA signing server..."
46+
make setup-server
47+
48+
- name: Build Signing Server
49+
run: |
50+
cd signing-server
51+
swift build
52+
53+
- name: Start Signing Server
54+
run: |
55+
cd signing-server
56+
DYLD_LIBRARY_PATH=libs:$DYLD_LIBRARY_PATH .build/debug/Run serve --env development --hostname 127.0.0.1 --port 8080 &
57+
echo $! > ../server.pid
58+
echo "Server started with PID $(cat ../server.pid)"
59+
60+
- name: Wait for Server to be Ready
61+
run: |
62+
echo "Waiting for signing server to be ready..."
63+
max_attempts=30
64+
attempt=0
65+
while [ $attempt -lt $max_attempts ]; do
66+
if curl -s http://127.0.0.1:8080/health > /dev/null 2>&1; then
67+
echo "✓ Signing server is ready"
68+
break
69+
fi
70+
echo "Waiting for server... (attempt $((attempt + 1))/$max_attempts)"
71+
sleep 2
72+
attempt=$((attempt + 1))
73+
done
74+
75+
if [ $attempt -eq $max_attempts ]; then
76+
echo "❌ Server failed to start after $max_attempts attempts"
77+
exit 1
78+
fi
79+
80+
- name: Verify Server Endpoints
81+
run: |
82+
echo "Testing server endpoints..."
83+
curl -v http://127.0.0.1:8080/health || echo "Health check failed"
84+
echo ""
85+
echo "Server is listening on:"
86+
lsof -i :8080 || echo "No process on port 8080"
87+
88+
- name: Clean Build Directory
89+
run: |
90+
cd example
91+
xcodebuild clean -project C2PAExample.xcodeproj -scheme "$SCHEME"
92+
93+
- name: Resolve Package Dependencies
94+
run: |
95+
cd example
96+
xcodebuild -resolvePackageDependencies -project C2PAExample.xcodeproj -scheme "$SCHEME"
97+
98+
- name: Build & Test
99+
run: |
100+
set -o pipefail
46101
cd example
47-
xcrun xcodebuild test \
102+
echo "CI environment variable: true"
103+
echo "Testing with server at http://127.0.0.1:8080"
104+
TEST_RUNNER_CI=true xcrun xcodebuild test \
48105
-project C2PAExample.xcodeproj \
49106
-scheme "$SCHEME" \
50107
-sdk iphonesimulator \
@@ -54,17 +111,36 @@ jobs:
54111
| xcpretty --test --color
55112
56113
- name: Generate test summary
114+
if: always()
57115
run: |
58116
cd example
59-
xcrun xcresulttool get test-results summary --path TestResults.xcresult || true
60-
xcrun xcresulttool get object --path TestResults.xcresult --format json > test-results.json || true
61-
xcrun xcresulttool get test-results tests --path TestResults.xcresult || true
117+
if [ -d TestResults.xcresult ]; then
118+
echo "=== Test Summary ==="
119+
xcrun xcresulttool get test-results summary --path TestResults.xcresult || true
120+
echo ""
121+
echo "=== Test Results ==="
122+
xcrun xcresulttool get test-results tests --path TestResults.xcresult || true
123+
echo ""
124+
echo "=== Exporting JSON ==="
125+
# Use the legacy flag as required by newer versions
126+
xcrun xcresulttool get --legacy --path TestResults.xcresult --format json > test-results.json 2>&1 || echo "Failed to export JSON"
127+
if [ -s test-results.json ]; then
128+
echo "Successfully exported test results to JSON ($(wc -c < test-results.json) bytes)"
129+
else
130+
echo "Warning: test-results.json is empty or not created"
131+
fi
132+
else
133+
echo "TestResults.xcresult not found"
134+
fi
62135
63136
- name: Upload test summary
137+
if: always()
64138
uses: actions/upload-artifact@v4
65139
with:
66140
name: TestSummary-${{ matrix.xcode }}-${{ matrix.device }}
67-
path: example/test-results.json
141+
path: |
142+
example/test-results.json
143+
example/TestResults.xcresult
68144
69145
- name: Export coverage LCOV
70146
if: success()
@@ -75,29 +151,13 @@ jobs:
75151
DERIVED_DATA=$(xcodebuild -showBuildSettings -project C2PAExample.xcodeproj -scheme C2PAExample -sdk iphonesimulator | grep -m 1 " BUILD_DIR " | sed 's/.*= //' | sed 's|/Build/Products||')
76152
echo "Derived data path: $DERIVED_DATA"
77153
78-
# Find Coverage.profdata
79-
PROFDATA_PATH=""
80-
if [ -n "$DERIVED_DATA" ]; then
81-
PROFDATA_PATH=$(find "$DERIVED_DATA/Build/ProfileData" -name "Coverage.profdata" -type f 2>/dev/null | head -1 || true)
82-
fi
83-
84-
# Method 2: If not found, look in common CI locations
85-
if [ -z "$PROFDATA_PATH" ]; then
86-
PROFDATA_PATH=$(find ~/Library/Developer/Xcode/DerivedData -path "*/Build/ProfileData/*/Coverage.profdata" -type f 2>/dev/null | grep -i c2paexample | head -1 || true)
87-
fi
154+
# Find Coverage.profdata - search in common locations
155+
PROFDATA_PATH=$(find ~/Library/Developer/Xcode/DerivedData -path "*/Build/ProfileData/*/Coverage.profdata" -type f 2>/dev/null | grep -i c2paexample | head -1 || true)
88156
89157
echo "Coverage.profdata path: $PROFDATA_PATH"
90158
91159
# Find the test binary
92-
TEST_BINARY_PATH=""
93-
if [ -n "$DERIVED_DATA" ]; then
94-
TEST_BINARY_PATH=$(find "$DERIVED_DATA/Build/Products" -path "*/C2PAExampleTests.xctest/C2PAExampleTests" -type f 2>/dev/null | head -1 || true)
95-
fi
96-
97-
# If not found, search more broadly
98-
if [ -z "$TEST_BINARY_PATH" ]; then
99-
TEST_BINARY_PATH=$(find ~/Library/Developer/Xcode/DerivedData -path "*/C2PAExampleTests.xctest/C2PAExampleTests" -type f 2>/dev/null | grep -i c2paexample | head -1 || true)
100-
fi
160+
TEST_BINARY_PATH=$(find ~/Library/Developer/Xcode/DerivedData -path "*/C2PAExampleTests.xctest/C2PAExampleTests" -type f 2>/dev/null | grep -i c2paexample | head -1 || true)
101161
102162
echo "Test binary path: $TEST_BINARY_PATH"
103163
@@ -135,3 +195,20 @@ jobs:
135195
with:
136196
token: ${{ secrets.CODECOV_TOKEN }}
137197
slug: contentauth/c2pa-ios
198+
199+
- name: Stop Signing Server
200+
if: always()
201+
run: |
202+
if [ -f server.pid ]; then
203+
SERVER_PID=$(cat server.pid)
204+
echo "Stopping signing server (PID: $SERVER_PID)..."
205+
kill $SERVER_PID || true
206+
rm server.pid
207+
fi
208+
209+
- name: Upload Server Logs
210+
if: failure()
211+
uses: actions/upload-artifact@v4
212+
with:
213+
name: server-logs-${{ matrix.xcode }}-${{ matrix.device }}
214+
path: signing-server/.build/debug/*.log

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,8 @@ Package.resolved
4242
# Editor temporary files
4343
*~
4444
*.swp
45-
*.swo
45+
*.swo
46+
47+
# Signing server
48+
signing-server/Resources/*
49+
signing-server/Sources/C2PA/*

Makefile

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: all clean setup download-binaries ios-framework
1+
.PHONY: all clean setup download-binaries ios-framework setup-server server clean-server
22

33
# GitHub Release Configuration
44
GITHUB_ORG := contentauth
@@ -56,6 +56,7 @@ download-binaries: setup
5656
# Patch the header file
5757
@echo "Patching c2pa.h header..."
5858
@sed 's/typedef struct C2paSigner C2paSigner;/typedef struct C2paSigner { } C2paSigner;/g' $(BUILD_DIR)/patched_headers/c2pa.h.orig > $(BUILD_DIR)/patched_headers/c2pa.h
59+
@rm -f $(BUILD_DIR)/patched_headers/c2pa.h.orig
5960

6061
@echo "Pre-built binaries downloaded successfully."
6162

@@ -110,8 +111,9 @@ define setup_swift_package
110111
@mkdir -p $(1)/Sources/C2PA
111112
@mkdir -p $(1)/Frameworks
112113

113-
# Copy Swift wrapper and patch c2pa.h
114+
# Copy Swift wrapper files
114115
@cp $(APPLE_DIR)/src/C2PA.swift $(1)/Sources/C2PA/
116+
@cp $(APPLE_DIR)/src/CertificateManager.swift $(1)/Sources/C2PA/
115117
@cp $(APPLE_DIR)/template/Package.swift $(1)/
116118

117119
# Copy XCFramework
@@ -136,13 +138,83 @@ clean:
136138
@rm -rf $(OUTPUT_DIR)
137139
@echo "Clean completed."
138140

141+
# Function to download and extract macOS library for server
142+
# Args: 1=architecture name for display, 2=release filename suffix, 3=target directory name
143+
define download_macos_library
144+
@echo "Downloading macOS $(1) library..."
145+
@mkdir -p $(DOWNLOAD_DIR)
146+
@curl -sL https://github.com/$(GITHUB_ORG)/c2pa-rs/releases/download/c2pa-$(C2PA_VERSION)/c2pa-$(C2PA_VERSION)-$(2).zip -o $(DOWNLOAD_DIR)/$(3).zip
147+
@unzip -q -o $(DOWNLOAD_DIR)/$(3).zip -d $(DOWNLOAD_DIR)/$(3)
148+
endef
149+
150+
# Server targets
151+
setup-server:
152+
@echo "Setting up C2PA signing server..."
153+
@command -v swift >/dev/null 2>&1 || { echo "Error: Swift is required but not installed."; exit 1; }
154+
155+
# Create server directories
156+
@mkdir -p signing-server/libs
157+
@mkdir -p signing-server/Sources/C2PA/include
158+
@mkdir -p signing-server/Resources
159+
160+
# Download universal macOS binary
161+
$(call download_macos_library,universal,universal-apple-darwin,macos-universal)
162+
163+
# Copy dylib to server
164+
@cp $(DOWNLOAD_DIR)/macos-universal/lib/libc2pa_c.dylib signing-server/libs/
165+
166+
# Get header file from macOS download
167+
@cp $(DOWNLOAD_DIR)/macos-universal/include/c2pa.h signing-server/Sources/C2PA/include/c2pa.h.orig
168+
169+
# Patch the header file
170+
@echo "Patching c2pa.h header for server..."
171+
@sed 's/typedef struct C2paSigner C2paSigner;/typedef struct C2paSigner { } C2paSigner;/g' signing-server/Sources/C2PA/include/c2pa.h.orig > signing-server/Sources/C2PA/include/c2pa.h
172+
@rm -f signing-server/Sources/C2PA/include/c2pa.h.orig
173+
174+
# Copy Swift files and module map
175+
@cp src/C2PA.swift signing-server/Sources/C2PA/
176+
@cp src/CertificateManager.swift signing-server/Sources/C2PA/
177+
@cp template/module.modulemap signing-server/Sources/C2PA/
178+
# Update header path in module map for server structure
179+
@sed -i '' 's|header "c2pa.h"|header "include/c2pa.h"|' signing-server/Sources/C2PA/module.modulemap
180+
181+
# Copy test certificates
182+
@cp example/C2PAExample/es256_certs.pem signing-server/Resources/
183+
@cp example/C2PAExample/es256_private.key signing-server/Resources/
184+
185+
@cd signing-server && swift package resolve
186+
@echo "Server setup complete!"
187+
188+
server: setup-server
189+
@echo "Building server..."
190+
@cd signing-server && swift build
191+
@echo "Starting C2PA signing server in development mode..."
192+
@cd signing-server && DYLD_LIBRARY_PATH=libs:$$DYLD_LIBRARY_PATH .build/debug/Run serve --env development --hostname 127.0.0.1 --port 8080
193+
194+
clean-server:
195+
@echo "Cleaning server build artifacts and copied files..."
196+
@cd signing-server && swift package clean
197+
@cd signing-server && rm -rf .build
198+
@rm -rf signing-server/libs
199+
@rm -rf signing-server/Sources/C2PA
200+
@rm -rf signing-server/Resources
201+
@echo "Server clean completed."
202+
139203
# Helper to show available targets
140204
help:
141205
@echo "Available targets:"
206+
@echo ""
207+
@echo "iOS Framework:"
142208
@echo " setup - Create necessary directories"
143209
@echo " download-binaries - Download pre-built binaries from GitHub releases"
144210
@echo " ios-dev - Build iOS library for arm64 simulator only (optimized for Apple Silicon)"
145211
@echo " ios-framework - Create iOS XCFramework"
146212
@echo " all - Build iOS framework (default)"
147213
@echo " clean - Remove build artifacts"
214+
@echo ""
215+
@echo "Test Server:"
216+
@echo " setup-server - Set up the C2PA signing server (downloads libs, copies files)"
217+
@echo " server - Build and run the signing server (port 8080)"
218+
@echo " clean-server - Clean server build artifacts"
219+
@echo ""
148220
@echo " help - Show this help message"

Package.swift

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,23 @@
1-
// swift-tools-version:5.7
1+
// swift-tools-version:5.9
22
import PackageDescription
33

44
let package = Package(
55
name: "C2PA",
66
platforms: [
7-
.iOS(.v15),
8-
.macOS(.v11)
7+
.iOS(.v16),
8+
.macOS(.v14),
99
],
1010
products: [
1111
.library(
1212
name: "C2PA",
13-
targets: ["C2PA"]),
13+
targets: ["C2PA"])
14+
],
15+
dependencies: [
16+
.package(
17+
url: "https://github.com/apple/swift-certificates.git", .upToNextMajor(from: "1.0.0")),
18+
.package(url: "https://github.com/apple/swift-asn1.git", .upToNextMajor(from: "1.0.0")),
19+
.package(url: "https://github.com/apple/swift-crypto.git", .upToNextMajor(from: "3.0.0")),
1420
],
15-
dependencies: [],
1621
targets: [
1722
.binaryTarget(
1823
name: "C2PAC",
@@ -21,7 +26,12 @@ let package = Package(
2126
),
2227
.target(
2328
name: "C2PA",
24-
dependencies: ["C2PAC"],
29+
dependencies: [
30+
"C2PAC",
31+
.product(name: "X509", package: "swift-certificates"),
32+
.product(name: "SwiftASN1", package: "swift-asn1"),
33+
.product(name: "Crypto", package: "swift-crypto"),
34+
],
2535
path: "src"
2636
),
2737
]

README.md

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ C2PA iOS offers:
2121

2222
- `/src` - Swift wrapper source code
2323
- `/template` - Swift package template
24-
- `/example` - Example iOS application
24+
- `/example` - Example iOS application
2525
- `/output` - Build output artifacts
2626
- `/build` - Temporary build files and external dependencies
2727
- `/Makefile` - Build system commands
@@ -153,7 +153,7 @@ let manifestData = try builder.sign(
153153
```bash
154154
# iOS framework (device + simulator)
155155
make ios-framework
156-
156+
157157
# For faster development on Apple Silicon Macs
158158
make ios-dev # Only builds for arm64 simulator
159159
```
@@ -163,7 +163,7 @@ let manifestData = try builder.sign(
163163
```bash
164164
# iOS Swift Package
165165
open output/C2PA-iOS
166-
166+
167167
# Example App
168168
open example
169169
```
@@ -198,6 +198,29 @@ The example iOS application in the `example` directory demonstrates comprehensiv
198198

199199
The app includes 19 comprehensive tests covering all major C2PA operations. Run the app and tap "Run All Tests" to see the library in action.
200200

201+
## Test Signing Server
202+
203+
For testing certificate enrollment and C2PA signing, a simple Swift-based signing server is included:
204+
205+
```bash
206+
# Start the test server
207+
make server
208+
```
209+
210+
The server runs on `http://localhost:8080` and provides:
211+
212+
- **Certificate Authority**: Signs Certificate Signing Requests (CSRs) for testing
213+
- **C2PA Signing**: Server-side C2PA manifest signing
214+
- **No Authentication**: Simplified for development/testing only
215+
216+
### Key Endpoints
217+
218+
- `GET /health` - Health check
219+
- `POST /api/v1/certificates/sign` - Sign a CSR
220+
- `POST /api/v1/c2pa/sign` - Sign image with C2PA manifest
221+
222+
**⚠️ Testing Only**: This server has no authentication and is intended for development and testing only. For production use, implement proper authentication and security measures.
223+
201224
## License
202225

203226
This project is licensed under the Apache License, Version 2.0 and MIT - see the LICENSE-APACHE and LICENSE-MIT files for details.

0 commit comments

Comments
 (0)