github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/sigid/sigid.go (about) 1 package sigid 2 3 import ( 4 "encoding/binary" 5 "sort" 6 7 "github.com/blang/semver" 8 "github.com/keybase/client/go/kbcrypto" 9 keybase1 "github.com/keybase/client/go/protocol/keybase1" 10 ) 11 12 type YNM int 13 14 const ( 15 No YNM = 0 16 Yes YNM = 1 17 Maybe YNM = 2 18 ) 19 20 func isModernSigIDMaker(clientName string, clientVersionString string) YNM { 21 var clientVersion, cutoffVersion semver.Version 22 var err error 23 if clientName != "keybase.io go client" { 24 return Yes 25 } 26 clientVersion, err = semver.Make(clientVersionString) 27 if err != nil { 28 return Yes 29 } 30 cutoffVersion, err = semver.Make("1.0.16") 31 if err != nil { 32 panic(err) 33 } 34 if clientVersion.GT(cutoffVersion) { 35 return Yes 36 } 37 if clientVersion.EQ(cutoffVersion) { 38 return Maybe 39 } 40 return No 41 } 42 43 // isMaybeModernSigIDMakerModern is something. The general lay of the 44 // land is that SigIDs generated with Keybase prior to version 1.0.16 45 // did so incorrectly, since they didn't compute the SHA256 of the NaclSigInfo 46 // properly before computing the SigID. Those versions of Keybase at 1.0.17 47 // and after do the right thing. The problem is 1.0.16. Some clients with the 48 // 1.0.16 compute SigIDs the right way, and some do it the wrong way, 49 // (likely because Linux nightlies were going out with the 1.0.16 version). 50 // 51 // There are 77331 total signatures that were generated via 1.0.16, of which 52 // 16555 where generated the wrong way (using the <= 1.0.16 method). In data.go, 53 // we have prefixes of wrong/legacy signature IDs, that need to be fixed. To save 54 // binary space, we only include in data.go the shortest possible prefix of the 55 // wrong/legacy signature IDs that don't collide with prefixes from the group of 56 // correct signatures. So the idea is that if we compute a hash the modern way, 57 // then find its prefix in data.go, then we need to recompute it the legacy way, 58 // since that's the SigID that we use throughout the app. 59 // 60 // Background: 61 // 62 // The commit that fixes the 2016 bug is: 63 // 64 // https://github.com/keybase/client/commit/ca023e2d6f9192fd8e923f3113ae4a11cda8b53a 65 // 66 // So code on opposite sides of this bug will generate SigIDs in two 67 // different ways. 68 func isMaybeModernSigIDMakerModern(sigID keybase1.SigIDBase) bool { 69 buf := sigID.ToBytes() 70 if buf == nil { 71 return true 72 } 73 74 // Note we have prefixes of 3 sizes --- 2 bytes, 3 and 4. 75 // We check if this sigID in the 2, then 3 then 4 byte 76 // tables. If we find it, then we know it's a *legacy* 77 // SigID and needs to be computed with the legacy bug. 78 ui16 := binary.BigEndian.Uint16(buf[0:2]) 79 n := sort.Search(len(legacyHashPrefixes16), func(i int) bool { 80 return legacyHashPrefixes16[i] >= ui16 81 }) 82 if n < len(legacyHashPrefixes16) && legacyHashPrefixes16[n] == ui16 { 83 return false 84 } 85 decode3Bytes := func(b []byte) uint32 { 86 return ((uint32(b[0]) << 16) | (uint32(b[1]) << 8) | uint32(b[2])) 87 } 88 ui32 := decode3Bytes(buf) 89 n = sort.Search(len(legacyHashPrefixes24)/3, func(i int) bool { 90 return decode3Bytes(legacyHashPrefixes24[(i*3):]) >= ui32 91 }) 92 if n < len(legacyHashPrefixes24)/3 && decode3Bytes(legacyHashPrefixes24[(n*3):]) == ui32 { 93 return false 94 } 95 ui32 = binary.BigEndian.Uint32(buf[0:4]) 96 n = sort.Search(len(legacyHashPrefixes32), func(i int) bool { 97 return legacyHashPrefixes32[i] >= ui32 98 }) 99 if n < len(legacyHashPrefixes32) && legacyHashPrefixes32[n] == ui32 { 100 return false 101 } 102 return true 103 } 104 105 func ComputeSigBodyAndID(sigInfo *kbcrypto.NaclSigInfo, clientName string, clientVersion string) (body []byte, sigID keybase1.SigIDBase, err error) { 106 isModern := isModernSigIDMaker(clientName, clientVersion) 107 body, err = kbcrypto.EncodePacketToBytes(sigInfo) 108 sigID = kbcrypto.ComputeSigIDFromSigBody(body) 109 if err != nil { 110 return nil, sigID, err 111 } 112 if isModern == Yes { 113 return body, sigID, nil 114 } 115 if isModern == Maybe && isMaybeModernSigIDMakerModern(sigID) { 116 return body, sigID, nil 117 } 118 body, err = kbcrypto.EncodePacketToBytesWithOptionalHash(sigInfo, false) 119 if err != nil { 120 return nil, sigID, err 121 } 122 sigID = kbcrypto.ComputeSigIDFromSigBody(body) 123 return body, sigID, nil 124 }