Build Status

🔐 swift-secp256k1

用于椭圆曲线公钥密码学、ECDSA 和 Schnorr 签名的 Swift 包,适用于比特币,具有来自 libsecp256k1 的 C 绑定。

目标

安装

此软件包使用 Swift Package Manager。要将其添加到您的项目

使用 Xcode

  1. 转到 File > Add Packages...
  2. 输入软件包 URL:https://github.com/21-DOT-DEV/swift-secp256k1
  3. 选择所需的版本

使用 Package.swift

将以下内容添加到您的 Package.swift 文件

.package(name: "swift-secp256k1", url: "https://github.com/21-DOT-DEV/swift-secp256k1", exact: "0.18.0"),

然后,在您的目标中包含 secp256k1 作为依赖项

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

警告

这些 API 不被认为是稳定的,可能会随着任何更新而改变。使用 exact: 指定版本以避免破坏性更改。

试试看

使用 SPI Playgrounds app

arena 21-DOT-DEV/swift-secp256k1

使用示例

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 signature
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 密钥格式 (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)

MuSig2

// Initialize private keys for two signers
let firstPrivateKey = try secp256k1.Schnorr.PrivateKey()
let secondPrivateKey = try secp256k1.Schnorr.PrivateKey()

// Aggregate the public keys using MuSig
let aggregateKey = try secp256k1.MuSig.aggregate([firstPrivateKey.publicKey, secondPrivateKey.publicKey])

// Message to be signed
let message = "Vires in Numeris.".data(using: .utf8)!
let messageHash = SHA256.hash(data: message)

// Generate nonces for each signer
let firstNonce = try secp256k1.MuSig.Nonce.generate(
    secretKey: firstPrivateKey,
    publicKey: firstPrivateKey.publicKey,
    msg32: Array(messageHash)
)

let secondNonce = try secp256k1.MuSig.Nonce.generate(
    secretKey: secondPrivateKey,
    publicKey: secondPrivateKey.publicKey,
    msg32: Array(messageHash)
)

// Aggregate nonces
let aggregateNonce = try secp256k1.MuSig.Nonce(aggregating: [firstNonce.pubnonce, secondNonce.pubnonce])

// Create partial signatures
let firstPartialSignature = try firstPrivateKey.partialSignature(
    for: messageHash,
    pubnonce: firstNonce.pubnonce,
    secureNonce: firstNonce.secnonce,
    publicNonceAggregate: aggregateNonce,
    publicKeyAggregate: aggregateKey
)

let secondPartialSignature = try secondPrivateKey.partialSignature(
    for: messageHash,
    pubnonce: secondNonce.pubnonce,
    secureNonce: secondNonce.secnonce,
    publicNonceAggregate: aggregateNonce,
    publicKeyAggregate: aggregateKey
)

// Aggregate partial signatures into a full signature
let aggregateSignature = try secp256k1.MuSig.aggregateSignatures([firstPartialSignature, secondPartialSignature])

// Verify the aggregate signature
let isValid = aggregateKey.isValidSignature(
    aggregateSignature,
    publicKey: firstPublicKey,
    nonce: firstNonce.pubnonce,
    for: messageHash
)

print("Is valid MuSig signature: \(isValid)")