Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
16c6399
remove library evolution
Sajjon Jan 30, 2026
15b0588
first apinotes usage works, renames and removes nullability
Sajjon Jan 30, 2026
28c66d0
Swift macros to build API using InlineArray
Sajjon Jan 31, 2026
2914425
Apinotes ornament ecdh with nullability
Sajjon Feb 1, 2026
36d6ed2
Apinotes ornament `secp256k1_xonly_pubkey_from_pubkey` -> `xOnlyKeyFr…
Sajjon Feb 1, 2026
aa71579
Apinotes ornament `secp256k1_schnorrsig_verify` -> `verifySchnorrSign…
Sajjon Feb 1, 2026
edc3a0c
Apinotes ornament `secp256k1_keypair_create` -> `keypairFromPrivateKe…
Sajjon Feb 1, 2026
be59287
Apinotes ornament `secp256k1_schnorrsig_sign32` -> `schnorrSign(conte…
Sajjon Feb 1, 2026
4561a1d
Apinotes ornament `secp256k1_ecdsa_sign_recoverable` -> `ecdsaSignRec…
Sajjon Feb 1, 2026
ff99e8d
Apinotes ornament of types `secp256k1_ecdsa_signature` -> `ECDSASigna…
Sajjon Feb 1, 2026
c84f053
Apinotes rename type `secp256k1_pubkey` -> `PublicKeyRaw` and `secp25…
Sajjon Feb 1, 2026
1a34018
Apinotes ornament `secp256k1_ecdsa_recoverable_signature_parse_compac…
Sajjon Feb 1, 2026
c4e59bf
Apinotes ornament `secp256k1_ecdsa_recoverable_signature_serialize_co…
Sajjon Feb 1, 2026
a507586
Apinotes ornament `secp256k1_ecdsa_recoverable_signature_convert` -> …
Sajjon Feb 1, 2026
92c9d03
Apinotes ornament `secp256k1_ecdsa_signature_serialize_compact` -> `s…
Sajjon Feb 1, 2026
9300b19
Apinotes ornament `secp256k1_ecdsa_signature_serialize_der` -> `seria…
Sajjon Feb 1, 2026
9cf0ee7
Apinotes mapping \
Sajjon Feb 2, 2026
d5266fa
Apinotes rename `secp256k1_xonly_pubkey` -> `PublicKeyXOnlyRaw`
Sajjon Feb 2, 2026
63bedc2
Improved error doc
Sajjon Feb 2, 2026
6171381
Made `FFI` init non throwing -> many methods in K1 are no longer thro…
Sajjon Feb 3, 2026
38cd578
[no ci] WIP
Sajjon Mar 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .swiftformat
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
--tabwidth 4

--stripunusedargs closure-only
--enable marktypes
--disable redundantNilInit,redundantSelf,extensionAccessControl,simplifyGenericConstraints
--lineaftermarks false
--ifdef no-indent
Expand Down
15 changes: 15 additions & 0 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 30 additions & 3 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// swift-tools-version:6.2.1
// The swift-tools-version declares the minimum version of Swift required to build this package.

import CompilerPluginSupport
import PackageDescription

let development = false
Expand All @@ -21,9 +22,30 @@ let package = Package(
]
),
],
dependencies: [],
dependencies: [
.package(
url: "https://github.com/swiftlang/swift-syntax.git",
revision: "603.0.0-prerelease-2025-12-17"
),
],
targets: [
// Target `libsecp256k1` https://github.com/bitcoin-core/secp256k1
// Macro target(s)
.target(
name: "K1Macros",
dependencies: [
"K1MacrosImpl",
]
),
.macro(
name: "K1MacrosImpl",
dependencies: [
.product(name: "SwiftCompilerPlugin", package: "swift-syntax"),
.product(name: "SwiftSyntax", package: "swift-syntax"),
.product(name: "SwiftSyntaxBuilder", package: "swift-syntax"),
.product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
],
path: "Sources/K1MacrosImpl"
),
.target(
name: "secp256k1",
exclude: [
Expand Down Expand Up @@ -54,6 +76,7 @@ let package = Package(
"libsecp256k1/SECURITY.md",
],
cSettings: [
.headerSearchPath("libsecp256k1/include"),
// Basic config values that are universal and require no dependencies.
// https://github.com/bitcoin-core/secp256k1/blob/master/src/basic-config.h#L12-L13
.define("ECMULT_WINDOW_SIZE", to: "15"),
Expand All @@ -65,12 +88,16 @@ let package = Package(
.define("ENABLE_MODULE_RECOVERY"),
.define("ENABLE_MODULE_SCHNORRSIG"),
.define("ENABLE_MODULE_EXTRAKEYS"),
],
swiftSettings: [
.enableExperimentalFeature("SafeInteropWrappers"),
]
),
.target(
name: "K1",
dependencies: [
"secp256k1",
"K1Macros",
],
exclude: [
"K1/Keys/Keys.swift.gyb",
Expand All @@ -79,6 +106,7 @@ let package = Package(
],
swiftSettings: [
.define("CRYPTO_IN_SWIFTPM_FORCE_BUILD_API"),
.enableExperimentalFeature("SafeInteropWrappers"),
]
),
.testTarget(
Expand All @@ -103,7 +131,6 @@ if development {
.unsafeFlags([
"-Xfrontend", "-warn-concurrency",
"-Xfrontend", "-enable-actor-data-race-checks",
"-enable-library-evolution",
])
)
}
Expand Down
22 changes: 17 additions & 5 deletions Sources/K1/K1/ECDSA/ECDSASignatureNonRecoverable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,22 @@ extension K1.ECDSA.Signature {
wrapped: FFI.ECDSA.from(compactBytes: [UInt8](rawRepresentation))
)
}

/// Creates a `secp256k1` ECDSA signature from the raw representation.
///
/// Accepts 64 bytes on format: `R || S`, as defined in [rfc4754][rfc]. In
/// `libsecp256k1` this representation is called "compact".
///
/// - Parameter rawRepresentation: A raw representation of the ECDSA signature as an
/// `InlineArray<64, UInt8>`
///
/// [rfc]: https://tools.ietf.org/html/rfc4754
@available(macOS 26.0, iOS 26.0, tvOS 26.0, watchOS 26.0, *)
public init(rawRepresentation array64: InlineArray<64, UInt8>) throws {
try self.init(
wrapped: FFI.ECDSA.from(compactBytes: array64)
)
}
}

// MARK: ContiguousBytes
Expand All @@ -60,11 +76,7 @@ extension K1.ECDSA.Signature {
/// Returns 64 bytes on format: `R || S`, as defined in [rfc4754][rfc]. In
/// `libsecp256k1` this representation is called "compact".
public var rawRepresentation: Data {
do {
return try FFI.ECDSA.compact(wrapped)
} catch {
fatalError("Should never fail to convert ECDSA signatures to rawRepresentation.")
}
FFI.ECDSA.compact(wrapped)
}

/// A Distinguished Encoding Rules (DER) encoded representation of a
Expand Down
8 changes: 4 additions & 4 deletions Sources/K1/K1/ECDSA/ECDSASignatureRecoverable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,10 @@ extension K1.ECDSAWithKeyRecovery.Signature {
/// Compact aka `IEEE P1363` aka `R||S` and `V` (`RecoveryID`).
public func compact() throws -> Compact {
// swiftlint:disable:next identifier_name
let (rs, recid) = try FFI.ECDSAWithKeyRecovery.serializeCompact(
let (rs, recid) = FFI.ECDSAWithKeyRecovery.serializeCompact(
wrapped
)
return try .init(
return try Compact(
compact: Data(rs),
recoveryID: .init(recid: recid)
)
Expand Down Expand Up @@ -199,8 +199,8 @@ extension K1.ECDSAWithKeyRecovery.Signature {
// MARK: Conversion
extension K1.ECDSAWithKeyRecovery.Signature {
/// Converts this recoverable ECDSA signature to a non-recoverable version.
public func nonRecoverable() throws -> K1.ECDSA.Signature {
try K1.ECDSA.Signature(
public func nonRecoverable() -> K1.ECDSA.Signature {
K1.ECDSA.Signature(
wrapped: FFI.ECDSAWithKeyRecovery.nonRecoverable(self.wrapped)
)
}
Expand Down
59 changes: 28 additions & 31 deletions Sources/K1/K1/Keys/Keys.generated.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,16 @@ extension K1.KeyAgreement {
let publicKeyImpl: K1._PublicKeyImplementation

/// The corresponding public key.
public var publicKey: PublicKey {
try! .init(rawRepresentation: publicKeyImpl.rawRepresentation)
}
public let publicKey: PublicKey

init(impl: Impl) {
self.impl = impl
self.publicKeyImpl = impl.publicKey
do {
self.publicKey = try PublicKey(rawRepresentation: impl.publicKey.rawRepresentation)
} catch {
fatalError("Should never fail to instantiate a PublicKey from a PrivateKey, error: \(error)")
}
}

/// Creates a random `secp256k1` private key for key agreement.
Expand Down Expand Up @@ -216,7 +219,7 @@ extension K1.KeyAgreement {

/// Negates a public key (point) on the secp256k1 curve
public func negate() throws -> Self {
try Self(impl: impl.negate())
Self(impl: impl.negate())
}

/// Combines multiple public keys (points) on the secp256k1 curve
Expand All @@ -239,13 +242,16 @@ extension K1.Schnorr {
let publicKeyImpl: K1._PublicKeyImplementation

/// The corresponding public key.
public var publicKey: PublicKey {
try! .init(rawRepresentation: publicKeyImpl.rawRepresentation)
}
public let publicKey: PublicKey

init(impl: Impl) {
self.impl = impl
self.publicKeyImpl = impl.publicKey
do {
self.publicKey = try PublicKey(rawRepresentation: impl.publicKey.rawRepresentation)
} catch {
fatalError("Should never fail to instantiate a PublicKey from a PrivateKey, error: \(error)")
}
}

/// Creates a random `secp256k1` private key for signing.
Expand Down Expand Up @@ -439,7 +445,7 @@ extension K1.Schnorr {

/// Negates a public key (point) on the secp256k1 curve
public func negate() throws -> Self {
try Self(impl: impl.negate())
Self(impl: impl.negate())
}

/// Combines multiple public keys (points) on the secp256k1 curve
Expand All @@ -462,13 +468,16 @@ extension K1.ECDSA {
let publicKeyImpl: K1._PublicKeyImplementation

/// The corresponding public key.
public var publicKey: PublicKey {
try! .init(rawRepresentation: publicKeyImpl.rawRepresentation)
}
public let publicKey: PublicKey

init(impl: Impl) {
self.impl = impl
self.publicKeyImpl = impl.publicKey
do {
self.publicKey = try PublicKey(rawRepresentation: impl.publicKey.rawRepresentation)
} catch {
fatalError("Should never fail to instantiate a PublicKey from a PrivateKey, error: \(error)")
}
}

/// Creates a random `secp256k1` private key for signing.
Expand Down Expand Up @@ -662,7 +671,7 @@ extension K1.ECDSA {

/// Negates a public key (point) on the secp256k1 curve
public func negate() throws -> Self {
try Self(impl: impl.negate())
Self(impl: impl.negate())
}

/// Combines multiple public keys (points) on the secp256k1 curve
Expand All @@ -685,13 +694,16 @@ extension K1.ECDSAWithKeyRecovery {
let publicKeyImpl: K1._PublicKeyImplementation

/// The corresponding public key.
public var publicKey: PublicKey {
try! .init(rawRepresentation: publicKeyImpl.rawRepresentation)
}
public let publicKey: PublicKey

init(impl: Impl) {
self.impl = impl
self.publicKeyImpl = impl.publicKey
do {
self.publicKey = try PublicKey(rawRepresentation: impl.publicKey.rawRepresentation)
} catch {
fatalError("Should never fail to instantiate a PublicKey from a PrivateKey, error: \(error)")
}
}

/// Creates a random `secp256k1` private key for signing.
Expand Down Expand Up @@ -885,7 +897,7 @@ extension K1.ECDSAWithKeyRecovery {

/// Negates a public key (point) on the secp256k1 curve
public func negate() throws -> Self {
try Self(impl: impl.negate())
Self(impl: impl.negate())
}

/// Combines multiple public keys (points) on the secp256k1 curve
Expand All @@ -896,28 +908,13 @@ extension K1.ECDSAWithKeyRecovery {
}
}

// MARK: - K1.KeyAgreement.PrivateKey + _K1PrivateKeyProtocol
extension K1.KeyAgreement.PrivateKey: _K1PrivateKeyProtocol {}

// MARK: - K1.KeyAgreement.PublicKey + _K1PublicKeyProtocol
extension K1.KeyAgreement.PublicKey: _K1PublicKeyProtocol {}

// MARK: - K1.Schnorr.PrivateKey + _K1PrivateKeyProtocol
extension K1.Schnorr.PrivateKey: _K1PrivateKeyProtocol {}

// MARK: - K1.Schnorr.PublicKey + _K1PublicKeyProtocol
extension K1.Schnorr.PublicKey: _K1PublicKeyProtocol {}

// MARK: - K1.ECDSA.PrivateKey + _K1PrivateKeyProtocol
extension K1.ECDSA.PrivateKey: _K1PrivateKeyProtocol {}

// MARK: - K1.ECDSA.PublicKey + _K1PublicKeyProtocol
extension K1.ECDSA.PublicKey: _K1PublicKeyProtocol {}

// MARK: - K1.ECDSAWithKeyRecovery.PrivateKey + _K1PrivateKeyProtocol
extension K1.ECDSAWithKeyRecovery.PrivateKey: _K1PrivateKeyProtocol {}

// MARK: - K1.ECDSAWithKeyRecovery.PublicKey + _K1PublicKeyProtocol
extension K1.ECDSAWithKeyRecovery.PublicKey: _K1PublicKeyProtocol {}

// swiftlint:enable all
12 changes: 8 additions & 4 deletions Sources/K1/K1/Keys/Keys.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,17 @@ extension K1.${FEATURE} {
let publicKeyImpl: K1._PublicKeyImplementation

/// The corresponding public key.
public var publicKey: PublicKey {
try! .init(rawRepresentation: publicKeyImpl.rawRepresentation)
}
public let publicKey: PublicKey


init(impl: Impl) {
self.impl = impl
self.publicKeyImpl = impl.publicKey
do {
self.publicKey = try PublicKey(rawRepresentation: impl.publicKey.rawRepresentation)
} catch {
fatalError("Should never fail to instantiate a PublicKey from a PrivateKey, error: \(error)")
}
}

/// Creates a random `secp256k1` private key for ${PURPOSE_PRIVATEKEY}.
Expand Down Expand Up @@ -259,7 +263,7 @@ extension K1.${FEATURE} {

/// Negates a public key (point) on the secp256k1 curve
public func negate() throws -> Self {
try Self(impl: impl.negate())
Self(impl: impl.negate())
}

/// Combines multiple public keys (points) on the secp256k1 curve
Expand Down
20 changes: 5 additions & 15 deletions Sources/K1/K1/Keys/PublicKeyImplementation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,14 +83,12 @@ extension K1._PublicKeyImplementation {

/// `04 || X || Y` (65 bytes)
var x963Representation: Data {
// swiftlint:disable:next force_try
try! FFI.PublicKey.serialize(wrapped, format: .uncompressed)
FFI.PublicKey.serialize(wrapped, format: .uncompressed)
}

/// `02|03 || X` (33 bytes)
var compressedRepresentation: Data {
// swiftlint:disable:next force_try
try! FFI.PublicKey.serialize(wrapped, format: .compressed)
FFI.PublicKey.serialize(wrapped, format: .compressed)
}

/// `DER`
Expand Down Expand Up @@ -119,15 +117,7 @@ extension K1._PublicKeyImplementation {
static func == (lhsSelf: Self, rhsSelf: Self) -> Bool {
let lhs = lhsSelf.wrapped
let rhs = rhsSelf.wrapped
do {
return try lhs.compare(to: rhs)
} catch {
return lhs.withUnsafeBytes { lhsBytes in
rhs.withUnsafeBytes { rhsBytes in
safeCompare(lhsBytes, rhsBytes)
}
}
}
return lhs.isEqual(to: rhs)
}
}

Expand All @@ -153,8 +143,8 @@ extension K1._PublicKeyImplementation {
}

/// Negates a public key (point) on the secp256k1 curve
func negate() throws -> Self {
try Self(wrapped: wrapped.negate())
func negate() -> Self {
Self(wrapped: wrapped.negate())
}

/// Combines multiple public keys (points) on the secp256k1 curve
Expand Down
Loading