Unify SHA256 hash and hex usages (#687)

This commit is contained in:
Islam
2025-09-30 12:47:16 +02:00
committed by GitHub
parent f5af00be88
commit 78fb3f1bf6
14 changed files with 50 additions and 82 deletions
+2 -2
View File
@@ -397,7 +397,7 @@ final class NoiseSymmetricState {
if nameData.count <= 32 { if nameData.count <= 32 {
self.hash = nameData + Data(repeating: 0, count: 32 - nameData.count) self.hash = nameData + Data(repeating: 0, count: 32 - nameData.count)
} else { } else {
self.hash = Data(SHA256.hash(data: nameData)) self.hash = nameData.sha256Hash()
} }
self.chainingKey = self.hash self.chainingKey = self.hash
} }
@@ -410,7 +410,7 @@ final class NoiseSymmetricState {
} }
func mixHash(_ data: Data) { func mixHash(_ data: Data) {
hash = Data(SHA256.hash(data: hash + data)) hash = (hash + data).sha256Hash()
} }
func mixKeyAndHash(_ inputKeyMaterial: Data) { func mixKeyAndHash(_ inputKeyMaterial: Data) {
@@ -8,7 +8,6 @@
import BitLogger import BitLogger
import Foundation import Foundation
import CryptoKit
// MARK: - Security Constants // MARK: - Security Constants
+1 -4
View File
@@ -226,10 +226,7 @@ struct NostrIdentityBridge {
} }
} }
// As a final fallback, hash the seed+msg and try again // As a final fallback, hash the seed+msg and try again
var combined = Data() let fallback = (seed + msg).sha256Hash()
combined.append(seed)
combined.append(msg)
let fallback = Data(CryptoKit.SHA256.hash(data: combined))
return try NostrIdentity(privateKeyData: fallback) return try NostrIdentity(privateKeyData: fallback)
} }
} }
+1 -4
View File
@@ -521,10 +521,7 @@ struct NostrEvent: Codable {
] as [Any] ] as [Any]
let data = try JSONSerialization.data(withJSONObject: serialized, options: [.withoutEscapingSlashes]) let data = try JSONSerialization.data(withJSONObject: serialized, options: [.withoutEscapingSlashes])
let hash = CryptoKit.SHA256.hash(data: data) return (data.sha256Fingerprint(), data.sha256Hash())
let hashData = Data(hash)
let hashHex = hash.compactMap { String(format: "%02x", $0) }.joined()
return (hashHex, hashData)
} }
func jsonString() throws -> String { func jsonString() throws -> String {
+1 -1
View File
@@ -197,7 +197,7 @@ extension Data {
offset += 16 offset += 16
// Convert 16 bytes to UUID string format // Convert 16 bytes to UUID string format
let uuid = uuidData.map { String(format: "%02x", $0) }.joined() let uuid = uuidData.hexEncodedString()
// Insert hyphens at proper positions: 8-4-4-4-12 // Insert hyphens at proper positions: 8-4-4-4-12
var result = "" var result = ""
-14
View File
@@ -1,14 +0,0 @@
import Foundation
import CryptoKit
// MARK: - Peer ID Utilities
struct PeerIDUtils {
/// Derive the stable 16-hex peer ID from a Noise static public key
static func derivePeerID(fromPublicKey publicKey: Data) -> String {
let digest = SHA256.hash(data: publicKey)
let hex = digest.map { String(format: "%02x", $0) }.joined()
return String(hex.prefix(16))
}
}
+9
View File
@@ -0,0 +1,9 @@
import Foundation
struct PeerIDUtils {
/// Derive the stable 16-hex peer ID from a Noise static public key
static func derivePeerID(fromPublicKey publicKey: Data) -> String {
String(publicKey.sha256Fingerprint().prefix(16))
}
}
+3 -14
View File
@@ -264,8 +264,7 @@ final class BLEService: NSObject {
// MARK: - Helpers: IDs, selection, and write backpressure // MARK: - Helpers: IDs, selection, and write backpressure
private func makeMessageID(for packet: BitchatPacket) -> String { private func makeMessageID(for packet: BitchatPacket) -> String {
let senderID = packet.senderID.hexEncodedString() let senderID = packet.senderID.hexEncodedString()
let digest = SHA256.hash(data: packet.payload) let digestPrefix = packet.payload.sha256Hash().prefix(4).hexEncodedString()
let digestPrefix = digest.prefix(4).map { String(format: "%02x", $0) }.joined()
return "\(senderID)-\(packet.timestamp)-\(packet.type)-\(digestPrefix)" return "\(senderID)-\(packet.timestamp)-\(packet.type)-\(digestPrefix)"
} }
@@ -777,12 +776,7 @@ final class BLEService: NSObject {
func getPeerFingerprint(_ peerID: String) -> String? { func getPeerFingerprint(_ peerID: String) -> String? {
return collectionsQueue.sync { return collectionsQueue.sync {
if let publicKey = peers[peerID]?.noisePublicKey { return peers[peerID]?.noisePublicKey?.sha256Fingerprint()
// Use the same fingerprinting method as NoiseEncryptionService/UnifiedPeerService (SHA-256 of raw key)
let hash = SHA256.hash(data: publicKey)
return hash.map { String(format: "%02x", $0) }.joined()
}
return nil
} }
} }
@@ -1705,17 +1699,12 @@ final class BLEService: NSObject {
} }
// Persist cryptographic identity and signing key for robust offline verification // Persist cryptographic identity and signing key for robust offline verification
do {
// Derive fingerprint from Noise public key
let hash = SHA256.hash(data: announcement.noisePublicKey)
let fingerprint = hash.map { String(format: "%02x", $0) }.joined()
identityManager.upsertCryptographicIdentity( identityManager.upsertCryptographicIdentity(
fingerprint: fingerprint, fingerprint: announcement.noisePublicKey.sha256Fingerprint(),
noisePublicKey: announcement.noisePublicKey, noisePublicKey: announcement.noisePublicKey,
signingPublicKey: announcement.signingPublicKey, signingPublicKey: announcement.signingPublicKey,
claimedNickname: announcement.nickname claimedNickname: announcement.nickname
) )
}
// Record this announce for lightweight rebroadcast buffer (exclude self) // Record this announce for lightweight rebroadcast buffer (exclude self)
if peerID != myPeerID { if peerID != myPeerID {
@@ -272,8 +272,7 @@ final class NoiseEncryptionService {
/// Get our identity fingerprint /// Get our identity fingerprint
func getIdentityFingerprint() -> String { func getIdentityFingerprint() -> String {
let hash = SHA256.hash(data: staticIdentityPublicKey.rawRepresentation) staticIdentityPublicKey.rawRepresentation.sha256Fingerprint()
return hash.map { String(format: "%02x", $0) }.joined()
} }
/// Get peer's public key data /// Get peer's public key data
@@ -554,7 +553,7 @@ final class NoiseEncryptionService {
private func handleSessionEstablished(peerID: String, remoteStaticKey: Curve25519.KeyAgreement.PublicKey) { private func handleSessionEstablished(peerID: String, remoteStaticKey: Curve25519.KeyAgreement.PublicKey) {
// Calculate fingerprint // Calculate fingerprint
let fingerprint = calculateFingerprint(for: remoteStaticKey) let fingerprint = remoteStaticKey.rawRepresentation.sha256Fingerprint()
// Store fingerprint mapping // Store fingerprint mapping
serviceQueue.sync(flags: .barrier) { serviceQueue.sync(flags: .barrier) {
@@ -573,11 +572,6 @@ final class NoiseEncryptionService {
} }
} }
private func calculateFingerprint(for publicKey: Curve25519.KeyAgreement.PublicKey) -> String {
let hash = SHA256.hash(data: publicKey.rawRepresentation)
return hash.map { String(format: "%02x", $0) }.joined()
}
// MARK: - Session Maintenance // MARK: - Session Maintenance
private func startRekeyTimer() { private func startRekeyTimer() {
-11
View File
@@ -10,7 +10,6 @@ import BitLogger
import Foundation import Foundation
import Combine import Combine
import SwiftUI import SwiftUI
import CryptoKit
/// Single source of truth for peer state, combining mesh connectivity and favorites /// Single source of truth for peer state, combining mesh connectivity and favorites
@MainActor @MainActor
@@ -389,13 +388,3 @@ final class UnifiedPeerService: ObservableObject, TransportPeerEventsDelegate {
}) })
} }
} }
// MARK: - Helper Extensions
extension Data {
func sha256Fingerprint() -> String {
// Implementation matches existing fingerprint generation in NoiseEncryptionService
let hash = SHA256.hash(data: self)
return hash.map { String(format: "%02x", $0) }.joined()
}
}
+1 -2
View File
@@ -1,5 +1,4 @@
import Foundation import Foundation
import CryptoKit
/// QR verification scaffolding: schema, signing, and basic challenge/response helpers. /// QR verification scaffolding: schema, signing, and basic challenge/response helpers.
final class VerificationService { final class VerificationService {
@@ -95,7 +94,7 @@ final class VerificationService {
nickname: payload.nickname, nickname: payload.nickname,
ts: payload.ts, ts: payload.ts,
nonceB64: payload.nonceB64, nonceB64: payload.nonceB64,
sigHex: sig.map { String(format: "%02x", $0) }.joined()) sigHex: sig.hexEncodedString())
let out = signed.toURLString() let out = signed.toURLString()
Cache.last = (nickname, npub, Date(), out) Cache.last = (nickname, npub, Date(), out)
return out return out
+22
View File
@@ -0,0 +1,22 @@
//
// Data+SHA256.swift
// bitchat
//
// Created by Islam on 26/09/2025.
//
import struct Foundation.Data
import struct CryptoKit.SHA256
extension Data {
/// Returns the hex representation of SHA256 hash
func sha256Fingerprint() -> String {
// Implementation matches existing fingerprint generation in NoiseEncryptionService
sha256Hash().hexEncodedString()
}
/// Returns the SHA256 hash wrapped in Data
func sha256Hash() -> Data {
Data(SHA256.hash(data: self))
}
}
+2 -9
View File
@@ -80,7 +80,6 @@
import BitLogger import BitLogger
import Foundation import Foundation
import SwiftUI import SwiftUI
import CryptoKit
import Combine import Combine
import CommonCrypto import CommonCrypto
import CoreBluetooth import CoreBluetooth
@@ -1299,10 +1298,7 @@ final class ChatViewModel: ObservableObject, BitchatDelegate {
// Called when we receive a peer's public key // Called when we receive a peer's public key
@MainActor @MainActor
func registerPeerPublicKey(peerID: String, publicKeyData: Data) { func registerPeerPublicKey(peerID: String, publicKeyData: Data) {
// Create a fingerprint from the public key (full SHA256, not truncated) let fingerprintStr = publicKeyData.sha256Fingerprint()
let fingerprintStr = SHA256.hash(data: publicKeyData)
.compactMap { String(format: "%02x", $0) }
.joined()
// Only register if not already registered // Only register if not already registered
if peerIDToPublicKeyFingerprint[peerID] != fingerprintStr { if peerIDToPublicKeyFingerprint[peerID] != fingerprintStr {
@@ -5184,12 +5180,9 @@ final class ChatViewModel: ObservableObject, BitchatDelegate {
} }
// Validate recipient // Validate recipient
if let rid = packet.recipientID { if let rid = packet.recipientID, rid.hexEncodedString() != meshService.myPeerID {
let ridHex = rid.map { String(format: "%02x", $0) }.joined()
if ridHex != meshService.myPeerID {
return return
} }
}
// Parse plaintext typed payload // Parse plaintext typed payload
guard let noisePayload = NoisePayload.decode(packet.payload) else { guard let noisePayload = NoisePayload.decode(packet.payload) else {
-6
View File
@@ -6,16 +6,10 @@
// //
import XCTest import XCTest
import CryptoKit
@testable import bitchat @testable import bitchat
final class NostrProtocolTests: XCTestCase { final class NostrProtocolTests: XCTestCase {
override func setUp() {
super.setUp()
// SecureLogger is always enabled
}
func testNIP17MessageRoundTrip() throws { func testNIP17MessageRoundTrip() throws {
// Create sender and recipient identities // Create sender and recipient identities
let sender = try NostrIdentity.generate() let sender = try NostrIdentity.generate()