|
1 | | -[](https://app.bitrise.io/app/18c18db60fc4fddf) [](https://app.bitrise.io/app/f1bbbdfeff08cd5c) [](https://swiftpackageindex.com/GigaBitcoin/secp256k1.swift) [](https://swiftpackageindex.com/GigaBitcoin/secp256k1.swift) |
| 1 | +[](https://app.bitrise.io/app/18c18db60fc4fddf) [](https://swiftpackageindex.com/21-DOT-DEV/swift-secp256k1) [](https://swiftpackageindex.com/21-DOT-DEV/swift-secp256k1) |
2 | 2 |
|
3 | | -# 🔐 secp256k1.swift |
4 | | -Swift package with elliptic curve public key cryptography, ECDSA, Schnorr Signatures for Bitcoin and C bindings from [libsecp256k1](https://github.com/bitcoin-core/secp256k1). |
| 3 | +# 🔐 swift-secp256k1 |
5 | 4 |
|
| 5 | +Swift package for elliptic curve public key cryptography, ECDSA, and Schnorr Signatures for Bitcoin, with C bindings from [libsecp256k1](https://github.com/bitcoin-core/secp256k1). |
6 | 6 |
|
7 | | -# Objectives |
| 7 | +## Objectives |
8 | 8 |
|
9 | | -Long-term goals are: |
10 | | - - Lightweight ECDSA & Schnorr Signatures functionality |
11 | | - - Built for simple or advance usage with things like BIP340 |
12 | | - - Exposed C bindings to take full control of the secp256k1 implementation |
13 | | - - Familiar API design by modeling after [Swift Crypto](https://github.com/apple/swift-crypto) |
14 | | - - Automatic updates for Swift and libsecp256k1 |
15 | | - - Availability for Linux and Apple platform ecosystems |
| 9 | +- Provide lightweight ECDSA & Schnorr Signatures functionality |
| 10 | +- Support simple and advanced usage, including BIP-327 and BIP-340 |
| 11 | +- Expose C bindings for full control of the secp256k1 implementation |
| 12 | +- Offer a familiar API design inspired by [Swift Crypto](https://github.com/apple/swift-crypto) |
| 13 | +- Maintain automatic updates for Swift and libsecp256k1 |
| 14 | +- Ensure availability for Linux and Apple platform ecosystems |
16 | 15 |
|
| 16 | +## Installation |
17 | 17 |
|
18 | | -# Getting Started |
| 18 | +This package uses Swift Package Manager. To add it to your project: |
19 | 19 |
|
20 | | -This repository primarily uses Swift package manager as its build tool, so we recommend using that as well. Xcode comes with [built-in support](https://developer.apple.com/documentation/xcode/adding-package-dependencies-to-your-app) for Swift packages. From the menu bar, goto: `File > Add Packages...` If you manage packages via a `Package.swift` file, simply add `secp256k1.swift` as a dependencies' clause in your Swift manifest: |
| 20 | +### Using Xcode |
| 21 | + |
| 22 | +1. Go to `File > Add Packages...` |
| 23 | +2. Enter the package URL: `https://github.com/21-DOT-DEV/swift-secp256k1` |
| 24 | +3. Select the desired version |
| 25 | + |
| 26 | +### Using Package.swift |
| 27 | + |
| 28 | +Add the following to your `Package.swift` file: |
21 | 29 |
|
22 | 30 | ```swift |
23 | | -.package(name: "secp256k1.swift", url: "https://github.com/GigaBitcoin/secp256k1.swift.git", exact: "0.15.0"), |
| 31 | +.package(name: "swift-secp256k1", url: "https://github.com/21-DOT-DEV/swift-secp256k1", exact: "0.18.0"), |
24 | 32 | ``` |
25 | 33 |
|
26 | | -Include `secp256k1` as a dependency for your executable target: |
| 34 | +Then, include `secp256k1` as a dependency in your target: |
27 | 35 |
|
28 | 36 | ```swift |
29 | 37 | .target(name: "<target>", dependencies: [ |
30 | | - .product(name: "secp256k1", package: "secp256k1.swift") |
| 38 | + .product(name: "secp256k1", package: "swift-secp256k1") |
31 | 39 | ]), |
32 | 40 | ``` |
33 | 41 |
|
34 | | -Try in a [playground](spi-playgrounds://open?dependencies=GigaBitcoin/secp256k1.swift) using the [SPI Playgrounds app](https://swiftpackageindex.com/try-in-a-playground) or 🏟 [Arena](https://github.com/finestructure/arena) |
| 42 | +> [!WARNING] |
| 43 | +> These APIs are not considered stable and may change with any update. Specify a version using `exact:` to avoid breaking changes. |
35 | 44 |
|
36 | | -```swift |
37 | | -arena GigaBitcoin/secp256k1.swift |
38 | | -``` |
| 45 | +### Try it out |
39 | 46 |
|
| 47 | +Use [SPI Playgrounds app](https://swiftpackageindex.com/try-in-a-playground): |
40 | 48 |
|
41 | | -# Example Usage |
| 49 | +```swift |
| 50 | +arena 21-DOT-DEV/swift-secp256k1 |
| 51 | +``` |
42 | 52 |
|
43 | | -## ECDSA |
| 53 | +## Usage Examples |
44 | 54 |
|
| 55 | +### ECDSA |
45 | 56 | ```swift |
46 | 57 | import secp256k1 |
47 | 58 |
|
48 | | -// Private key |
| 59 | +// Private key |
49 | 60 | let privateBytes = try! "14E4A74438858920D8A35FB2D88677580B6A2EE9BE4E711AE34EC6B396D87B5C".bytes |
50 | 61 | let privateKey = try! secp256k1.Signing.PrivateKey(rawRepresentation: privateBytes) |
51 | 62 |
|
52 | | -// Public key |
| 63 | +// Public key |
53 | 64 | print(String(bytes: privateKey.publicKey.rawRepresentation)) |
54 | 65 |
|
55 | | -// ECDSA |
| 66 | +// ECDSA signature |
56 | 67 | let messageData = "We're all Satoshi.".data(using: .utf8)! |
57 | 68 | let signature = try! privateKey.signature(for: messageData) |
58 | 69 |
|
59 | | -// DER signature |
| 70 | +// DER signature |
60 | 71 | print(try! signature.derRepresentation.base64EncodedString()) |
61 | 72 | ``` |
62 | 73 |
|
63 | | -## Schnorr |
64 | | - |
| 74 | +### Schnorr |
65 | 75 | ```swift |
66 | 76 | // Strict BIP340 mode is disabled by default for Schnorr signatures with variable length messages |
67 | 77 | let privateKey = try! secp256k1.Schnorr.PrivateKey() |
@@ -161,7 +171,63 @@ oUQDQgAEt2uDn+2GqqYs/fmkBr5+rCQ3oiFSIJMAcjHIrTDS6HEELgguOatmFBOp |
161 | 171 | let privateKey = try! secp256k1.Signing.PrivateKey(pemRepresentation: privateKeyString) |
162 | 172 | ``` |
163 | 173 |
|
| 174 | +## MuSig2 |
164 | 175 |
|
165 | | -# Danger |
166 | | -These APIs should not be considered stable and may change at any time. |
167 | | - |
| 176 | +```swift |
| 177 | +// Initialize private keys for two signers |
| 178 | +let firstPrivateKey = try secp256k1.Schnorr.PrivateKey() |
| 179 | +let secondPrivateKey = try secp256k1.Schnorr.PrivateKey() |
| 180 | + |
| 181 | +// Aggregate the public keys using MuSig |
| 182 | +let aggregateKey = try secp256k1.MuSig.aggregate([firstPrivateKey.publicKey, secondPrivateKey.publicKey]) |
| 183 | + |
| 184 | +// Message to be signed |
| 185 | +let message = "Vires in Numeris.".data(using: .utf8)! |
| 186 | +let messageHash = SHA256.hash(data: message) |
| 187 | + |
| 188 | +// Generate nonces for each signer |
| 189 | +let firstNonce = try secp256k1.MuSig.Nonce.generate( |
| 190 | + secretKey: firstPrivateKey, |
| 191 | + publicKey: firstPrivateKey.publicKey, |
| 192 | + msg32: Array(messageHash) |
| 193 | +) |
| 194 | + |
| 195 | +let secondNonce = try secp256k1.MuSig.Nonce.generate( |
| 196 | + secretKey: secondPrivateKey, |
| 197 | + publicKey: secondPrivateKey.publicKey, |
| 198 | + msg32: Array(messageHash) |
| 199 | +) |
| 200 | + |
| 201 | +// Aggregate nonces |
| 202 | +let aggregateNonce = try secp256k1.MuSig.Nonce(aggregating: [firstNonce.pubnonce, secondNonce.pubnonce]) |
| 203 | + |
| 204 | +// Create partial signatures |
| 205 | +let firstPartialSignature = try firstPrivateKey.partialSignature( |
| 206 | + for: messageHash, |
| 207 | + pubnonce: firstNonce.pubnonce, |
| 208 | + secureNonce: firstNonce.secnonce, |
| 209 | + publicNonceAggregate: aggregateNonce, |
| 210 | + publicKeyAggregate: aggregateKey |
| 211 | +) |
| 212 | + |
| 213 | +let secondPartialSignature = try secondPrivateKey.partialSignature( |
| 214 | + for: messageHash, |
| 215 | + pubnonce: secondNonce.pubnonce, |
| 216 | + secureNonce: secondNonce.secnonce, |
| 217 | + publicNonceAggregate: aggregateNonce, |
| 218 | + publicKeyAggregate: aggregateKey |
| 219 | +) |
| 220 | + |
| 221 | +// Aggregate partial signatures into a full signature |
| 222 | +let aggregateSignature = try secp256k1.MuSig.aggregateSignatures([firstPartialSignature, secondPartialSignature]) |
| 223 | + |
| 224 | +// Verify the aggregate signature |
| 225 | +let isValid = aggregateKey.isValidSignature( |
| 226 | + aggregateSignature, |
| 227 | + publicKey: firstPublicKey, |
| 228 | + nonce: firstNonce.pubnonce, |
| 229 | + for: messageHash |
| 230 | +) |
| 231 | + |
| 232 | +print("Is valid MuSig signature: \(isValid)") |
| 233 | +``` |
0 commit comments