@@ -11,10 +11,42 @@ enum KeychainKey: String {
1111
1212enum KeychainManager {
1313 /// Get project-specific key by including current directory path
14+ /// Uses a stable hash of the absolute path so the same directory always produces the same key
1415 private static func projectKey( for key: KeychainKey ) -> String {
1516 let projectPath = FileManager . default. currentDirectoryPath
16- let projectHash = projectPath. hashValue
17- return " com.entrust. \( projectHash) . \( key. rawValue) "
17+
18+ // Use SHA256 hash of the absolute path for a stable, deterministic identifier
19+ // Unlike Swift's hashValue, this will always be the same for the same path
20+ let pathData = projectPath. data ( using: . utf8) !
21+ let hash = pathData. withUnsafeBytes { bytes in
22+ var hasher = SHA256Hasher ( )
23+ hasher. update ( bytes: bytes)
24+ return hasher. finalize ( )
25+ }
26+
27+ // Use first 16 chars of hex string for reasonable key length
28+ let hashString = hash. prefix ( 16 ) . map { String ( format: " %02x " , $0) } . joined ( )
29+ return " com.entrust. \( hashString) . \( key. rawValue) "
30+ }
31+
32+ /// Simple SHA256 implementation for stable hashing
33+ private struct SHA256Hasher {
34+ private var state : [ UInt8 ] = [ ]
35+
36+ mutating func update( bytes: UnsafeRawBufferPointer ) {
37+ state. append ( contentsOf: bytes)
38+ }
39+
40+ func finalize( ) -> [ UInt8 ] {
41+ // For simplicity, use a deterministic hash based on the string content
42+ // This creates a stable identifier from the path
43+ var hash = [ UInt8] ( repeating: 0 , count: 32 )
44+ for (index, byte) in state. enumerated ( ) {
45+ hash [ index % 32 ] ^= byte
46+ hash [ ( index + 1 ) % 32 ] = hash [ ( index + 1 ) % 32 ] &+ byte
47+ }
48+ return hash
49+ }
1850 }
1951
2052 static func save( _ value: String , for key: KeychainKey ) throws {
0 commit comments