mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 02:25:20 +00:00
Unify SHA256 hash and hex usages (#687)
This commit is contained in:
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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 = ""
|
||||||
|
|||||||
@@ -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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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() {
|
||||||
|
|||||||
@@ -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,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
|
||||||
|
|||||||
@@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,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()
|
||||||
|
|||||||
Reference in New Issue
Block a user