{"id":13650900,"url":"https://github.com/21-DOT-DEV/swift-secp256k1","last_synced_at":"2025-04-22T18:33:10.729Z","repository":{"id":37079945,"uuid":"277252157","full_name":"21-DOT-DEV/swift-secp256k1","owner":"21-DOT-DEV","description":"Elliptic Curve, Schnorr, and ZKP for Bitcoin. Supports iOS macOS tvOS watchOS visionOS + Linux.","archived":false,"fork":false,"pushed_at":"2024-10-29T17:01:46.000Z","size":661,"stargazers_count":108,"open_issues_count":4,"forks_count":53,"subscribers_count":8,"default_branch":"main","last_synced_at":"2024-10-29T18:28:34.480Z","etag":null,"topics":["bitcoin","c","ecdh","ecdsa","ios","library","linux","macos","mobile","public-key","schnorr","secp256k1","swift","swift-package-manager","taproot","xcode","zero-knowledge"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/21-DOT-DEV.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-07-05T07:26:05.000Z","updated_at":"2024-10-29T17:01:49.000Z","dependencies_parsed_at":"2024-01-03T05:09:16.527Z","dependency_job_id":"2d49e415-742c-41fa-ac72-63310e4c9461","html_url":"https://github.com/21-DOT-DEV/swift-secp256k1","commit_stats":{"total_commits":528,"total_committers":6,"mean_commits":88.0,"dds":0.1515151515151515,"last_synced_commit":"e42c527989c3b2f0511cbf636a8be2e4f857dd4a"},"previous_names":["21-dot-dev/swift-secp256k1","gigabitcoin/secp256k1.swift"],"tags_count":43,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/21-DOT-DEV%2Fswift-secp256k1","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/21-DOT-DEV%2Fswift-secp256k1/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/21-DOT-DEV%2Fswift-secp256k1/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/21-DOT-DEV%2Fswift-secp256k1/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/21-DOT-DEV","download_url":"https://codeload.github.com/21-DOT-DEV/swift-secp256k1/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223903240,"owners_count":17222501,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bitcoin","c","ecdh","ecdsa","ios","library","linux","macos","mobile","public-key","schnorr","secp256k1","swift","swift-package-manager","taproot","xcode","zero-knowledge"],"created_at":"2024-08-02T02:00:42.319Z","updated_at":"2025-04-22T18:33:10.716Z","avatar_url":"https://github.com/21-DOT-DEV.png","language":"Swift","funding_links":[],"categories":["Swift libraries"],"sub_categories":[],"readme":"[![Build Status](https://app.bitrise.io/app/18c18db60fc4fddf/status.svg?token=nczB4mTPCrlTfDQnXH_8Pw\u0026branch=main)](https://app.bitrise.io/app/18c18db60fc4fddf) [![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2F21-DOT-DEV%2Fswift-secp256k1%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/21-DOT-DEV/swift-secp256k1) [![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2F21-DOT-DEV%2Fswift-secp256k1%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/21-DOT-DEV/swift-secp256k1)\n\n# 🔐 swift-secp256k1\n\nSwift package for elliptic curve public key cryptography, ECDSA, and Schnorr Signatures for Bitcoin, with C bindings from [libsecp256k1](https://github.com/bitcoin-core/secp256k1).\n\n## Objectives\n\n- Provide lightweight ECDSA \u0026 Schnorr Signatures functionality\n- Support simple and advanced usage, including BIP-327 and BIP-340\n- Expose C bindings for full control of the secp256k1 implementation\n- Offer a familiar API design inspired by [Swift Crypto](https://github.com/apple/swift-crypto)\n- Maintain automatic updates for Swift and libsecp256k1\n- Ensure availability for Linux and Apple platform ecosystems\n\n## Installation\n\nThis package uses Swift Package Manager. To add it to your project:\n\n### Using Xcode\n\n1. Go to `File \u003e Add Packages...`\n2. Enter the package URL: `https://github.com/21-DOT-DEV/swift-secp256k1`\n3. Select the desired version\n\n### Using Package.swift\n\nAdd the following to your `Package.swift` file:\n\n```swift\n.package(name: \"swift-secp256k1\", url: \"https://github.com/21-DOT-DEV/swift-secp256k1\", exact: \"0.18.0\"),\n```\n\nThen, include `secp256k1` as a dependency in your target:\n\n```swift\n.target(name: \"\u003ctarget\u003e\", dependencies: [\n    .product(name: \"secp256k1\", package: \"swift-secp256k1\")\n]),\n```\n\n\u003e [!WARNING]  \n\u003e These APIs are not considered stable and may change with any update. Specify a version using `exact:` to avoid breaking changes.\n\n### Try it out\n\nUse [SPI Playgrounds app](https://swiftpackageindex.com/try-in-a-playground):\n\n```swift\narena 21-DOT-DEV/swift-secp256k1\n```\n\n## Usage Examples\n\n### ECDSA\n```swift\nimport secp256k1\n\n// Private key\nlet privateBytes = try! \"14E4A74438858920D8A35FB2D88677580B6A2EE9BE4E711AE34EC6B396D87B5C\".bytes\nlet privateKey = try! secp256k1.Signing.PrivateKey(dataRepresentation: privateBytes)\n\n// Public key\nprint(String(bytes: privateKey.publicKey.dataRepresentation))\n\n// ECDSA signature\nlet messageData = \"We're all Satoshi.\".data(using: .utf8)!\nlet signature = try! privateKey.signature(for: messageData)\n\n// DER signature\nprint(try! signature.derRepresentation.base64EncodedString())\n```\n\n### Schnorr\n```swift\n// Strict BIP340 mode is disabled by default for Schnorr signatures with variable length messages\nlet privateKey = try! secp256k1.Schnorr.PrivateKey()\n\n// Extra params for custom signing\nvar auxRand = try! \"C87AA53824B4D7AE2EB035A2B5BBBCCC080E76CDC6D1692C4B0B62D798E6D906\".bytes\nvar messageDigest = try! \"7E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C\".bytes\n\n// API allows for signing variable length messages\nlet signature = try! privateKey.signature(message: \u0026messageDigest, auxiliaryRand: \u0026auxRand)\n```\n\n## Tweak\n\n```swift\nlet privateKey = try! secp256k1.Signing.PrivateKey()\n\n// Adding a tweak to the private key and public key\nlet tweak = try! \"5f0da318c6e02f653a789950e55756ade9f194e1ec228d7f368de1bd821322b6\".bytes\nlet tweakedPrivateKey = try! privateKey.add(tweak)\nlet tweakedPublicKeyKey = try! privateKey.publicKey.add(tweak)\n```\n\n## Elliptic Curve Diffie Hellman\n\n```swift\nlet privateKey = try! secp256k1.KeyAgreement.PrivateKey()\nlet publicKey = try! secp256k1.KeyAgreement.PrivateKey().publicKey\n\n// Create a compressed shared secret with a private key from only a public key\nlet sharedSecret = try! privateKey.sharedSecretFromKeyAgreement(with: publicKey, format: .compressed)\n\n// By default, libsecp256k1 hashes the x-coordinate with version information.\nlet symmetricKey = SHA256.hash(data: sharedSecret.bytes)\n```\n\n## Silent Payments Scheme\n\n```swift\nlet privateSign1 = try! secp256k1.Signing.PrivateKey()\nlet privateSign2 = try! secp256k1.Signing.PrivateKey()\n\nlet privateKey1 = try! secp256k1.KeyAgreement.PrivateKey(dataRepresentation: privateSign1.dataRepresentation)\nlet privateKey2 = try! secp256k1.KeyAgreement.PrivateKey(dataRepresentation: privateSign2.dataRepresentation)\n\nlet publicKey1 = try! secp256k1.KeyAgreement.PublicKey(dataRepresentation: privateKey1.publicKey.dataRepresentation)\n\nlet sharedSecret1 = try! privateKey1.sharedSecretFromKeyAgreement(with: privateKey2.publicKey)\nlet sharedSecret2 = try! privateKey2.sharedSecretFromKeyAgreement(with: publicKey1)\n\nlet symmetricKey1 = SHA256.hash(data: sharedSecret1.bytes)\nlet symmetricKey2 = SHA256.hash(data: sharedSecret2.bytes)\n\nlet sharedSecretSign1 = try! secp256k1.Signing.PrivateKey(dataRepresentation: symmetricKey1.bytes)\nlet sharedSecretSign2 = try! secp256k1.Signing.PrivateKey(dataRepresentation: symmetricKey2.bytes)\n\n// Spendable Silent Payment private key\nlet privateTweak1 = try! sharedSecretSign1.add(xonly: privateSign1.publicKey.xonly.bytes)\nlet publicTweak2 = try! sharedSecretSign2.publicKey.add(privateSign1.publicKey.xonly.bytes)\n\nlet schnorrPrivate = try! secp256k1.Schnorr.PrivateKey(dataRepresentation: sharedSecretSign2.dataRepresentation)\n// Payable Silent Payment public key\nlet xonlyTweak2 = try! schnorrPrivate.xonly.add(privateSign1.publicKey.xonly.bytes)\n```\n\n## Recovery\n\n```swift\nlet privateKey = try! secp256k1.Recovery.PrivateKey()\nlet messageData = \"We're all Satoshi.\".data(using: .utf8)!\n\n// Create a recoverable ECDSA signature\nlet recoverySignature = try! privateKey.signature(for: messageData)\n\n// Recover an ECDSA public key from a signature\nlet publicKey = try! secp256k1.Recovery.PublicKey(messageData, signature: recoverySignature)\n\n// Convert a recoverable signature into a normal signature\nlet signature = try! recoverySignature.normalize\n```\n\n## Combine Public Keys\n\n```swift\nlet privateKey = try! secp256k1.Signing.PrivateKey()\nlet publicKey = try! secp256k1.Signing.PrivateKey().public\n\n// The Combine API arguments are an array of PublicKey objects and an optional format \npublicKey.combine([privateKey.publicKey], format: .uncompressed)\n```\n\n## PEM Key Format\n\n```swift\nlet privateKeyString = \"\"\"\n-----BEGIN EC PRIVATE KEY-----\nMHQCAQEEIBXwHPDpec6b07GeLbnwetT0dvWzp0nV3MR+4pPKXIc7oAcGBSuBBAAK\noUQDQgAEt2uDn+2GqqYs/fmkBr5+rCQ3oiFSIJMAcjHIrTDS6HEELgguOatmFBOp\n2wU4P2TAl/0Ihiq+nMkrAIV69m2W8g==\n-----END EC PRIVATE KEY-----\n\"\"\"\n\n// Import keys generated from OpenSSL\nlet privateKey = try! secp256k1.Signing.PrivateKey(pemRepresentation: privateKeyString)\n```\n\n## MuSig2\n\n```swift\n// Initialize private keys for two signers\nlet firstPrivateKey = try secp256k1.Schnorr.PrivateKey()\nlet secondPrivateKey = try secp256k1.Schnorr.PrivateKey()\n\n// Aggregate the public keys using MuSig\nlet aggregateKey = try secp256k1.MuSig.aggregate([firstPrivateKey.publicKey, secondPrivateKey.publicKey])\n\n// Message to be signed\nlet message = \"Vires in Numeris.\".data(using: .utf8)!\nlet messageHash = SHA256.hash(data: message)\n\n// Generate nonces for each signer\nlet firstNonce = try secp256k1.MuSig.Nonce.generate(\n    secretKey: firstPrivateKey,\n    publicKey: firstPrivateKey.publicKey,\n    msg32: Array(messageHash)\n)\n\nlet secondNonce = try secp256k1.MuSig.Nonce.generate(\n    secretKey: secondPrivateKey,\n    publicKey: secondPrivateKey.publicKey,\n    msg32: Array(messageHash)\n)\n\n// Aggregate nonces\nlet aggregateNonce = try secp256k1.MuSig.Nonce(aggregating: [firstNonce.pubnonce, secondNonce.pubnonce])\n\n// Create partial signatures\nlet firstPartialSignature = try firstPrivateKey.partialSignature(\n    for: messageHash,\n    pubnonce: firstNonce.pubnonce,\n    secureNonce: firstNonce.secnonce,\n    publicNonceAggregate: aggregateNonce,\n    publicKeyAggregate: aggregateKey\n)\n\nlet secondPartialSignature = try secondPrivateKey.partialSignature(\n    for: messageHash,\n    pubnonce: secondNonce.pubnonce,\n    secureNonce: secondNonce.secnonce,\n    publicNonceAggregate: aggregateNonce,\n    publicKeyAggregate: aggregateKey\n)\n\n// Aggregate partial signatures into a full signature\nlet aggregateSignature = try secp256k1.MuSig.aggregateSignatures([firstPartialSignature, secondPartialSignature])\n\n// Verify the aggregate signature\nlet isValid = aggregateKey.isValidSignature(\n    firstPartialSignature,\n    publicKey: firstPrivateKey.publicKey,\n    nonce: firstNonce.pubnonce,\n    for: messageHash\n)\n\nprint(\"Is valid MuSig signature: \\(isValid)\")\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F21-DOT-DEV%2Fswift-secp256k1","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F21-DOT-DEV%2Fswift-secp256k1","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F21-DOT-DEV%2Fswift-secp256k1/lists"}