This is an automated email from the ASF dual-hosted git repository.

gstein pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/steve.git


The following commit(s) were added to refs/heads/trunk by this push:
     new a4cbb8e  align around "Person" terminology
a4cbb8e is described below

commit a4cbb8eaf9c66b761643cf3e333706679c204bc3
Author: Greg Stein <[email protected]>
AuthorDate: Sun Jun 5 08:22:47 2022 -0500

    align around "Person" terminology
---
 v3/README.md         | 56 +++++++++++++++---------------
 v3/schema.sql        | 20 +++++------
 v3/steve/crypto.py   | 10 +++---
 v3/steve/election.py | 98 ++++++++++++++++++++++++++--------------------------
 4 files changed, 92 insertions(+), 92 deletions(-)

diff --git a/v3/README.md b/v3/README.md
index 20231ef..8428d9f 100644
--- a/v3/README.md
+++ b/v3/README.md
@@ -3,10 +3,10 @@
 ## History
 
 v1 was a set of command-line tools to run voting on a host server, with the
-participants ssh'ing to that server to cast votes.
+people ssh'ing to that server to cast votes.
 
 v2 was a webapp and data server to run the voting process, utilizing LDAP
-authentication for the participants.
+authentication for the people voting.
 
 v3 is intended (primarily) to revamp the data model/storage and the webui
 frameworks, using more recent technologies for greater leverage.
@@ -18,9 +18,9 @@ v2 is the initial guide for a data model, to be used by v3.
 The top-level item is an **Election**, and our design-point (in terms of scale)
 is to manage hundreds of these.
 
-Each **Election** contains some simple metadata, along with **Participants**
+Each **Election** contains some simple metadata, along with **Persons**
 (numbering in the hundreds) that are on record to vote, a set of **Issues**
-(also, hundreds) on the ballot for the participants to vote upon, and a small
+(also, hundreds) on the ballot for the people to vote upon, and a small
 set of **Vote Monitors** (single digit count) for the Election.
 
 This will produce a set of **Votes** (tens of thousands).
@@ -36,13 +36,13 @@ be looking for?
 * Alice voting as Bob (see below)
 * Alice stuffing ballots (eg. as a person not on Record; should be impossible)
 
-Each participant receives a **token** to represent themself during voting. The
-token is regarded as a **shared secret** between STeVe and the Participant.
+Each person receives a **token** to represent themself during voting. The
+token is regarded as a **shared secret** between STeVe and the Person.
 
 Note: this token could be used internally, and the **shared secret** would be
-the Participant's LDAP password. This *may* create undesired data in access 
logs,
+the Person's LDAP password. This *may* create undesired data in access logs,
 which could be solved by custom config to **omit** the authenticated user from
-the logs. And/or, a Participant could sign in to retrieve a link that embeds
+the logs. And/or, a Person could sign in to retrieve a link that embeds
 their token, and that link requires no authentication (note: would need to
 ensure that **all** browsers obey path-based directives on when to send
 credentials; we'd only want creds for retrieving the token/link, but for them
@@ -51,28 +51,28 @@ to be dropped during voting the ballot).
 Given the above, if Alice is able to discover Bob's token, then she can vote
 as if she were Bob. This may be discoverable by aberrant repeat voting by Bob.
 
-Since votes may only be performed by those on record, with voter tokens, it
+Since votes may only be performed by those on record, with person tokens, it
 does not seem possible for Alice to stuff the ballot box.
 
 ?? other attack vectors? can Monitors help with any?
 
 ## Hashes and Anonymity
 
-The participants must be as anonymous as possible. The goal is that 
Participants
-and Monitors cannot "unmask" any Participant in the election, nor the Votes 
that
+The Persons must be as anonymous as possible. The goal is that Persons
+and Monitors cannot "unmask" any Person in the election, nor the Votes that
 they have cast.
 
 It is presumed that the "root" users of the team operating the software would 
be
-able to unmask Participants and view their votes.
+able to unmask Persons and view their votes.
 
 Cryptographic-grade hashes are used as identifiers to create anonymity.
 
 ## Integrity
 
-When an Election is "opened for voting", all Participants, Issues, and Monitors
+When an Election is "opened for voting", all Persons, Issues, and Monitors
 will be used to construct a singular hash that identifies the precise state of
 the Election. This hash is used to prevent any post-opening tampering of the
-voters of record, the ballot, or those watching for such tampering.
+Persons of record, the ballot, or those watching for such tampering.
 
 ## Implementation
 
@@ -80,7 +80,7 @@ Some notes on implementation, hashing, storage, at-rest 
encryption, etc.
 
 ```
 ElectionID := 32 bits
-VoterID := availid from iclas.txt
+PersonID := availid from iclas.txt
 IssueID := [-a-zA-Z0-9]+
 
 Election-data := TBD
@@ -88,15 +88,15 @@ Issue-data := TBD
 BLOCK := Election-data + sorted(Issue-Data)
 OpenedKey := Hash(BLOCK, Salt(each-election))
 
-Voters := Map<VoterID, Salt(each-voter)>
-VoterToken := Hash(OpenedKey + VoterID, Salt(each-voter))
+Personss := Map<PersonID, Salt(each-person)>
+PersonToken := Hash(OpenedKey + PersonID, Salt(each-person))
 
 Issues := Map<IssueID, Salt(each-issue)>
 IssueToken := Hash(OpenedKey + IssueID, Salt(each-issue))
 
 votestring = TBD; padding TBD
-VoteKey := Hash(VoterToken + IssueToken, Salt(each-vote))
-Vote := Tuple[ VoterToken, IssueToken, Salt(each-vote), Encrypt(VoteKey, 
votestring) ]
+VoteKey := Hash(PersonToken + IssueToken, Salt(each-vote))
+Vote := Tuple[ PersonToken, IssueToken, Salt(each-vote), Encrypt(VoteKey, 
votestring) ]
 ```
 
 When an **Election** is Opened for voting, the `OpenedKey` is calculated, 
stored,
@@ -120,8 +120,8 @@ the 32 bytes needed for a Fernet key.
 
 ### Storage and Transmission
 
-**IMPORTANT**: the `VoterToken` and `IssueToken` should never be
-stored in a way that ties them to the VoterID and IssueID.  The
+**IMPORTANT**: the `PersonToken` and `IssueToken` should never be
+stored in a way that ties them to the PersonID and IssueID.  The
 `VoteKey` should never be stored. Instead, the `Salt(xx)` values
 are stored, and the tokens/key are computed when needed.
 
@@ -130,18 +130,18 @@ by storing the result. Any attacker must perform the 
work. During normal
 operation of the voting system, each call of the `Hash()` function should be
 within human-reasonable time limits (but unreasonable to perform in bulk).
 
-Note that `VoterToken` and `IssueToken` are stored as part of each `Vote`,
-but those tokens provide no easy mapping back to a voter or issue.
+Note that `PersonToken` and `IssueToken` are stored as part of each `Vote`,
+but those tokens provide no easy mapping back to a person or issue.
 
-The `VoterToken` is normally emailed to the Participant. If it is not
+The `PersonToken` is normally emailed to the Person. If it is not
 emailed, then LDAP authentication would be used, and the server will
 compute it from the authenticated credentials.
 
-Since `VoterToken` *may* be used by the Participant, via URL, to perform
+Since `PersonToken` *may* be used by the Person, via URL, to perform
 their voting, it must be "URL safe". If LDAP authn mode is used, then
-the `VoterToken` will never be encoded for humans.
+the `PersonToken` will never be encoded for humans.
 
-The `ElectionID` is also visible to Participants, and will be encoded
+The `ElectionID` is also visible to Persons, and will be encoded
 as eight (8) hex digits, just like STeVe v2.
 
 ### (Re)Tally Process
@@ -156,7 +156,7 @@ as eight (8) hex digits, just like STeVe v2.
 Notes: be wary of repeats; collect STV votestrings, for passing in-bulk
 to the STV algorithm.
 
-Note that the tally process does not require unmasking the Participant.
+Note that the tally process does not require unmasking the Person.
 
 
 
diff --git a/v3/schema.sql b/v3/schema.sql
index dc3cef7..67e7a3c 100644
--- a/v3/schema.sql
+++ b/v3/schema.sql
@@ -29,7 +29,7 @@
 
    An Election has three states:
 
-     1. Editable. The election is being set up. Issues and voters of
+     1. Editable. The election is being set up. Issues and persons of
         record can be added, edited, and deleted. The Election's title
         may be changed (EID is fixed, however).
         DEFINITION: salt and opened_key are NULL. closed is n/a.
@@ -102,18 +102,18 @@ CREATE TABLE ISSUES (
 
 /* The set of people "on record" for this Election. Only these people
    may vote.  */
-CREATE TABLE RECORD (
+CREATE TABLE PERSON (
 
-    /* An id assigned to the user (eg. an LDAP username).  */
-    rid  TEXT PRIMARY KEY NOT NULL,
+    /* An id assigned to the person (eg. an LDAP username).  */
+    pid  TEXT PRIMARY KEY NOT NULL,
 
-    /* Optional human-readable name for this user.  */
+    /* Optional human-readable name for this person.  */
     name  TEXT,
 
     /* How to contact this person (ie. to send a ballot link).  */
     email  TEXT NOT NULL,
 
-    /* A salt value to use for hashing this Participant. 16 bytes.
+    /* A salt value to use for hashing this Person. 16 bytes.
        This will be NULL until the Election is opened.  */
     salt  BLOB
 
@@ -122,7 +122,7 @@ CREATE TABLE RECORD (
 /* --------------------------------------------------------------------- */
 
 /* The registered votes, once the Election has been opened. Note that
-   duplicates of (voter, issue) may occur, as re-voting is allowed. Only
+   duplicates of (person, issue) may occur, as re-voting is allowed. Only
    the latest is used.  */
 CREATE TABLE VOTES (
 
@@ -132,8 +132,8 @@ CREATE TABLE VOTES (
        Note: an integer primary key is an alias for _ROWID_.  */
     vid  INTEGER PRIMARY KEY AUTOINCREMENT,
 
-    /* A hashed token representing a single Participant.  32 bytes.  */
-    voter_token  BLOB NOT NULL,
+    /* A hashed token representing a single Person.  32 bytes.  */
+    person_token  BLOB NOT NULL,
 
     /* A hashed token representing an issue.  32 bytes.  */
     issue_token  BLOB NOT NULL,
@@ -146,7 +146,7 @@ CREATE TABLE VOTES (
 
     ) STRICT;
 
-CREATE INDEX I_BY_VOTER ON VOTES (voter_token);
+CREATE INDEX I_BY_PERSON ON VOTES (person_token);
 CREATE INDEX I_BY_ISSUE ON VOTES (issue_token);
 
 /* --------------------------------------------------------------------- */
diff --git a/v3/steve/crypto.py b/v3/steve/crypto.py
index 9c7d33d..ca969c0 100644
--- a/v3/steve/crypto.py
+++ b/v3/steve/crypto.py
@@ -42,28 +42,28 @@ def gen_opened_key(edata: bytes, salt: bytes) -> bytes:
 
 
 def gen_token(opened_key: bytes, value: str, salt: bytes) -> bytes:
-    "Generate a voter or issue token."
+    "Generate a person or issue token."
     return _hash(opened_key + value.encode(), salt)
 
 
 ### fix return type, to be a tuple
-def create_vote(voter_token: bytes,
+def create_vote(person_token: bytes,
                 issue_token: bytes,
                 votestring: str) -> bytes:
     "Create a vote tuple, to record the VOTESTRING."
     salt = gen_salt()
-    key = _hash(voter_token + issue_token, salt)
+    key = _hash(person_token + issue_token, salt)
     b64key = base64.urlsafe_b64encode(key)
     f = cryptography.fernet.Fernet(b64key)
     return salt, f.encrypt(votestring.encode())
 
 
-def decrypt_votestring(voter_token: bytes,
+def decrypt_votestring(person_token: bytes,
                        issue_token: bytes,
                        salt: bytes,
                        token: bytes) -> str:
     "Decrypt TOKEN into a VOTESTRING."
-    key = _hash(voter_token + issue_token, salt)
+    key = _hash(person_token + issue_token, salt)
     b64key = base64.urlsafe_b64encode(key)
     f = cryptography.fernet.Fernet(b64key)
     return f.decrypt(token).decode()
diff --git a/v3/steve/election.py b/v3/steve/election.py
index 81eda0f..6cf13c3 100644
--- a/v3/steve/election.py
+++ b/v3/steve/election.py
@@ -34,8 +34,8 @@ class Election:
         # Construct cursors for all operations.
         self.c_salt_issue = self.db.add_statement(
             'UPDATE ISSUES SET salt = ? WHERE _ROWID_ = ?')
-        self.c_salt_record = self.db.add_statement(
-            'UPDATE RECORD SET salt = ? WHERE _ROWID_ = ?')
+        self.c_salt_person = self.db.add_statement(
+            'UPDATE PERSON SET salt = ? WHERE _ROWID_ = ?')
         self.c_open = self.db.add_statement(
             'UPDATE METADATA SET salt = ?, opened_key = ?')
         self.c_close = self.db.add_statement(
@@ -48,21 +48,21 @@ class Election:
                  type=excluded.type,
                  kv=excluded.kv
             ''')
-        self.c_add_record = self.db.add_statement(
-            '''INSERT INTO RECORD VALUES (?, ?, ?, ?)
+        self.c_add_person = self.db.add_statement(
+            '''INSERT INTO PERSON VALUES (?, ?, ?, ?)
                ON CONFLICT DO UPDATE SET
                  name=excluded.name,
                  email=excluded.email
             ''')
         self.c_delete_issue = self.db.add_statement(
             'DELETE FROM ISSUES WHERE iid = ?')
-        self.c_delete_record = self.db.add_statement(
-            'DELETE FROM RECORD WHERE rid = ?')
+        self.c_delete_person = self.db.add_statement(
+            'DELETE FROM PERSON WHERE pid = ?')
         self.c_add_vote = self.db.add_statement(
             'INSERT INTO VOTES VALUES (NULL, ?, ?, ?, ?)')
         self.c_has_voted = self.db.add_statement(
             '''SELECT 1 FROM VOTES
-               WHERE voter_token = ? AND issue_token = ?
+               WHERE person_token = ? AND issue_token = ?
                LIMIT 1
             ''')
 
@@ -71,12 +71,12 @@ class Election:
             'SELECT * FROM METADATA')
         self.q_issues = self.db.add_query('issues',
             'SELECT * FROM ISSUES ORDER BY iid')
-        self.q_record = self.db.add_query('record',
-            'SELECT * FROM RECORD ORDER BY rid')
+        self.q_person = self.db.add_query('person',
+            'SELECT * FROM PERSON ORDER BY pid')
         self.q_get_issue = self.db.add_query('issues',
             'SELECT * FROM ISSUES WHERE iid = ?')
-        self.q_get_record = self.db.add_query('record',
-            'SELECT * FROM RECORD WHERE rid = ?')
+        self.q_get_person = self.db.add_query('person',
+            'SELECT * FROM PERSON WHERE pid = ?')
         self.q_by_issue = self.db.add_query('votes',
             'SELECT * FROM VOTES WHERE issue_token = ? ORDER BY _ROWID_')
 
@@ -112,14 +112,14 @@ class Election:
 
         self.q_issues.perform()
         # Use an f-string to render "None" if a column is NULL.
-        idata = ''.join(f'{r.iid}{r.title}{r.description}{r.type}{r.kv}'
-                        for r in self.q_issues.fetchall())
+        idata = ''.join(f'{i.iid}{i.title}{i.description}{i.type}{i.kv}'
+                        for i in self.q_issues.fetchall())
 
-        self.q_record.perform()
-        rdata = ''.join(r.rid + r.email
-                        for r in self.q_record.fetchall())
+        self.q_person.perform()
+        pdata = ''.join(p.pid + p.email
+                        for p in self.q_person.fetchall())
 
-        return (mdata + idata + rdata).encode()
+        return (mdata + idata + pdata).encode()
 
     def close(self):
         "Close an election."
@@ -131,7 +131,7 @@ class Election:
         self.c_close.perform()
 
     def add_salts(self):
-        "Set the SALT column in the ISSUES and RECORD tables."
+        "Set the SALT column in the ISSUES and PERSON tables."
 
         # The Election should be editable.
         assert self.is_editable()
@@ -153,7 +153,7 @@ class Election:
                 mod_cursor.perform((salt, r[0]))
 
         for_table('issues', self.c_salt_issue)
-        for_table('record', self.c_salt_record)
+        for_table('person', self.c_salt_person)
 
     def get_issue(self, iid):
         "Return TITLE, DESCRIPTION, TYPE, and KV for issue IID."
@@ -183,53 +183,53 @@ class Election:
         self.q_issues.perform()
         return [ row[:5] for row in self.q_issues.fetchall() ]
 
-    def get_participant(self, rid):
-        "Return NAME, EMAIL for Participant on record RID."
+    def get_person(self, pid):
+        "Return NAME, EMAIL for Person identified by PID."
 
-        # NEVER return record.salt
-        record = self.q_get_record.first_row((rid,))
-        return record.name, record.email
+        # NEVER return person.salt
+        person = self.q_get_person.first_row((pid,))
+        return person.name, person.email
 
-    def add_participant(self, rid, name, email):
-        "Add or update a Participant (voter of record) designated by RID."
+    def add_person(self, pid, name, email):
+        "Add or update a Person designated by PID."
         assert self.is_editable()
 
         # If we ADD, then SALT will be NULL. If we UPDATE, then it will not
         # be touched (it should be NULL).
-        self.c_add_record.perform((rid, name, email, None))
+        self.c_add_person.perform((pid, name, email, None))
 
-    def delete_participant(self, rid):
-        "Delete the Participant designated by RID."
+    def delete_person(self, pid):
+        "Delete the Person designated by PID."
         assert self.is_editable()
 
-        self.c_delete_record.perform((rid,))
+        self.c_delete_person.perform((pid,))
 
-    def list_participants(self):
-        "Return ordered (RID, NAME, EMAIL) for each Participant in RECORD."
-        self.q_record.perform()
-        return [ row[:3] for row in self.q_record.fetchall() ]
+    def list_persons(self):
+        "Return ordered (PID, NAME, EMAIL) for each Person."
+        self.q_person.perform()
+        return [ row[:3] for row in self.q_prson.fetchall() ]
 
-    def add_vote(self, rid, iid, votestring):
-        "Add VOTESTRING as the (latest) vote by RID for IID."
+    def add_vote(self, pid, iid, votestring):
+        "Add VOTESTRING as the (latest) vote by PID for IID."
 
         # The Election should be open.
         assert self.is_open()
 
         md = self.q_metadata.first_row()
-        record = self.q_get_record.first_row((rid,))
+        person = self.q_get_person.first_row((pid,))
         issue = self.q_get_issue.first_row((iid,))
 
         ### validate VOTESTRING for ISSUE.TYPE voting
 
-        voter_token = crypto.gen_token(md.opened_key, rid, record.salt)
-        #print('VOTER:', rid, record.salt, voter_token)
+        person_token = crypto.gen_token(md.opened_key, pid, person.salt)
+        #print('PERSON:', pid, person.salt, person_token)
         issue_token = crypto.gen_token(md.opened_key, iid, issue.salt)
         #print('ISSUE:', iid, issue.salt, issue_token)
 
-        salt, token = crypto.create_vote(voter_token, issue_token, votestring)
+        salt, token = crypto.create_vote(person_token, issue_token, votestring)
         #print('SALT:', salt)
         #print('TOKEN:', token)
-        self.c_add_vote.perform((voter_token, issue_token, salt, token))
+        self.c_add_vote.perform((person_token, issue_token, salt, token))
 
     def gather_issue_votes(self, iid):
         "Return a list of votestrings for a given ISSUE-ID."
@@ -242,28 +242,28 @@ class Election:
         issue_token = crypto.gen_token(md.opened_key, iid, issue.salt)
 
         # Use this dict to retain "most recent" votes.
-        dedup = { }  # (VOTER_TOKEN, ISSUE_TOKEN) : VOTESTRING
+        dedup = { }  # (PERSON_TOKEN, ISSUE_TOKEN) : VOTESTRING
 
         self.q_by_issue.perform((issue_token,))
         for row in self.q_by_issue.fetchall():
             votestring = crypto.decrypt_votestring(
-                row.voter_token, issue_token, row.salt, row.token)
-            dedup[row.voter_token, row.issue_token] = votestring
+                row.person_token, issue_token, row.salt, row.token)
+            dedup[row.person_token, row.issue_token] = votestring
 
         # Make sure the votes are not in database-order.
         votes = list(dedup.values())
         crypto.shuffle(votes)  # in-place
         return votes
 
-    def has_voted_upon(self, rid):
+    def has_voted_upon(self, pid):
         "Return {ISSUE-ID: BOOL} stating what has been voted upon."
 
         # The Election should be open.
         assert self.is_open()
 
         md = self.q_metadata.first_row()
-        record = self.q_get_record.first_row((rid,))
-        voter_token = crypto.gen_token(md.opened_key, rid, record.salt)
+        person = self.q_get_person.first_row((pid,))
+        person_token = crypto.gen_token(md.opened_key, pid, person.salt)
 
         voted_upon = { }
 
@@ -274,11 +274,11 @@ class Election:
                                            issue.salt)
 
             # Is any vote present?
-            self.c_has_voted.perform((voter_token, issue_token))
+            self.c_has_voted.perform((person_token, issue_token))
             row = self.c_has_voted.fetchone()
             _ = self.c_has_voted.fetchall()  # should be empty (LIMIT 1)
 
-            #print('HAS-VOTED:', row, '||', voter_token, issue_token)
+            #print('HAS-VOTED:', row, '||', person_token, issue_token)
             voted_upon[issue.iid] = row is not None
 
         return voted_upon

Reply via email to