github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/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  }