Encryption failed for String with Max attribute

Hi Axelor Team,

I just want to notify you that encrypted field does not work.

In the “Employee” model you declared the social security numer as encrypted but the field in the database if only varchar(15) which is too short to store an AES encrypted string.

For this reason when trying to create an employee an error occured: Unexpected database error occurred on the server. ERROR: value too long for type character varying(15)

Because it’s not possible to have “large=true” with “encrypted=true” (see Encrypted field: tomcat error at start) encryption can’t work for String type.

I think that you should patch the ADK to make encrypted string as Large by default (set to type TEXT and not VARCHAR)

Regards

Hi,

Just to say that I tried to add some stuff in the ADK/GRADLE/Property.groovy

		if(isEncrypted() && type == 'string') {
			return [
				annon("javax.persistence.Lob", true),
				annon("javax.persistence.Basic").add("fetch", "javax.persistence.FetchType.LAZY", false),
			]
		}

It works well, encrypted field is TEXT inside the database but also taken as large by the IHM. So instead of having a single line text field I have a multiline one.

Regards,

Hi @femtonext

Yes, fields encryption feature have been added recently. For the moment it is the first implementation and should be improve in future version. So it should be use with care, large attribute is not supported, and field min/max lenght should be enought to store field encrypted value in database.

ABS fields encrypted issues should be fixed now.

Hi @p.belloy,

I think if encryption is not fully functionnal you should not use it the master branch like for Employee and Employee Vehicule. If anyone try to use the 5.0.2 it’s impossible to create an Employee if the Social Security Number is sets (max size is 15, value required 15 char, encrypted values is 40 chars length). It’s obvious that encrypted field (AES+base32) is bigger than the clear values :slight_smile:

What do you means by “ABS fields encrypted issues should be fixed now” ? I can’t see any fix on Employee or ADK about this problem?

Regards,

See here, available in 5.0.2 :

Sorry,

I thought that you just removed the “encrypted=true” :smile:

Regards

Hi @p.belloy

I would like to share my work with you about encrypted field. There are some few things I saw and I tried to fix so maybe it can help you to improve this feature which is, from my point of view, more and more important in an ERP:

  1. You initiate an empty IV when encrypting in CBC and it’s really bad. I think you made that to obtain the shorted encrypted string as possible. IV is not an option with CBC, it’s just mandatory !
  2. I’m not already lookup at the java core source but normally the IV for CGM should be 12 bytes and not 16 (but maybe truncated internally).
  3. When using GCM as encryption mode it uses a real large string which can cause size overflow, so varchar(255) is not enough in many cases
  4. Min/Max attribute should be supported even if string field is encrypted (usefull for UI check).

For these reasons:

  1. I have updated the ByteEncryptor to reflect my 2 first points;
  2. I have updated de Property.groovy to convert any field String + encrypted as TEXT (Lob);
  3. I have updated the view.form.js to display a single line string when encrypted but not large.

I don’t know if my approach merges your but it seams to work and to solve many problèmes.
I will join you the final patch after having finalized my tests.

Regard

Hi @p.belloy,

Like explained in the last thread on this topics, you could find below some proposal patches. For the moment it works well with Postgres & MySQL but not yet tested on Oracle.

With this patch, encrypted string field uses @Lob to avoid any problem of column size whilst to be able to keep min/max attributes usage. It also introduce a ‘large’ attribute in widget to be able to set the UI with the correct type (large = multiline input, not large = single line input)

diff --git a/axelor-gradle/src/main/groovy/com/axelor/tools/x2j/pojo/Property.groovy b/axelor-gradle/src/main/groovy/com/axelor/tools/x2j/pojo/Property.groovy
index 85990bc..5d0efda 100644
--- a/axelor-gradle/src/main/groovy/com/axelor/tools/x2j/pojo/Property.groovy
+++ b/axelor-gradle/src/main/groovy/com/axelor/tools/x2j/pojo/Property.groovy
@@ -643,6 +643,11 @@
 				.add("translatable", translatable, false)
 				.add("copyable", copyable, false)
 				.add("defaultNow", defaultNow ? "true" : null, false)
+				
+		// add LARGE annotation if field is an encrypted string with the 'large' attribute sets to true		
+		if (type == "string" && isEncrypted() && isLarge()) {
+			annon("com.axelor.db.annotations.Widget").add("large", "true", false);
+		}
 	}
 
 	private List<Annotation> $binary() {
@@ -662,10 +667,21 @@
 		}
 
 		if (isLarge() && type == 'string') {
+			// use annotation type only for non encrypted columns (type & converter cannot be specified together
+			if(!isEncrypted()) {
+				return [
+					annon("javax.persistence.Lob", true),
+					annon("javax.persistence.Basic").add("fetch", "javax.persistence.FetchType.LAZY", false),
+					annon("org.hibernate.annotations.Type").add("type", "text")
+				]
+			}
+		}
+		
+		// is string is an encrypted on (whatever large or not), set it to LOB
+		if (isEncrypted() && type == 'string') {
 			return [
 				annon("javax.persistence.Lob", true),
 				annon("javax.persistence.Basic").add("fetch", "javax.persistence.FetchType.LAZY", false),
-				annon("org.hibernate.annotations.Type").add("type", "text")
 			]
 		}

This patch is to manage the ‘large’ widget

 diff --git a/axelor-core/src/main/java/com/axelor/db/annotations/Widget.java b/axelor-core/src/main/java/com/axelor/db/annotations/Widget.java
index c9aec6c..ab54fdb 100644
--- a/axelor-core/src/main/java/com/axelor/db/annotations/Widget.java
+++ b/axelor-core/src/main/java/com/axelor/db/annotations/Widget.java
@@ -127,4 +127,11 @@
 	 * @return name of the selection, default is empty
 	 */
 	String selection() default "";
+	
+	/**
+	 * To know if the field is a large one or not (used for String and especially for encrypted one
+	 * to set a default widget as TEXT to have a multiline input
+	 * @return
+	 */
+	boolean large() default false;
 }
\ No newline at end of file

This patch is to force the UI to take the encrypted string (TEXT datatype) not as a large string but a single one. The problem here is that the “large” attribute is not taken in consideration. To be able to have a multiline encrypted string the “text” widget need to be explicitly added in the form field.

diff --git a/axelor-web/src/main/webapp/js/view/view.form.js b/axelor-web/src/main/webapp/js/view/view.form.js
index 6210399..9e14de1 100644
--- a/axelor-web/src/main/webapp/js/view/view.form.js
+++ b/axelor-web/src/main/webapp/js/view/view.form.js
@@ -1206,6 +1206,13 @@
 			if (type == 'label') {
 				type = 'static-label';
 			}
+			
+			// set field as String if is an encrypted
+			if(type == 'text' && attrs.encrypted) {				
+				if(widget == null || widget != "text") {		
+					type = "string"
+				}
+			}
 
 			if (attrs.type == 'panel-related') {
 				type = 'panel-' + (field.type || attrs.serverType || type);

This is the patch for encryptor: user 16 bytes IV for all modes (truncated internally with CGM) and PBKDF2WithHmacSHA256 as key algo.

diff --git a/axelor-common/src/main/java/com/axelor/common/crypto/BytesEncryptor.java b/axelor-common/src/main/java/com/axelor/common/crypto/BytesEncryptor.java
index 8b7deb1..15beed0 100644
--- a/axelor-common/src/main/java/com/axelor/common/crypto/BytesEncryptor.java
+++ b/axelor-common/src/main/java/com/axelor/common/crypto/BytesEncryptor.java
@@ -36,7 +36,7 @@
 public class BytesEncryptor implements Encryptor<byte[], byte[]> {
 
 	private static final String AES_ALGORITHM = "AES";
-	private static final String KEY_ALGORITHM = "PBKDF2WithHmacSHA1";
+	private static final String KEY_ALGORITHM = "PBKDF2WithHmacSHA256";
 
 	private static final SecureRandom SECURE_RANDOM = new SecureRandom();
 
@@ -62,9 +62,7 @@
 		this.transformation = String.format("%s/%s/%s", AES_ALGORITHM, mode, paddingScheme);
 		this.encryptionSalt = generateRandomBytes(SALT_SIZE);
 		this.encryptionKey = newSecretKey(password, this.encryptionSalt);
-		this.payloadSize = mode == OperationMode.CBC
-				? PREFIX_BYTES.length + SALT_SIZE
-				: PREFIX_BYTES.length + SALT_SIZE + IV_SIZE;
+		this.payloadSize = PREFIX_BYTES.length + SALT_SIZE + IV_SIZE;
 	}
 
 	public BytesEncryptor(String password) {
@@ -139,13 +137,11 @@
 			return bytes;
 		}
 
-		final byte[] iv = this.mode == OperationMode.CBC ? new byte[IV_SIZE] : generateRandomBytes(IV_SIZE);
+		final byte[] iv = generateRandomBytes(IV_SIZE);
 		final Cipher cipher = newCipher(Cipher.ENCRYPT_MODE, this.encryptionKey, iv);
 		final byte[] encrypted = doFinal(cipher, bytes);
 
-		return this.mode == OperationMode.CBC
-				? Bytes.concat(PREFIX_BYTES, this.encryptionSalt, encrypted)
-				: Bytes.concat(PREFIX_BYTES, this.encryptionSalt, iv, encrypted);
+		return Bytes.concat(PREFIX_BYTES, this.encryptionSalt, iv, encrypted);
 	}
 
 	@Override
@@ -158,9 +154,7 @@
 		final byte[] iv = new byte[IV_SIZE];
 		final byte[] data = new byte[bytes.length - this.payloadSize];
 
-		final List<byte[]> sections = this.mode == OperationMode.CBC
-				? Arrays.asList(salt, data)
-				: Arrays.asList(salt, iv, data);
+		final List<byte[]> sections = Arrays.asList(salt, iv, data);
 
 		int index = PREFIX_BYTES.length;
 		for (byte[] section : sections) {

This is the patch to manage correct type on the UI

diff --git a/axelor-web/src/main/webapp/js/view/view.form.js b/axelor-web/src/main/webapp/js/view/view.form.js
index 6210399..246ed85 100644
--- a/axelor-web/src/main/webapp/js/view/view.form.js
+++ b/axelor-web/src/main/webapp/js/view/view.form.js
@@ -1206,6 +1206,11 @@
 			if (type == 'label') {
 				type = 'static-label';
 			}
+			
+			// set field as String if is an encrypted and not large
+			if(type == 'text' && attrs.encrypted) {			
+				if(attrs.large == null || !attrs.large) type = "string"
+			}
 
 			if (attrs.type == 'panel-related') {
 				type = 'panel-' + (field.type || attrs.serverType || type);

Moreover,I think you need to add a special explanation inside the documentation for encrypted field due to Java Security Policy that is restricted by default with Oracle JRE (Java Cryptography Extension Unlimited Strength Jurisdiction Policy).

Now this kind of fields are working:

<string name="encrypted_single" min="5" max="20" encrypted="true"/>
<string name="encrypted_large" encrypted="true" large="true" />

Hope it help you to improve encrypted fields necanism.

Regards

A strong encryption support (AES + PBKDF2WithHmacSHA512) has been implemented in upcoming AOP 6.0.

For the string fields, as we already support encryption of large text fields, there is no need of other changes. We can have many ways to have single line string field:

<entity name="Some">
  ...
  <string name="field1" large="true" encrypted="true" />
  <string name="field2" encrypted="true" max="1024" />
</entity>

and view:

<form name="some-form">
  ...
  <field name="field1" large="true" multiline="false" />
  <field name="field2" max="255" /> <!-- override the max in view -->
</form>

Thanks @femtonext for the valuable inputs.