-Community authentication 2.0
-============================
+Community authentication 2.0-4.0
+================================
While the old community authentication system was simply having the
clients call a PostgreSQL function on the main website server, version
2.0 of the system uses browser redirects to perform this. This allows
#. This dictionary of information is then URL-encoded.
#. The resulting URL-encoded string is padded with spaces to an even
16 bytes, and is then AES-SIV encrypted with a shared key and a 16
- byte nonce. This key is stored in the main website system and
+ byte nonce (v4 uses ChaCha20_Poly1305 with standard size key and nonce,
+ but v3 is the preferred version). This key is stored in the main website system and
indexed by the site id, and it is stored in the settings of the
community website somewhere. Since this key is what protects the
authentication, it should be treated as very valuable.
#. The community website detects that this is a redirected authentication
response, and starts processing it specifically.
#. Using the shared key, the data is decrypted (while first being base64
- decoded, of course). Since authenticated encryption using AES-SIV
+ decoded, of course). Since authenticated encryption using AES-SIV or ChaCha20_Poly1305
is used, this step will fail if there has been any tampering with the
data.
#. The resulting string is urldecoded - and if any errors occur in the
at the URL <redirection_url>?s=logout (where redirection_url is the
same URL as when logging in)
+Versions
+--------
+The different versions are primarily different in that they use different
+encryption algorithms.
+
+v2 uses standard AES without authentication. This version is *deprecated*.
+v3 uses AES-SIV authenticated encryption. This is the *recommended* vcersion.
+v4 uses ChaCha20_Poly1305 authenticated encryption, for platforms that don't
+ support AES-SIV.
+
+
Searching
---------
The community authentication system also supports an API for searching for
migrations.AddField(
model_name='communityauthsite',
name='version',
- field=models.IntegerField(choices=[(2, 2), (3, 3)], default=2),
+ field=models.IntegerField(choices=[(2, 2), (3, 3), (4, 4)], default=2),
),
]
apiurl = models.URLField(max_length=200, null=False, blank=True)
cryptkey = models.CharField(max_length=100, null=False, blank=False,
help_text="Use tools/communityauth/generate_cryptkey.py to create a key")
- version = models.IntegerField(choices=((2, 2), (3, 3)), default=2)
+ version = models.IntegerField(choices=((2, 2), (3, 3), (4, 4)), default=2)
comment = models.TextField(null=False, blank=True)
org = models.ForeignKey(CommunityAuthOrg, null=False, blank=False, on_delete=models.CASCADE)
cooloff_hours = models.PositiveIntegerField(null=False, blank=False, default=0,
import base64
import urllib.parse
from Cryptodome.Cipher import AES
+from Cryptodome.Cipher import ChaCha20_Poly1305
from Cryptodome import Random
import time
import json
# the first block more random..
s = "t=%s&%s" % (int(time.time()), urllib.parse.urlencode(info))
- if site.version == 3:
- # v3 = authenticated encryption
+ if site.version in (3, 4):
+ # v3 = authenticated encryption, v4 = authenticated encryption with XChaCha20-Poly1305
r = Random.new()
- nonce = r.read(16)
- encryptor = AES.new(base64.b64decode(site.cryptkey), AES.MODE_SIV, nonce=nonce)
+ nonce = r.read(16 if site.version == 3 else 24)
+ if site.version == 3:
+ encryptor = AES.new(base64.b64decode(site.cryptkey), AES.MODE_SIV, nonce=nonce)
+ else:
+ encryptor = ChaCha20_Poly1305.new(key=base64.b64decode(site.cryptkey), nonce=nonce)
cipher, tag = encryptor.encrypt_and_digest(s.encode('ascii'))
redirparams = {
'd': base64.urlsafe_b64encode(cipher),
def _encrypt_site_response(site, s, version):
- if version == 3:
- # Use authenticated encryption
+ if version in (3, 4):
+ # Use authenticated encryption (v3 = SIV, v4 = ChaCha20_Poly1305
r = Random.new()
- nonce = r.read(16)
- encryptor = AES.new(base64.b64decode(site.cryptkey), AES.MODE_SIV, nonce=nonce)
+ nonce = r.read(16 if site.version == 3 else 24)
+ if site.version == 3:
+ encryptor = AES.new(base64.b64decode(site.cryptkey), AES.MODE_SIV, nonce=nonce)
+ else:
+ encryptor = ChaCha20_Poly1305.new(key=site.cryptkey, nonce=nonce)
cipher, tag = encryptor.encrypt_and_digest(s.encode('ascii'))
return "&".join((
from Cryptodome import Random
import base64
+import sys
+
+
+def usage():
+ print("Usage: generate_cryptkey.py <version>")
+ print("")
+ print("Version must be 3 or 4, representing the version of community authentication encryption to use")
+ sys.exit(0)
+
if __name__ == "__main__":
+ if len(sys.argv) != 2:
+ usage()
+ if sys.argv[1] not in ("3", "4"):
+ usage()
+
+ version = int(sys.argv[1])
+
print("The next row contains a 64-byte (512-bit) symmetric crypto key.")
print("This key should be used to integrate a community auth site.")
print("Note that each site should have it's own key!!")
print("")
r = Random.new()
- key = r.read(64)
+ key = r.read(64 if version == 3 else 32)
print(base64.b64encode(key).decode('ascii'))