|
|
Message-ID: <CAOvwWh1b_Bhpn6cuqb7pQn1hbhj6F1Bn2tjLUJqjwr7fySrvPQ@mail.gmail.com>
Date: Thu, 19 Feb 2026 13:35:10 -0500
From: Soatok Dreamseeker <soatok.dhole@...il.com>
To: oss-security@...ts.openwall.com
Subject: Re: Default IV & other issues in aes-js & pyaes
modules, & strongMan VPN manager
Hi Alan,
On Thu, Feb 19, 2026 at 1:27 PM Alan Coopersmith <alan.coopersmith@...cle.com> 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.
Powered by blists - more mailing lists
Please check out the Open Source Software Security Wiki, which is counterpart to this mailing list.
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.