secp256k1.swift

0.16.0

Elliptic Curve, Schnorr, and ZKP for Bitcoin. Supports iOS macOS tvOS watchOS visionOS + Linux.
GigaBitcoin/secp256k1.swift

What's New

Support visionOS

2024-01-26T22:15:30Z

Highlights

What's Changed
  • Bump Submodules/secp256k1 from 1a81df8 to efe85c7 by @dependabot in #467
  • Bump github.com/csjones/lefthook-plugin from 1.5.1 to 1.5.5 by @dependabot in #471
  • Bump github.com/nicklockwood/swiftformat from 0.52.3 to 0.52.11 by @dependabot in #470
  • Bump Submodules/secp256k1 from efe85c7 to d373bf6 by @dependabot in #473
  • Bump Submodules/secp256k1-zkp from eb4fb6d to 0ffca6f by @dependabot in #474
  • Bump github.com/nicklockwood/swiftformat from 0.52.11 to 0.53.0 by @dependabot in #476
  • Bump Submodules/swift-crypto from 3e84977 to cc76b89 by @dependabot in #475
  • Bump Submodules/secp256k1 from d373bf6 to a9db9f2 by @dependabot in #477
  • Bump github.com/csjones/lefthook-plugin from 1.5.5 to 1.5.6 by @dependabot in #479
  • Bump Submodules/secp256k1 from a9db9f2 to e4af41c by @dependabot in #480
  • Bump github.com/csjones/lefthook-plugin from 1.5.6 to 1.5.7 by @dependabot in #481
  • Bump github.com/csjones/lefthook-plugin from 1.5.7 to 1.6.0 by @dependabot in #482
  • Bump github.com/csjones/lefthook-plugin from 1.6.0 to 1.6.1 by @dependabot in #486
  • Bump Submodules/swift-crypto from cc76b89 to 029eeac by @dependabot in #485
  • Bump Submodules/secp256k1 from e4af41c to 2483627 by @dependabot in #483
  • Bump Submodules/secp256k1-zkp from 0ffca6f to b5a6812 by @dependabot in #484
  • Bump Submodules/secp256k1-zkp from b5a6812 to 03aecaf by @dependabot in #487
  • Bump Submodules/secp256k1 from 2483627 to 0653a25 by @dependabot in #488
  • Bump Submodules/swift-crypto from 029eeac to 606608d by @dependabot in #490

Full Changelog: 0.15.0...0.16.0

Build Status Build Status

🔐 secp256k1.swift

Swift package with elliptic curve public key cryptography, ECDSA, Schnorr Signatures for Bitcoin and C bindings from libsecp256k1.

Objectives

Long-term goals are:

  • Lightweight ECDSA & Schnorr Signatures functionality
  • Built for simple or advance usage with things like BIP340
  • Exposed C bindings to take full control of the secp256k1 implementation
  • Familiar API design by modeling after Swift Crypto
  • Automatic updates for Swift and libsecp256k1
  • Availability for Linux and Apple platform ecosystems

Getting Started

This repository primarily uses Swift package manager as its build tool, so we recommend using that as well. Xcode comes with built-in support 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:

.package(name: "secp256k1.swift", url: "https://github.com/GigaBitcoin/secp256k1.swift.git", exact: "0.15.0"),

Include secp256k1 as a dependency for your executable target:

.target(name: "<target>", dependencies: [
    .product(name: "secp256k1", package: "secp256k1.swift")
]),

Try in a playground using the SPI Playgrounds app or 🏟 Arena

arena GigaBitcoin/secp256k1.swift

Example Usage

ECDSA

import secp256k1

//  Private key
let privateBytes = try! "14E4A74438858920D8A35FB2D88677580B6A2EE9BE4E711AE34EC6B396D87B5C".bytes
let privateKey = try! secp256k1.Signing.PrivateKey(rawRepresentation: privateBytes)

//  Public key
print(String(bytes: privateKey.publicKey.rawRepresentation))

// ECDSA
let messageData = "We're all Satoshi.".data(using: .utf8)!
let signature = try! privateKey.signature(for: messageData)

//  DER signature
print(try! signature.derRepresentation.base64EncodedString())

Schnorr

// Strict BIP340 mode is disabled by default for Schnorr signatures with variable length messages
let privateKey = try! secp256k1.Schnorr.PrivateKey()

// Extra params for custom signing
var auxRand = try! "C87AA53824B4D7AE2EB035A2B5BBBCCC080E76CDC6D1692C4B0B62D798E6D906".bytes
var messageDigest = try! "7E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C".bytes

// API allows for signing variable length messages
let signature = try! privateKey.signature(message: &messageDigest, auxiliaryRand: &auxRand)

Tweak

let privateKey = try! secp256k1.Signing.PrivateKey()

// Adding a tweak to the private key and public key
let tweak = try! "5f0da318c6e02f653a789950e55756ade9f194e1ec228d7f368de1bd821322b6".bytes
let tweakedPrivateKey = try! privateKey.add(tweak)
let tweakedPublicKeyKey = try! privateKey.publicKey.add(tweak)

Elliptic Curve Diffie Hellman

let privateKey = try! secp256k1.KeyAgreement.PrivateKey()
let publicKey = try! secp256k1.KeyAgreement.PrivateKey().publicKey

// Create a compressed shared secret with a private key from only a public key
let sharedSecret = try! privateKey.sharedSecretFromKeyAgreement(with: publicKey, format: .compressed)

// By default, libsecp256k1 hashes the x-coordinate with version information.
let symmetricKey = SHA256.hash(data: sharedSecret.bytes)

Silent Payments Scheme

let privateSign1 = try! secp256k1.Signing.PrivateKey()
let privateSign2 = try! secp256k1.Signing.PrivateKey()

let privateKey1 = try! secp256k1.KeyAgreement.PrivateKey(rawRepresentation: privateSign1.rawRepresentation)
let privateKey2 = try! secp256k1.KeyAgreement.PrivateKey(rawRepresentation: privateSign2.rawRepresentation)

let sharedSecret1 = try! privateKey1.sharedSecretFromKeyAgreement(with: privateKey2.publicKey)
let sharedSecret2 = try! privateKey2.sharedSecretFromKeyAgreement(with: privateKey1.publicKey)

let sharedSecretSign1 = try! secp256k1.Signing.PrivateKey(rawRepresentation: sharedSecret1.bytes)
let sharedSecretSign2 = try! secp256k1.Signing.PrivateKey(rawRepresentation: sharedSecret2.bytes)

// Payable Silent Payment public key
let xonlyTweak2 = try! sharedSecretSign2.publicKey.xonly.add(privateSign1.publicKey.xonly.bytes)

// Spendable Silent Payment private key
let privateTweak1 = try! sharedSecretSign1.add(xonly: privateSign1.publicKey.xonly.bytes)

Recovery

let privateKey = try! secp256k1.Recovery.PrivateKey()
let messageData = "We're all Satoshi.".data(using: .utf8)!

// Create a recoverable ECDSA signature
let recoverySignature = try! privateKey.signature(for: messageData)

// Recover an ECDSA public key from a signature
let publicKey = try! secp256k1.Recovery.PublicKey(messageData, signature: recoverySignature)

// Convert a recoverable signature into a normal signature
let signature = try! recoverySignature.normalize

Combine Public Keys

let privateKey = try! secp256k1.Signing.PrivateKey()
let publicKey = try! secp256k1.Signing.PrivateKey().public

// The Combine API arguments are an array of PublicKey objects and an optional format 
publicKey.combine([privateKey.publicKey], format: .uncompressed)

PEM Key Format

let privateKeyString = """
-----BEGIN EC PRIVATE KEY-----
MHQCAQEEIBXwHPDpec6b07GeLbnwetT0dvWzp0nV3MR+4pPKXIc7oAcGBSuBBAAK
oUQDQgAEt2uDn+2GqqYs/fmkBr5+rCQ3oiFSIJMAcjHIrTDS6HEELgguOatmFBOp
2wU4P2TAl/0Ihiq+nMkrAIV69m2W8g==
-----END EC PRIVATE KEY-----
"""

// Import keys generated from OpenSSL
let privateKey = try! secp256k1.Signing.PrivateKey(pemRepresentation: privateKeyString)

Danger

These APIs should not be considered stable and may change at any time.

Description

  • Swift Tools 5.8.0
View More Packages from this Author

Dependencies

Last updated: Tue Apr 23 2024 21:08:03 GMT-0900 (Hawaii-Aleutian Daylight Time)