比K2更安全
K1 是对 libsecp256k1 (bitcoin-core/secp256k1) 的 Swift 封装,提供 ECDSA、Schnorr (BIP340) 和 ECDH 功能。
在此处阅读完整文档 SwiftPackageIndex。
K1 的 API 几乎与 Apple 的 CryptoKit 1:1 映射,为每个功能提供一组密钥对。 例如,在 CryptoKit 中,你有 Curve25519.KeyAgreement.PrivateKey
和 Curve25519.KeyAgreement.PublicKey
,它们与 Curve25519.Signing.PrivateKey
和 Curve25519.Signing.PublicKey
是分开的。
就像这样,K1 提供这些密钥对
K1.KeyAgreement.PrivateKey
/ K1.KeyAgreement.PublicKey
用于密钥协商 (ECDH)K1.Schnorr.PrivateKey
/ K1.Schnorr.PublicKey
用于使用 Schnorr 签名方案进行签名/验证的方法K1.ECDSAWithKeyRecovery.PrivateKey
/ K1.ECDSAWithKeyRecovery.PublicKey
用于使用 ECDSA 进行签名/验证的方法(生成/验证可以恢复公钥的签名)K1.ECDSA.PrivateKey
/ K1.ECDSA.PublicKey
用于使用 ECDSA 进行签名/验证的方法(生成/验证不可恢复公钥的签名)就像你可以使用任何初始化器和序列化器在 Curve25519.KeyAgreement.PrivateKey
和 Curve25519.Signing.PrivateKey
之间来回转换一样,你可以在 K1 中所有功能的 PrivateKey 和 PublicKey 之间进行转换。
所有密钥都可以使用这些计算属性进行序列化
{
var rawRepresentation: Data { get }
var derRepresentation: Data { get }
var pemRepresentation: String { get }
var x963Representation: Data { get }
}
所有密钥都可以使用这些初始化器进行反序列化
{
init(rawRepresentation: some ContiguousBytes) throws
init(derRepresentation: some RandomAccessCollection<UInt8>) throws
init(pemRepresentation: String) throws
init(x963Representation: some ContiguousBytes) throws
}
此外,所有 PrivateKey 都有这些额外的 API
{
init()
associatedtype PublicKey
var publicKey: PublicKey { get }
}
此外,所有 PublicKey 都有这些额外的 API
{
init(compressedRepresentation: some ContiguousBytes) throws
var compressedRepresentation: Data { get }
}
存在两组 ECDSA 密钥对
K1.ECDSAWithKeyRecovery.PrivateKey
和 K1.ECDSAWithKeyRecovery.PublicKey
K1.ECDSA.PrivateKey
和 K1.ECDSA.PublicKey
对于每个私钥,存在两种不同的 signature:for:options
方法(一种接受哈希数据,另一种接受 Digest
作为参数)和一种 signature:forUnhashed:options
。
option
是一个 K1.ECDSA.SigningOptions
结构体,默认情况下指定 RFC6979
确定性签名,根据比特币标准,但是,您可以更改为使用安全随机 nonce 代替。
let alice = K1.ECDSA.PrivateKey()
let hashedMessage: Data = // from somewhere
let signature = try alice.signature(for: hashedMessage)
let message: Data = // from somewhere
let digest = SHA256.hash(data: message)
let signature = try alice.signature(for: digest)
forUnhashed
将对消息进行 SHA256
哈希,然后对其进行签名。
let message: Data = // from somewhere
let signature = try alice.signature(forUnhashed: message)
let hashedMessage: Data = // from somewhere
let publicKey: K1.ECDSA.PublicKey = alice.publcKey
let signature: K1.ECDSA.Signature // from above
assert(
publicKey.isValidSignature(signature, hashed: hashedMessage)
) // PASS
let message: Data = // from somewhere
let digest = SHA256.hash(data: message)
let signature: K1.ECDSA.Signature // from above
assert(
publicKey.isValidSignature(signature, digest: digest)
) // PASS
let message: Data = // from somewhere
let signature: K1.ECDSA.Signature // from above
assert(
publicKey.isValidSignature(signature, unhashed: message)
) // PASS
所有签名和验证 API 与 NonRecoverable
命名空间相同。
let alice = K1.ECDSA.PrivateKey()
let message: Data = // from somewhere
let digest = SHA256.hash(data: message)
let signature: K1.ECDSAWithKeyRecovery.Signature = try alice.signature(for: digest)
let publicKey: K1.ECDSAWithKeyRecovery.PublicKey = alice.publicKey
assert(
publicKey.isValidSignature(signature, digest: digest)
) // PASS
let alice = K1.Schnorr.PrivateKey()
let signature = try alice.signature(forUnhashed: message)
存在其他签名变体,signature:for:options
(哈希数据)和 signature:for:options
(Digest
),如果您已经有一个哈希消息。 所有三种变体都采用一个 K1.Schnorr.SigningOptions
结构,您可以在其中传递 auxiliaryRandomData
进行签名。
let publicKey: K1.Schnorr.PublicKey = alice.publicKey
assert(publicKey.isValidSignature(signature, unhashed: message)) // PASS
或者,可选地 isValidSignature:digest
或 isValidSignature:hashed
。
Schnorr 签名实现是 BIP340,因为我们使用 libsecp256k1,它只提供 BIP340 Schnorr 方案。
值得注意的是,某些 Schnorr 实现与 BIP340 不兼容,因此与此库不兼容,例如 Zilliqa 的 ( kudelski 报告, libsecp256k1 提案, Twitter 帖子)。
该库提供三种不同的 EC Diffie-Hellman (ECDH) 密钥交换函数
ASN1 x9.63
- 无哈希,仅返回点的 X
坐标 - sharedSecretFromKeyAgreement:with -> SharedSecret
libsecp256k1
- SHA-256 哈希压缩点 - ecdh:with -> SharedSecret
ecdhPoint -> Data
let alice = try K1.KeyAgreement.PrivateKey()
let bob = try K1.KeyAgreement.PrivateKey()
仅返回点的 X
坐标,遵循 ANSI X9.63 标准,嵌入在 CryptoKit.SharedSecret
中,这很有用,因为您可以在此 SharedSecret 上使用 CryptoKit
密钥派生函数,例如 x963DerivedSymmetricKey
或 hkdfDerivedSymmetricKey
。
如果您需要,可以使用 withUnsafeBytes
以原始数据的形式检索 X
坐标。
let ab: CryptoKit.SharedSecret = try alice.sharedSecretFromKeyAgreement(with: bob.publicKey)
let ba: CryptoKit.SharedSecret = try bob.sharedSecretFromKeyAgreement(with: alice.publicKey)
assert(ab == ba) // pass
ab.withUnsafeBytes {
assert(Data($0).count == 32) // pass
}
使用 libsecp256k1
默认行为,返回 **压缩** 点的 SHA-256 哈希,嵌入在 CryptoKit.SharedSecret
中,这很有用,因为您可以使用 CryptoKit
密钥派生函数。
let ab: CryptoKit.SharedSecret = try alice.ecdh(with: bob.publicKey)
let ba: CryptoKit.SharedSecret = try bob.ecdh(with: alice.publicKey)
assert(ab == ba) // pass
ab.withUnsafeBytes {
assert(Data($0).count == 32) // pass
}
返回整个未压缩的 EC 点,而不对其进行哈希处理。 如果你想构建自己的密码函数(例如一些自定义 ECIES),这可能很有用。
let ab: Data = try alice.ecdhPoint(with: bob.publicKey)
let ba: Data = try bob.ecdhPoint(with: alice.publicKey)
assert(ab == ba) // pass
assert(ab.count == 65) // pass
K1
是 libsecp256k1 的 Swift 封装,因此如果没有 Bitcoin Core 开发人员,就不会有这个库。 非常感谢你提供了一个出色的库! 我已将其作为子模块包含在内,而没有对代码进行任何更改,即文件中的版权标头保持不变。
K1
使用了 swift-crypto
中的一些代码,这些代码已复制并带有相关的版权标头。 由于 swift-crypto
是在 Apache 下获得许可的,因此该库也是如此。
进入根目录并运行
./scripts/build.sh
要克隆依赖项 libsecp256k1,使用提交 427bc3cdcfbc74778070494daab1ae5108c71368 (semver 0.3.0)
该项目中的某些文件是使用名为 gyb ("generate your boilerplate") 的 Swift Utils 工具自动生成的(元编程)。 gyb
包含在 ./scripts/gyb
中。
gyb
将从某些 Foobar.swift.gyb
*模板* 文件生成一些 Foobar.swift
Swift 文件。 **你不应该直接编辑 Foobar.swift
**,因为下次运行 gyb
时,该生成文件中的所有手动编辑都将被覆盖。
你可以像这样为单个文件运行 gyb
./scripts/gyb --line-directive "" Sources/Foobar.swift.gyb -o Sources/Foobar.swift
更方便的是,你可以运行 bash 脚本 ./scripts/generate_boilerplate_files_with_gyb.sh
以从其相应的 gyb 模板生成所有 Swift 文件。
**如果你添加一个新的 .gyb
文件,你应该在其内部附加一个 // MARK: - Generated file, do NOT edit
警告**,例如
// MARK: - Generated file, do NOT edit
// any edits of this file WILL be overwritten and thus discarded
// see section `gyb` in `README` for details.
libsecp256k1
,libsecp256k1
, ❌ 没有 Schnorr)libsecp256k1
, ❌ 没有 Schnorr)