比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.PublicKeyK1.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 -> SharedSecretlibsecp256k1 - SHA-256 哈希压缩点 - ecdh:with -> SharedSecretecdhPoint -> Datalet 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)