RFR: 8328119: Support HKDF in SunPKCS11 (Preview) [v10]
Martin Balao
mbalao at openjdk.org
Sat Jan 11 04:15:55 UTC 2025
On Sat, 11 Jan 2025 01:44:06 GMT, Valerie Peng <valeriep at openjdk.org> wrote:
>> src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11HKDF.java line 230:
>>
>>> 228: }
>>> 229:
>>> 230: private abstract sealed class KeyMaterialMerger permits
>>
>> Hmm, instead of all these different sub-classes which handle the various combination of key/data when merging the key materials, how about encapsulate the key/data into a record and handling the merging in one class? Something like:
>>
>>
>> record KeyMaterial(boolean isBytes, byte[] data, P11Key.P11SecretKey key) {
>> }
>>
>> private final class KeyMaterialMerger {
>>
>> private KeyMaterial curr;
>>
>> private KeyMaterial toKeyMaterial(SecretKey key) {
>> if (key instanceof SecretKeySpec) {
>> return new KeyMaterial(true, key.getEncoded(), null);
>> } else {
>> return new KeyMaterial(false, null,
>> convertKey(key, "Failure merging key material"));
>> }
>> }
>>
>> KeyMaterialMerger merge(SecretKey nextKey) {
>> if (curr == null) {
>> curr = toKeyMaterial(nextKey);
>> } else {
>> KeyMaterial next = toKeyMaterial(nextKey);
>> int combination = (curr.isBytes ? 2 : 0) |
>> (next.isBytes ? 1 : 0);
>> switch (combination) {
>> case 3: // 'curr' and 'next' both are byte[]
>> int newLen = curr.data.length + next.data.length;
>> var resData = Arrays.copyOf(curr.data, newLen);
>> System.arraycopy(next.data, 0, resData, newLen -
>> next.data.length, next.data.length);
>> curr = new KeyMaterial(true, resData, null);
>> break;
>> case 2: // 'curr' is byte[], 'next' is key
>> var resKey = p11Merge(next.key, new CK_MECHANISM(
>> CKM_CONCATENATE_DATA_AND_BASE,
>> new CK_KEY_DERIVATION_STRING_DATA(curr.data)),
>> curr.data.length + next.key.keyLength);
>> curr = new KeyMaterial(false, null, resKey);
>> break;
>> case 1: // 'curr' is key, 'next' is byte[]
>> resKey = p11Merge(curr.key, new CK_MECHANISM(
>> CKM_CONCATENATE_BASE_AND_DATA,
>> new CK_KE...
>
> This way, there is no need for the 3 XXXKeyMaterialMerger (where XXX: Any/Data/Key) and the merging code is in 1 method instead of handled over 4 different methods.
May be a matter of taste and trade-offs, but I personally lean more towards the object-oriented/polymorphic design. While a bit more verbose, I like the separation of responsibilities, the closed-world type of transitions in the states machine and the potential for extensibility. The procedural approach bases transitions in the state of a record that is not self-explanatory, opens the world to the reader for meaningless instantiations/states (e.g. with a key and a byte array, or with `isBytes = false` and a byte array) and is less extensible. That type of aggregation could be just fields in the merger class, and I don't see the point of doing `keyMerger = keyMerger.merge(key);` for a method that can only return `this`. @franferrax ?
-------------
PR Review Comment: https://git.openjdk.org/jdk/pull/22215#discussion_r1911874222
More information about the security-dev
mailing list