AO Security Changelog

ao-security-3.0.0

Release Notes

  • Now supports Java 9+ modules with included module-info.class.
  • Maven artifact relocated from com.aoindustries:ao-security to com.aoapps:ao-security.
  • Package renamed from com.aoindustries.security to com.aoapps.security.
  • SQL schema renamed from com.aoindustries.security to com.aoapps.security.

ao-security-2.3.0

Release Notes

  • Fixed bug in MD5 and SHA-1 password hashing. Any extra zeroes after conversion to UTF-8 were incorrectly being passed to the message digest.

ao-security-2.2.0

Release Notes

  • Implemented Key.equals(…) and Password.equals(…). Both are length-constant time implementations, and will always return false when either is destroyed (including when both are destroyed).

ao-security-2.1.0

Release Notes

  • Added SecurityStreamables for HashedKey and HashedPassword. This allows optimized network protocol versus converting to/from string.

ao-security-2.0.1

Release Notes

  • Fixed database dump/restore data ordering problems by skipping some validation steps when the user is postgres. Please replace the following functions:
    1. HashedKey.Algorithm.validateHash-function.sql
    2. HashedPassword.Algorithm.validate-function.sql

ao-security-2.0.0

Release Notes

  • Exposed some static implementation utilities as SecurityUtil.
  • Created a new pair of classes Password and UnprotectedPassword that should be used to encapsulate plaintext passwords instead of using char[] or String. Instances are Destroyable.

    All uses are aggressively destroyed, thus requiring the caller to actively clone when the password is still required. Thus, programmer errors should be in the direction of safety: no action means password is destroyed.

    Deprecated the String-based representations of passwords, in favor of the aggressively destroyed Password.

  • Created a new pair of classes Key and UnprotectedKey that should be used to encapsulate keys instead of working directly with byte[]. Instances are Destroyable.

    All uses are aggressively destroyed, thus requiring the caller to actively clone when the key is still required. Thus, programmer errors should be in the direction of safety: no action means key is destroyed.

    Deprecated the byte[]-based representations of keys, in favor of the aggressively destroyed Key.

    generateKey(…) now returns UnprotectedKey instead of byte[], which is a breaking change. This is major version increment.

  • New method HashedKey.matches(Key), for consistency with HashedPassword.matches(Password).
  • Added domains over the composite types for self-validation. This means tables may now use the type directly, without needing an explicit check constraint calling the per-type validate functions.
  • Now requires PostgreSQL 11 and above.
  • A database schema update is required:
    1. BEGIN;
      
      ALTER TYPE "com.aoindustries.security"."HashedKey"       RENAME TO "<HashedKey>";
      ALTER TYPE "com.aoindustries.security"."HashedPassword"  RENAME TO "<HashedPassword>";
      ALTER TYPE "com.aoindustries.security"."Identifier"      RENAME TO "<Identifier>";
      
      COMMENT ON TYPE "com.aoindustries.security"."<HashedKey>" IS
      'Row definition for "com.aoindustries.security"."HashedKey"';
      COMMENT ON TYPE "com.aoindustries.security"."<HashedPassword>" IS
      'Row definition for "com.aoindustries.security"."HashedPassword"';
      COMMENT ON TYPE "com.aoindustries.security"."<Identifier>" IS
      'Row definition for "com.aoindustries.security"."Identifier"';
      
      CREATE DOMAIN "com.aoindustries.security"."HashedKey" AS "com.aoindustries.security"."<HashedKey>" CHECK (
      	VALUE IS NOT DISTINCT FROM NULL
      	OR "com.aoindustries.security"."HashedKey.validate"(VALUE) IS NULL
      );
      CREATE DOMAIN "com.aoindustries.security"."HashedPassword" AS "com.aoindustries.security"."<HashedPassword>" CHECK (
      	VALUE IS NOT DISTINCT FROM NULL
      	OR "com.aoindustries.security"."HashedPassword.validate"(VALUE) IS NULL
      );
      CREATE DOMAIN "com.aoindustries.security"."Identifier" AS "com.aoindustries.security"."<Identifier>" CHECK (
      	VALUE IS NOT DISTINCT FROM NULL
      	OR "com.aoindustries.security"."Identifier.validate"(VALUE) IS NULL
      );
      
      COMMENT ON DOMAIN "com.aoindustries.security"."HashedKey" IS
      'Matches class com.aoindustries.security.HashedKey';
      COMMENT ON DOMAIN "com.aoindustries.security"."HashedPassword" IS
      'Matches class com.aoindustries.security.HashedPassword';
      COMMENT ON DOMAIN "com.aoindustries.security"."Identifier" IS
      'Matches class com.aoindustries.security.Identifier';
      
      COMMIT;
    2. Recreate all functions, for "PARALLEL SAFE" and type changes.
    3. Update your column types
    4. Drop the unnecessary column check constraints

ao-security-1.0.7

Release Notes

  • Created new SQL function "com.aoindustries.security"."Identifier.validate". Although it is very simple in that it merely ensures both values are non-null and could be accomplished in per-table check "IS NOT NULL" check constraints, this follows the pattern of other composite types. Composite types are responsible for their own validation, and will also become DOMAIN in PostgreSQL 11+, further solidifying this.

ao-security-1.0.6

Release Notes

  • Made the per-algorithm validation methods public. This allows them to be used earlier, such as when validating request parameters.

ao-security-1.0.5

Release Notes

  • Documented that the default SecureRandom instance is not a strong instance.
  • Added overloads to all places that use SecureRandom, allowing the caller to provide their own random instance, which may be a strong instance where appropriate to the application.
  • HashedPassword now refuses to hash an empty password. Implementations should be using null or HashedPassword.NO_PASSWORD when there is no password. We expect that all forms and tools already check for empty passwords, but this is a final sanity check.

ao-security-1.0.4

Release Notes

  • Corrected the zero-padding of hash for crypt algorithm in SQL function HashedPassword.valueOf(TEXT). Hashes that started with zero nibbles could not be parsed and would result in an exception.

ao-security-1.0.3

Release Notes

  • Implemented SQL function HashedPassword.valueOf(TEXT) for the "crypt" algorithm. This allows the conversion of very old databases with just a simple SQL cast - no need to write any Java conversion code.

ao-security-1.0.2

Release Notes

  • Reverted back to generating keys with as many bits as their corresponding hash function (with the exception of SHA-1). Keys themselves are subject to the birthday paradox and should thus be as long as the hash.

    We have set SHA-1 to a key of 128 bits, however, which is roughly double its currently estimated collision resistance of 65 bits. But, SHA-1 is deprecated, so this shouldn't matter.

    With this change, we have restored the recommended algorithm to the previous SHA-256 so that URL parameter and cookie value lengths will not change. We believe SHA-512 would be overkill, especially considering it is a hash of an equally-sized random key.

  • Deprecated the all-field constructors, with new valueOf(…) methods. This is because the public constructor does not accept a null algorithm, since the NO_KEY / NO_PASSWORD singletons must be used. With this change, the static method can determine the correct action, instead of forcing it on the calling code.

ao-security-1.0.1

Release Notes

  • Fixed bug in SQL HashedKey.validate and HashedPassword.validate functions. The composite-type overload was incorrectly calling the Algorithm.validate function directly, instead of calling the other overload.

ao-security-1.0.0

Release Notes

  • Pulled existing security utilities out of AO Lang into this new AO Security project.
  • Added SQL implementation in alternate "sql" classifier.
  • Length-constant time equality checks Identifier.
  • HashedKey improvements:
    1. New constant NO_KEY that must be used when there is no key.
    2. Now supports multiple algorithms, with the recommended algorithm being "SHA-512". Previously, all keys were "SHA-256".
    3. For the SHA-* algorithms (with the exception of SHA-1 explained below), the key length now defaults to half that of the hash length. This is selected so the likelihood to guess the original key is equal to the hash's expected collision resistance.

      NIST 800-107: 4.1 Hash Function Properties:

      The expected collision-resistance strength of a hash function is half the length of the hash value produced by that hash function.

      SHA-256 still allows a 256-bit key for compatibility, but new keys will be generated with 128 bits. However, given that the default algorithm has also been changed to "SHA-512", expect the same default key length (URL parameter and cookie value lengths will not change).

      For SHA-1, which is deprecated and should not be used anyway, we have further reduced the key size to 64 bits (instead of the 80 bits it would be if assumed half the hash size). This is because SHA-1 is now considered to have at best 65-bits of collision resistance. If using SHA-1 (which you shouldn't) its key size is correspondingly limited to 64 bits. See:

    4. Length-constant time equality checks
    5. compareTo(HashedKey) now orders unsigned
    6. toString() and a new valueOf(String) now represent the hashed key in an unambiguous string format. "*" represents "No key".

      This may be used to insert the password into a database, and it is compatible with AutoObjectFactory. However, a PostgreSQL composite type is provided in the alternate "sql" classifier. This type is more compact and enforces much more integrity than just stuffing the value in as a string.

    7. Is now Serializable.
    8. New main method that can be used to generate a key and hash pair. Includes a -b option for benchmark mode, which runs all algorithms and gives timings.
  • HashedPassword improvements:
    1. New constant NO_PASSWORD that must be used when there is no password.
    2. Now supports multiple algorithms, with the recommended algorithm being "PBKDF2WithHmacSHA512".
    3. Per-algorithm recommended iterations, with values increased significantly higher than the previously recommended 1000. Recommended values are selected to complete the hashing in around 100 ms on commodity PC hardware from around the year 2012.
    4. New method isRehashRecommended() that provides a hint when the password should be rehashed during login.
    5. toString() and a new valueOf(String) now represent the hashed password in an unambiguous string format. "*" represents "No key".

      This may be used to insert the password into a database, and it is compatible with AutoObjectFactory. However, a PostgreSQL composite type is provided in the alternate "sql" classifier. This type is more compact and enforces much more integrity than just stuffing the value in as a string.

    6. Is now Serializable.
    7. New main method that can be used to hash passwords. Includes a -b option for benchmark mode, which runs all algorithms, gives timings, and will recommend increasing recommendedIterations when a hash is performed in under 100 ms.
    8. Unit tests will now issue a warning to suggest increasing default iterations when an algorithm completes in under 100 ms.
    9. Convenience constructors that perform all the steps of generating salt and hash.
  • Moved read/write methods for Identifier and SmallIdentifier from StreamableInput and StreamableOutput to static methods on a new SecurityStreamables class.
  • Deprecated UnixCrypt in favor of the Apache Commons Codec implementation: UnixCrypt.