Skip to content

Why are Votes "pseudo anonymous"?

Ringo Hoffmann edited this page Oct 25, 2021 · 1 revision

Votes are stored in the database to be able to "revive" running votes after a restart of the bot. After a vote has been closed or expired, the entry is removed from the database.

A vote is stored as an gob encoded object stored as base64 encoded string into the database to ensure compatibility across multiple relational databases.

This vote object also includes all ticks stored in a map with a unique user identifier. This is done to ensure tick integrity so that a user can not tick for multiple choices and to be able to change their choice during vote lifetime.

// Vote wraps the information and current
// state of a vote and its ticks.
type Vote struct {
	ID            string
	MsgID         string
	CreatorID     string
	GuildID       string
	ChannelID     string
	Description   string
	ImageURL      string
	Expires       time.Time
	Possibilities []string
	Ticks         map[string]*Tick
}

// Tick wraps a user ID and the index of
// the selection ticked.
type Tick struct {
	UserID string
	Tick   int
}

As mentioned above, vote ticks are directly linked to users using unique identifiers. These identifiers are generated using the following function.

// HashUserID takes a userID as well as a salt value
// to generate a unique hash.
//
// Therefore, the first 12 bits are cut of to obscure
// the ID while keeping the risk of collissions low.
func HashUserID(userID string, salt []byte) (hash string, err error) {
	sid, err := snowflake.ParseString(userID)
	if err != nil {
		return
	}

	idb := big.NewInt(sid.Int64() & int64(^uint(0)>>(64-48))).Bytes()
	comb := append(idb, salt...)
	hash = fmt.Sprintf("%x", sha256.Sum256(comb))

	return
}

The last 48 bits of the user ID is hashed together with the ID of the vote as salt. That is done to ensure some level of obscurity and anonymity for the users to protect against database dump incidents.

The problem is, that if you have access to the vote data as well as to a list of user IDs of a guild where the vote was created, you can potentially use the function above to generate hashed for all users on a guild for the specific vote and then match which users ticked which option. But this is only possible for someone who has access to the database as well as to the guild where the vote was created on.