Hi Alan,

On Thu, Feb 19, 2026 at 1:27 PM Alan Coopersmith <[email protected]> 
wrote:
> https://blog.trailofbits.com/2026/02/18/carelessness-versus-craftsmanship-in-cryptography/
> reports:
> > Two popular AES libraries, aes-js and pyaes, “helpfully” provide a default 
> > IV
> > in their AES-CTR API, leading to a large number of key/IV reuse bugs. These
> > bugs potentially affect thousands of downstream projects.

> > strongMan is a web-based management tool for folks using the strongSwan VPN
> > suite. It allows for credential and user management, initiation of VPN
> > connections, and more. It’s a pretty slick piece of software; if you’re into
> > IPsec VPNs, you should definitely give it a look.

> > There will be a security advisory for strongMan issued in conjunction with 
> > this
> > fix, outlining the nature of the problem, its severity, and the measures 
> > taken
> > to address it. Everything will be out in the open, with full transparency 
> > for
> > all strongMan users.

That strongMan patch is refreshing to see:

The latest version fixes the issue by switching to AES-GCM-SIV encryption
> with a random nonce and an individually derived encryption key, using HKDF,
> for each encrypted value. Database migrations are provided to automatically
> re-encrypt all credentials.


The patch is also pretty small:

diff --git a/requirements.txt b/requirements.txt
index 6cf1caa..111fa76 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,6 +1,7 @@
 Django==4.2.28
 
git+https://github.com/wbond/oscrypto.git@1547f535001ba568b239b8797465536759c742a3
 asn1crypto==1.5.1
+cryptography==46.0.3
 pyaes==1.6.1
 django-tables2==2.3.4
 vici==5.8.4
diff --git a/strongMan/helper_apps/encryption/fields.py
b/strongMan/helper_apps/encryption/fields.py
index f574782..3af11eb 100644
--- a/strongMan/helper_apps/encryption/fields.py
+++ b/strongMan/helper_apps/encryption/fields.py
@@ -1,11 +1,16 @@
 '''
 https://github.com/orcasgit/django-fernet-fields
 '''
+import os
+
 from django.conf import settings
 from django.core.exceptions import FieldError, ImproperlyConfigured
 from django.db import models
 from django.utils.encoding import force_bytes, force_str
 from django.utils.functional import cached_property
+from cryptography.hazmat.primitives import hashes
+from cryptography.hazmat.primitives.ciphers.aead import AESGCMSIV
+from cryptography.hazmat.primitives.kdf.hkdf import HKDF
 from pyaes import aes as aeslib

 __all__ = [
@@ -20,7 +25,7 @@


 class EncryptedField(models.Field):
-    """A field that encrypts values using Fernet symmetric encryption."""
+    """A field that encrypts values using AES-GCM-SIV symmetric encryption."""
     _internal_type = 'BinaryField'

     def __init__(self, *args, **kwargs):
@@ -42,12 +47,24 @@ def __init__(self, *args, **kwargs):
         super(EncryptedField, self).__init__(*args, **kwargs)

     def encrypt(self, value):
-        aes = aeslib.AESModeOfOperationCTR(self.key)
-        return aes.encrypt(value)
+        # we use a random nonce for the encryption and to generate an
individual encryption key, which is
+        # then concatenated and separated by a : from the ciphertext
+        nonce = os.urandom(12)
+        # leave the salt intentionally blank
+        hkdf = HKDF(algorithm=hashes.SHA256(), length=32, salt=b'',
info=nonce + b'EncryptedField')
+        aesgcmsiv = AESGCMSIV(hkdf.derive(self.key))
+        return nonce + b':' + aesgcmsiv.encrypt(nonce, value, None)

     def decrypt(self, value):
-        aes = aeslib.AESModeOfOperationCTR(self.key)
-        return aes.decrypt(value)
+        # decrypt unsafe legacy values if we don't find a nonce
+        if len(value) < 13 or value[12] != b':'[0]:
+            aes = aeslib.AESModeOfOperationCTR(self.key)
+            return aes.decrypt(value)
+        # <12-byte nonce>:<ciphertext>
+        nonce = value[:12]
+        hkdf = HKDF(algorithm=hashes.SHA256(), length=32, salt=b'',
info=nonce + b'EncryptedField')
+        aesgcmsiv = AESGCMSIV(hkdf.derive(self.key))
+        return aesgcmsiv.decrypt(nonce, value[13:], None)

     @cached_property

     def key(self):


Hell, it even uses HKDF correctly
<https://soatok.blog/2021/11/17/understanding-hkdf/>. Most people screw
HKDF up in a way that only security proof authors really care about, and
they didn't.

Just wanted to say: As awful as pyaes and aes-js are, seeing this high
quality work in response to the disclosure is a little heartwarming.

Reply via email to