Skip to content

Commit

Permalink
Default to SHA3, fallback to SHA2.
Browse files Browse the repository at this point in the history
This update allows the hashing algorithm to be configured. It defaults to `sha3-256`, which is the CUID2 specified algorithm. But, since `SHA3` isn't supported on Java 8, the hashing algorithm can be explicitly set to `sha-256` as a needed stop-gap.
  • Loading branch information
bennadel committed Jan 26, 2023
1 parent 34be997 commit d5d1092
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 20 deletions.
14 changes: 3 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,6 @@

by [Ben Nadel][ben-nadel]

## WARNING: Incomplete Implementation!

I'm told that using `SHA-256` instead of `SHA3-256` (introduced in Java 9) is a disqualifying choice for the implementation due to the security properties of the earlier hashing algorithms. As such, please consider this implementation _incomplete_ and not production ready.

----

This is a **ColdFusion / CFML port** of the [Cuid2][cuid2] token generator created by [Eric Elliott][eric-elliott]. Cuid2 is an evolution of the [Cuid][cuid] library (for which I also have a [ColdFusion port][ben-nadel-cuid]) that is intended to address some security issues.

Each Cuid token starts with a letter and is a consistent, configured length between 24 (default) and 34 characters.
Expand Down Expand Up @@ -38,17 +32,15 @@ token: lycfyvl0dlspi0us6smqkkr0
token: x0hhypk7l7k4hga8newn4gnw
```

The `Cuid2.cfc` ColdFusion component can be instantiated with two optional arguments:
The `Cuid2.cfc` ColdFusion component can be instantiated with three optional arguments:

`new Cuid2( [ length [, fingerprint ] ] )`
`new Cuid2( [ length [, fingerprint [, algorithm ] ] ] )`

* `length` - Numeric: The length of the generated token. Defaults to 24 but can be anything between 24 and 32.

* `fingerprint` - String: The machine fingerprint. This is provided as an additional source of entropy. It defaults to the name of the JVM process as reported by the `ManagementFactory` Runtime MX Bean.

## Known Issues

Eric Elliott uses the `SHA3-256` hashing algorithm in order to reduce the various sources of entropy down into a single token. Unfortunately, the `SHA3` algorithms weren't available in Java until version 9. As such, I'm using the `SHA-256` hashing algorithm. I don't know what kind of impact this will have on the security; but, I believe the `SHA-256` algorithm to still be a commonly used and secure algorithm. I could always make this a configurable property of the ColdFusion component.
* `algorithm` - String: The hash algorithm to be used when reducing the sources of entropy. It defaults to `SHA3-256` (which is the CUID2 standard); but, can also be set to `SHA-256` for older versions of Java (8) that don't support `SHA3` yet.

## Random Distribution

Expand Down
44 changes: 36 additions & 8 deletions lib/Cuid2.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@ component
hint = "I provide secure, collision-resistant ids optimized for horizontal scaling and performance."
{

this.SHA3_256 = "sha3-256";
this.SHA_256 = "sha-256";

/**
* I initialize the CUID2 generator with the given (optional) parameters.
*/
public void function init(
numeric length,
string fingerprint
string fingerprint,
string algorithm
) {

variables.minCuidLength = 24;
Expand Down Expand Up @@ -42,11 +46,9 @@ component
variables.MessageDigestClass = createObject( "java", "java.security.MessageDigest" );

// Store and test arguments.
// --
// CAUTION: These assignments must come last because generating the fingerprint
// depends on other variables being defined.
variables.cuidLength = testCuidLength( arguments.length ?: minCuidLength );
variables.processFingerprint = testProcessFingerprint( arguments.fingerprint ?: generateFingerprint() );
variables.hashAlgorithm = testHashAlgorithm( arguments.algorithm ?: this.SHA3_256 );

}

Expand Down Expand Up @@ -74,7 +76,7 @@ component
// ---

/**
* I generate a seucre random binary value of the given length.
* I generate a secure random binary value of the given length.
*/
private array function generateBytes( required numeric length ) {

Expand All @@ -91,7 +93,7 @@ component


/**
* I generate the devince fingerprint for the CUID.
* I generate the device fingerprint for the CUID.
*
* DIVERGENCE FROM CUID v1: In first version of CUID, the fingerprint generation was
* guaranteed to be 4-characters. However, in CUID v2, the fingerprint is nothing more
Expand All @@ -116,11 +118,11 @@ component
*/
private string function generateHashBlock() {

var inputs = MessageDigestClass.getInstance( "sha-256" );
var inputs = MessageDigestClass.getInstance( hashAlgorithm );

// These are all just sources of entropy to aide in collision prevention. None of
// the individual parts holds any particular magical meaning.
inputs.update( generateBytes( cuidLength * 2 ) );
inputs.update( generateBytes( maxCuidLength * 2 ) );
inputs.update( charsetDecode( getTickCount(), "utf-8" ) );
inputs.update( charsetDecode( counter.getAndIncrement(), "utf-8" ) );
inputs.update( charsetDecode( processFingerprint, "utf-8" ) );
Expand Down Expand Up @@ -187,6 +189,32 @@ component
}


/**
* I test and return the given hash algorithm, throwing an error if the algorithm is
* not supported.
*/
private string function testHashAlgorithm( required string value ) {

value = value.lcase();

if (
( value != this.SHA3_256 ) &&
( value != this.SHA_256 )
) {

throw(
type = "Cuid2.Algorithm.NotSupported",
message = "Cuid2 hashing algorithm not supported.",
detail = "At this time, only the following hashing algorithms are supported: [#this.SHA3_256#, #this.SHA_256#]."
);

}

return( value );

}


/**
* I test and return the given fingerprint, throwing an error if the fingerprint is
* invalid.
Expand Down
5 changes: 4 additions & 1 deletion tests/server-acf.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,8 @@
"app":{
"cfengine":"adobe@2021"
},
"name":"cuid-v2-acf"
"name":"cuid-v2-acf",
"jvm":{
"javaVersion":"openjdk11"
}
}
1 change: 1 addition & 0 deletions tests/server.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
},
"name":"cuid-v2-lucee",
"jvm":{
"javaVersion":"openjdk11",
"args":"-Dlucee.preserve.case=true"
}
}

0 comments on commit d5d1092

Please sign in to comment.