CVE-2026-32953
Description
Tillitis TKey Client package is a Go package for a TKey client. Versions 1.2.0 and below contain a critical bug in the tkeyclient Go module which causes 1 out of every 256 User Supplied Secrets (USS) to be silently ignored, producing the same Compound Device Identifier (CDI)—and thus the same key material—as if no USS is provided. This happens because a buffer index error overwrites the USS-enabled boolean with the first byte of the USS digest, so any USS whose hash starts with 0x00 is effectively discarded. This issue has been fixed in version 1.3.0. Users unable to upgrade immediately should switch to a USS whose hash does not begin with a zero byte.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/tillitis/tkeyclientGo | < 1.3.0 | 1.3.0 |
Affected products
1Patches
14954dccf0287Merge commit from fork
2 files changed · +53 −14
RELEASE.md+13 −0 modified@@ -1,5 +1,18 @@ # Release notes +## v1.3.0 + +- Version v1.2.0 and earlier had a protocol vulnerability leaving some + USSs unused. A CDI generated from use of this package might be + changed since earlier versions! Read more here: + + https://github.com/tillitis/tkeyclient/security/advisories/GHSA-4w7r-3222-8h6v + +Complete +[changelog](https://github.com/tillitis/tkeyclient/compare/v1.1.0...v1.2.0). + +- Adds new option function WithFullUss() for use with Connect(). + ## v1.2.0 - Detect new Castor USB VID/PID and older TKey models.
tkeyclient.go+40 −14 modified@@ -66,8 +66,9 @@ const ( // TillitisKey is a serial connection to a TKey and the commands that // the firmware supports. type TillitisKey struct { - speed int - conn serial.Port + speed int + conn serial.Port + forceFullUss bool } // New allocates a new TillitisKey. Use the Connect() method to @@ -77,18 +78,27 @@ func New() *TillitisKey { return tk } +// Option to set serial speed. For use with Connect. func WithSpeed(speed int) func(*TillitisKey) { return func(tk *TillitisKey) { tk.speed = speed } } +// Option to force use of 32 byte USS digest on Bellatrix and earlier. For use with Connect. +func WithFullUss() func(*TillitisKey) { + return func(tk *TillitisKey) { + tk.forceFullUss = true + } +} + // Connect connects to a TKey serial port using the provided port // device and options. func (tk *TillitisKey) Connect(port string, options ...func(*TillitisKey)) error { var err error tk.speed = SerialSpeed + tk.forceFullUss = false for _, opt := range options { opt(tk) } @@ -287,17 +297,22 @@ func (tk TillitisKey) LoadAppFromFile(fileName string, secretPhrase []byte) erro return tk.LoadApp(content, secretPhrase) } -// LoadApp loads the USS (User Supplied Secret), and contents of bin -// into the TKey, running the app after verifying that the digest -// calculated on the host is the same as the digest from the TKey. +// LoadApp sends a device app in bin and optionally a User Supplied +// Secret digest to the TKey. +// +// After succesfully sending the device app it computes a digest over +// bin and compares it to what was returned from the TKey, returning +// an error if it isn't equal. +// +// The USS is a BLAKE2s digest of the secretPhrase argument. How much +// of the digest is used differs on different hardware: +// +// - Bellatrix and earlier: last 31 bytes of the digest for backward +// compatibility reasons. // -// The USS is a 32 bytes digest hashed from secretPhrase (which is -// provided by the user). If secretPhrase is an empty slice, 32 bytes -// of zeroes will be loaded as USS. +// - Castor and later: all 32 bytes used. // -// Loading USS is always done together with loading and running an -// app, because the host program can't otherwise be sure that the -// expected USS is used. +// Returns ErrResponseStatusNotOK if firmware is not detected. func (tk TillitisKey) LoadApp(bin []byte, secretPhrase []byte) error { binLen := len(bin) if binLen > AppMaxSize { @@ -306,8 +321,13 @@ func (tk TillitisKey) LoadApp(bin []byte, secretPhrase []byte) error { le.Printf("app size: %v, 0x%x, 0b%b\n", binLen, binLen, binLen) - err := tk.loadApp(binLen, secretPhrase) + udi, err := tk.GetUDI() if err != nil { + // Probably not running firmware? Or communication error + return err + } + + if err := tk.loadApp(binLen, secretPhrase, udi.ProductID); err != nil { return err } @@ -346,7 +366,7 @@ func (tk TillitisKey) LoadApp(bin []byte, secretPhrase []byte) error { } // loadApp sets the size and USS of the app to be loaded into the TKey. -func (tk TillitisKey) loadApp(size int, secretPhrase []byte) error { +func (tk TillitisKey) loadApp(size int, secretPhrase []byte, pid uint8) error { id := 2 tx, err := NewFrameBuf(cmdLoadApp, id) if err != nil { @@ -365,7 +385,13 @@ func (tk TillitisKey) loadApp(size int, secretPhrase []byte) error { tx[6] = 1 // Hash user's phrase as USS uss := blake2s.Sum256(secretPhrase) - copy(tx[6:], uss[:]) + + if pid >= UDIPIDCastor || tk.forceFullUss { + copy(tx[7:], uss[:]) + } else { + // skip first byte for backwards compatibility, 31 byte uss + copy(tx[7:], uss[1:]) + } } Dump("LoadApp tx", tx)
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/tillitis/tkeyclient/commit/4954dccf0287657edf8d405057e134cdff9c59e8nvdPatchWEB
- github.com/tillitis/tkeyclient/security/advisories/GHSA-4w7r-3222-8h6vnvdExploitVendor AdvisoryWEB
- github.com/advisories/GHSA-4w7r-3222-8h6vghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-32953ghsaADVISORY
- github.com/tillitis/tkeyclient/releases/tag/v1.3.0nvdRelease NotesWEB
News mentions
0No linked articles in our index yet.