Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions .git-blame-ignore-revs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Mass-reformat with swift-format; use this file to skip it in blame.
# git config blame.ignoreRevsFile .git-blame-ignore-revs
0ab9b0e4eeaf377930cf340b66fc1ba49dbfa714
12 changes: 12 additions & 0 deletions .github/workflows/ci-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@ concurrency:
cancel-in-progress: true

jobs:
lint:
name: Lint
runs-on: ubuntu-latest
container:
image: swift:6.2
timeout-minutes: 30
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Lint
run: swift format lint --recursive --configuration .swift-format --strict Sources Tests

linux_test:
name: Test Linux
runs-on: ubuntu-latest
Expand Down
3 changes: 2 additions & 1 deletion Sources/AtprotoTypesVerify/CAR/CARv1.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ public struct CARv1: Sendable {
throw Atproto.Repo.ProofError.badCARHeader
}
guard version == 1 else {
throw Atproto.Repo.ProofError.unsupportedCARVersion(UInt64(clamping: version))
throw Atproto.Repo.ProofError.unsupportedCARVersion(
UInt64(clamping: version))
}
guard let rawRoots = header["roots"]?.arrayValue else {
throw Atproto.Repo.ProofError.badCARHeader
Expand Down
6 changes: 3 additions & 3 deletions Sources/AtprotoTypesVerify/ContentIdentifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public struct ContentIdentifier: Sendable, Hashable {
}

//multicodec sha2-256, and the only digest atproto uses
static let sha2_256: UInt64 = 0x12
static let sha256: UInt64 = 0x12
static let digestLength = 32

public let codec: Codec
Expand All @@ -44,7 +44,7 @@ public struct ContentIdentifier: Sendable, Hashable {
public var bytes: Data {
var out = Self.varint(1)
out += Self.varint(codec.rawValue)
out += Self.varint(Self.sha2_256)
out += Self.varint(Self.sha256)
out += Self.varint(UInt64(Self.digestLength))
out += digest
return Data(out)
Expand Down Expand Up @@ -86,7 +86,7 @@ public struct ContentIdentifier: Sendable, Hashable {
}

let hash = try reader.readUnsignedVarint()
guard hash == Self.sha2_256 else {
guard hash == Self.sha256 else {
throw Atproto.Repo.ProofError.unsupportedHash(hash)
}

Expand Down
3 changes: 2 additions & 1 deletion Sources/AtprotoTypesVerify/DAGCBOR/DAGCBORDecoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@ public enum DAGCBORDecoder {
throw Atproto.Repo.ProofError.duplicateMapKey(key)
}
guard canonicallyPrecedes(previousKey, key) else {
throw Atproto.Repo.ProofError.unorderedMapKeys(previousKey, key)
throw Atproto.Repo.ProofError.unorderedMapKeys(
previousKey, key)
}
}
previousKey = key
Expand Down
3 changes: 2 additions & 1 deletion Sources/AtprotoTypesVerify/ProofError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ extension Atproto.Repo {
case .unsupportedSimpleValue(let v): "Unsupported CBOR simple value \(v)"
case .nonStringMapKey: "DAG-CBOR map keys must be strings"
case .duplicateMapKey(let k): "Duplicate map key \(k)"
case .unorderedMapKeys(let a, let b): "Map keys out of order: \(a) before \(b)"
case .unorderedMapKeys(let a, let b):
"Map keys out of order: \(a) before \(b)"
case .badCIDLink: "Malformed CID link"
case .invalidUTF8: "Invalid UTF-8 in text string"
case .nestingTooDeep: "DAG-CBOR nesting too deep"
Expand Down
12 changes: 8 additions & 4 deletions Sources/AtprotoTypesVerify/Signing/RepoSigningKey.swift
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ public struct RepoSigningKey: Sendable {
var reader = ByteReader(decoded)
let code = try reader.readUnsignedVarint()
guard let curve = Curve.named(code) else {
throw Atproto.Repo.ProofError.unsupportedCurve("multicodec 0x\(String(code, radix: 16))")
throw Atproto.Repo.ProofError.unsupportedCurve(
"multicodec 0x\(String(code, radix: 16))")
}

let pointLength = reader.remaining
Expand All @@ -126,14 +127,16 @@ public struct RepoSigningKey: Sendable {
case .secp256k1:
//same low-S rule as p256, against this curve's own order — atproto
//requires it network-wide, not just for the curve swift-crypto backs
guard Self.isLowS(Array(signature.suffix(32)), order: Self.secp256k1Order) else {
guard Self.isLowS(Array(signature.suffix(32)), order: Self.secp256k1Order)
else {
throw Atproto.Repo.ProofError.nonCanonicalSignature
}

let digest = SHA256.hash(data: message)
guard
Secp256k1.ECDSA.verify(
signature: signature, digest: Data(digest), compressedPublicKey: compressedPoint)
signature: signature, digest: Data(digest),
compressedPublicKey: compressedPoint)
else {
throw Atproto.Repo.ProofError.signatureDidNotVerify
}
Expand All @@ -152,7 +155,8 @@ public struct RepoSigningKey: Sendable {
key = try P256.Signing.PublicKey(
compressedRepresentation: compressedPoint
)
parsed = try P256.Signing.ECDSASignature(rawRepresentation: signature)
parsed = try P256.Signing.ECDSASignature(
rawRepresentation: signature)
} catch {
throw Atproto.Repo.ProofError.badMultibaseKey
}
Expand Down
15 changes: 11 additions & 4 deletions Sources/AtprotoTypesVerify/Signing/Secp256k1/ECDSA.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ extension Secp256k1 {
///point at infinity all collapse to a plain refusal rather than a
///distinguished error, matching how Wycheproof's "other invalid"
///bucket expects these to be indistinguishable from a wrong signature.
static func verify(signature: Data, digest: Data, compressedPublicKey: Data) -> Bool {
static func verify(signature: Data, digest: Data, compressedPublicKey: Data) -> Bool
{
//not reachable from `RepoSigningKey.verify` today (always a
//32-byte SHA-256 output) — guarded anyway, because
//`Scalar(reducingBigEndian:)` folds any other length to zero,
Expand All @@ -30,13 +31,19 @@ extension Secp256k1 {
//any t, R = t·G, r = R.x mod n, s = r·t⁻¹ mod n.
guard digest.count == 32 else { return false }
guard signature.count == 64 else { return false }
guard let r = Scalar(canonicalBigEndian: Array(signature.prefix(32))), !r.isZero else {
guard let r = Scalar(canonicalBigEndian: Array(signature.prefix(32))),
!r.isZero
else {
return false
}
guard let s = Scalar(canonicalBigEndian: Array(signature.suffix(32))), !s.isZero else {
guard let s = Scalar(canonicalBigEndian: Array(signature.suffix(32))),
!s.isZero
else {
return false
}
guard let publicKey = Point(compressed: compressedPublicKey) else {
return false
}
guard let publicKey = Point(compressed: compressedPublicKey) else { return false }

let z = Scalar(reducingBigEndian: Array(digest))
let w = s.inverted
Expand Down
8 changes: 6 additions & 2 deletions Sources/AtprotoTypesVerify/Signing/Secp256k1/Field.swift
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,9 @@ extension Secp256k1 {
}

var negated: Field {
isZero ? self : Field(unchecked: Limbs256.subtracting(Self.modulus, value).0)
isZero
? self
: Field(unchecked: Limbs256.subtracting(Self.modulus, value).0)
}

///Folds a 512-bit product down using 2^256 ≡ 2^32 + 977 (mod p): each
Expand All @@ -108,7 +110,9 @@ extension Secp256k1 {
var buffer = wide

var pass = 0
while !(buffer[4] == 0 && buffer[5] == 0 && buffer[6] == 0 && buffer[7] == 0) {
while !(buffer[4] == 0 && buffer[5] == 0 && buffer[6] == 0
&& buffer[7] == 0)
{
pass += 1
precondition(pass < 8, "field reduction did not converge")

Expand Down
11 changes: 8 additions & 3 deletions Sources/AtprotoTypesVerify/Signing/Secp256k1/Limbs256.swift
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,17 @@ extension Secp256k1 {
///believed so: a hand-fused add can undercount by exactly one in a
///narrow edge case (large product, both inputs already near
///`UInt64.max`), and that is not a bug a quick reading catches.
static func rippleAdd(_ value: UInt64, at position: Int, into accumulator: inout [UInt64]) {
static func rippleAdd(
_ value: UInt64, at position: Int, into accumulator: inout [UInt64]
) {
var carry = value
var index = position
while carry != 0 {
precondition(index < accumulator.count, "carry overflowed the accumulator")
let (sum, overflow) = accumulator[index].addingReportingOverflow(carry)
precondition(
index < accumulator.count,
"carry overflowed the accumulator")
let (sum, overflow) = accumulator[index].addingReportingOverflow(
carry)
accumulator[index] = sum
carry = overflow ? 1 : 0
index += 1
Expand Down
19 changes: 14 additions & 5 deletions Sources/AtprotoTypesVerify/Signing/Secp256k1/Point.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,26 @@ extension Secp256k1 {
static let generator: Point = {
let x = Field(
bigEndian: Array(
hex: "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798"))!
hex:
"79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798"
))!
let y = Field(
bigEndian: Array(
hex: "483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8"))!
hex:
"483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8"
))!
return .jacobian(x: x, y: y, z: Field.one)
}()

// MARK: - Doubling and addition

///Standard Jacobian doubling for a = 0 curves.
func doubled() -> Point {
guard case .affinePoint(let x1, let y1, let z1) = self else { return .infinity }
if y1.isZero { return .infinity } //a point of order 2, which secp256k1 has none of except this degenerate input
guard case .affinePoint(let x1, let y1, let z1) = self else {
return .infinity
}
//a point of order 2, which secp256k1 has none of except this degenerate input
if y1.isZero { return .infinity }

let a = x1.squared()
let b = y1.squared()
Expand Down Expand Up @@ -107,7 +114,9 @@ extension Secp256k1 {
}

var negated: Point {
guard case .affinePoint(let x, let y, let z) = self else { return .infinity }
guard case .affinePoint(let x, let y, let z) = self else {
return .infinity
}
return .jacobian(x: x, y: y.negated, z: z)
}

Expand Down
4 changes: 3 additions & 1 deletion Sources/AtprotoTypesVerify/Signing/Secp256k1/Scalar.swift
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,9 @@ extension Secp256k1 {
var buffer = wide

var pass = 0
while !(buffer[4] == 0 && buffer[5] == 0 && buffer[6] == 0 && buffer[7] == 0) {
while !(buffer[4] == 0 && buffer[5] == 0 && buffer[6] == 0
&& buffer[7] == 0)
{
pass += 1
precondition(pass < 12, "scalar reduction did not converge")

Expand Down
10 changes: 8 additions & 2 deletions Sources/AtprotoTypesVerifyMocks/RepoFixture.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ extension P256.Signing.PublicKey: RepoFixturePublicKey {

extension P256.Signing.PrivateKey: RepoFixtureSigningKey {
public func repoFixtureSignature(for message: Data) throws -> Data {
RepoFixture.lowS(try signature(for: message).rawRepresentation, order: RepoSigningKey.p256Order)
RepoFixture.lowS(
try signature(for: message).rawRepresentation,
order: RepoSigningKey.p256Order)
}
}

Expand Down Expand Up @@ -129,7 +131,11 @@ public enum RepoFixture {
.map([
("k", .bytes(Data(key.dropFirst(shared)))),
("p", .integer(Int64(shared))),
("t", subtrees[entry.key].map { DAGCBORValue.link($0) } ?? .null),
(
"t",
subtrees[entry.key].map { DAGCBORValue.link($0) }
?? .null
),
("v", .link(entry.value)),
])
)
Expand Down
5 changes: 4 additions & 1 deletion Tests/AtprotoTypesVerifyTests/DAGCBORTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,10 @@ struct DAGCBORTests {
let value = DAGCBORValue.map([
("did", .string("did:plc:example")),
("rev", .string("3lbw")),
("data", .link(try ContentIdentifier.compute(codec: .dagCBOR, block: Data()))),
(
"data",
.link(try ContentIdentifier.compute(codec: .dagCBOR, block: Data()))
),
("prev", .null),
("nested", .array([.integer(-1), .bytes(Data([0, 1, 2])), .bool(true)])),
("version", .integer(3)),
Expand Down
3 changes: 2 additions & 1 deletion Tests/AtprotoTypesVerifyTests/MerkleSearchTreeTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ struct MerkleSearchTreeTests {

for (key, expected) in tree.values {
#expect(
try MerkleSearchTree.find(key: key, root: tree.root, in: tree.archive)
try MerkleSearchTree.find(
key: key, root: tree.root, in: tree.archive)
== expected,
"looking up \(key)"
)
Expand Down
22 changes: 17 additions & 5 deletions Tests/AtprotoTypesVerifyTests/RepoProofVerifierTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,16 @@ struct RepoProofVerifierTests {
)
if let mutateSignature, let existing = commit["sig"]?.bytesValue {
commit = commit.removing(key: "sig")
guard case .map(let fields) = commit else { fatalError("unreachable") }
guard case .map(let fields) = commit else {
fatalError("unreachable")
}
commit = .map(
fields + [(key: "sig", value: .bytes(mutateSignature(existing)))]
fields + [
(
key: "sig",
value: .bytes(mutateSignature(existing))
)
]
)
}
let commitBlock = try RepoFixture.block(commit)
Expand Down Expand Up @@ -272,13 +279,18 @@ struct RepoProofVerifierTests {
let node = try RepoFixture.block(
RepoFixture.node(
entries: [
(key: RepoProofVerifierTests.path.mstKey, value: recordBlock.cid)
(
key: RepoProofVerifierTests.path.mstKey,
value: recordBlock.cid
)
]
)
)
let commit = try RepoFixture.commit(did: did, dataRoot: node.cid, signedBy: signer)
let commit = try RepoFixture.commit(
did: did, dataRoot: node.cid, signedBy: signer)
let commitBlock = try RepoFixture.block(commit)
return RepoFixture.car(root: commitBlock.cid, blocks: [recordBlock, node, commitBlock])
return RepoFixture.car(
root: commitBlock.cid, blocks: [recordBlock, node, commitBlock])
}

func document() throws -> Atproto.DIDDocument {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,14 @@ struct Secp256k1ECDSADifferentialTests {
let signature = key.signature(for: message).compactRepresentation

let oracleAccepts = key.publicKey.isValidSignature(
try P256K.Signing.ECDSASignature(compactRepresentation: signature), for: message)
try P256K.Signing.ECDSASignature(compactRepresentation: signature),
for: message)
#expect(oracleAccepts) //sanity: P256K agrees with itself

let digest = Crypto.SHA256.hash(data: message)
let oursAccepts = Secp256k1.ECDSA.verify(
signature: signature, digest: Data(digest), compressedPublicKey: publicKeyBytes)
signature: signature, digest: Data(digest),
compressedPublicKey: publicKeyBytes)
#expect(oursAccepts)
}
}
Expand Down Expand Up @@ -92,9 +94,11 @@ struct Secp256k1ECDSADifferentialTests {
if let parsedSignature = try? P256K.Signing.ECDSASignature(
compactRepresentation: testCase.signature),
let parsedKey = try? P256K.Signing.PublicKey(
dataRepresentation: testCase.key, format: .compressed)
dataRepresentation: testCase.key,
format: .compressed)
{
parsedKey.isValidSignature(parsedSignature, for: testCase.message)
parsedKey.isValidSignature(
parsedSignature, for: testCase.message)
} else {
false
}
Expand Down
Loading
Loading