Skip to content
Open
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
39 changes: 39 additions & 0 deletions bitchat/Nostr/NostrProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ struct NostrProtocol {
}

let seal = try NostrEvent(from: sealDict)
try seal.verify()
// Unwrapped seal

return seal
Expand Down Expand Up @@ -569,6 +570,42 @@ struct NostrEvent: Codable {
let data = try encoder.encode(self)
return String(data: data, encoding: .utf8) ?? ""
}

/// Verify the event ID and Schnorr signature
func verify() throws {
if kind == NostrProtocol.EventKind.dm.rawValue {// NIP17 Rumors are unsigned and nested within Seals. skip verification for them
return
}

guard let signatureHex = sig, !signatureHex.isEmpty else {
throw NostrError.invalidSignature
}

let (calculatedId, calculatedIdHash) = try calculateEventId()

guard id == calculatedId else {
throw NostrError.invalidEventId
}

guard let pubkeyData = Data(hexString: pubkey), pubkeyData.count == 32 else {
throw NostrError.invalidPublicKey
}

guard let signatureData = Data(hexString: signatureHex), signatureData.count == 64 else {
throw NostrError.invalidSignature
}

// Verify Schnorr signature (BIP-340)
let schnorrPublicKey = try P256K.Schnorr.PublicKey(xOnly: [UInt8](pubkeyData))
let schnorrSignature = try P256K.Schnorr.Signature(dataRepresentation: signatureData)

var messageBytes = [UInt8](calculatedIdHash)
let isValid = schnorrPublicKey.verify(message: &messageBytes, signature: schnorrSignature)

if !isValid {
throw NostrError.invalidSignature
}
}
}

enum NostrError: Error {
Expand All @@ -578,6 +615,8 @@ enum NostrError: Error {
case invalidCiphertext
case signingFailed
case encryptionFailed
case invalidSignature
case invalidEventId
}

// MARK: - NIP-44 v2 helpers (XChaCha20-Poly1305 + base64url)
Expand Down