Skip to content

Commit

Permalink
Add support for cloning Hmac objects
Browse files Browse the repository at this point in the history
  • Loading branch information
SalusaSecondus committed Jan 6, 2020
1 parent 9eeead4 commit 8a3767d
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 38 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## 1.3.0

### Improvements (Unreleased)
* Now allows cloning of `Mac` objects. [PR #78](https://github.com/corretto/amazon-corretto-crypto-provider/pull/78)

## 1.2.0

### Improvements
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ plugins {
}

group = 'software.amazon.cryptools'
version = '1.2.0'
version = '1.3.0'

configurations {
jacocoAgent
Expand Down
2 changes: 1 addition & 1 deletion src/com/amazon/corretto/crypto/provider/InputBuffer.java
Original file line number Diff line number Diff line change
Expand Up @@ -632,7 +632,7 @@ public void update(final byte val) {
//@ pure
@Override
protected Object clone() throws CloneNotSupportedException {
if (!stateCloner.isPresent()) {
if (state != null && !stateCloner.isPresent()) {
throw new CloneNotSupportedException("No stateCloner configured");
}
@SuppressWarnings("unchecked")
Expand Down
100 changes: 64 additions & 36 deletions template-src/com/amazon/corretto/crypto/provider/TemplateHmacSpi.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

import com.amazon.corretto.crypto.provider.AesCtrDrbg.SPI;

public class TemplateHmacSpi extends MacSpi {
public class TemplateHmacSpi extends MacSpi implements Cloneable {
private static final String MAC_NAME = "Hmac@@@SHORT_HASH_NAME@@@";
private static final int HASH_SIZE;
private static final int BLOCK_SIZE;
Expand Down Expand Up @@ -215,11 +215,23 @@ public void setKey(final byte[] key) {
public void reset() {
System.arraycopy(INITIAL_CONTEXT, 0, ctx, 0, INITIAL_CONTEXT.length);
}

/*
* We aren't bothering with clone() because this is a very simple object with lots of final fields.
*/
public State copy() {
State result = new State();
System.arraycopy(normalKey, 0, result.normalKey, 0, normalKey.length);
System.arraycopy(ctx, 0, result.ctx, 0, ctx.length);
result.initialized = initialized;
return result;
}
}

// None of these fields are final so that we can clone them.
private byte[] oneByteArray = null;
private final State baseState = new State();
private final InputBuffer<byte[], Void> buffer;
private State baseState = new State();
private InputBuffer<byte[], Void> buffer;


public TemplateHmacSpi() {
Expand All @@ -230,39 +242,55 @@ private TemplateHmacSpi(boolean inSelfTest) {
if (!inSelfTest) {
SELF_TEST.assertSelfTestPassed();
}
buffer = new InputBuffer<byte[], Void>(1024)
.withInitialUpdater((src, offset, length) -> {
assertInitialized();
synchronizedUpdateCtxArray(baseState.ctx, baseState.normalKey, src, offset, length);
return null;
})
.withInitialUpdater((src) -> {
assertInitialized();
synchronizedUpdateCtxBuffer(baseState.ctx, baseState.normalKey, src);
return null;
})
.withUpdater((ignored, src, offset, length) -> {
assertInitialized();
synchronizedUpdateCtxArray(baseState.ctx, null, src, offset, length);
})
.withUpdater((ignored, src) -> {
assertInitialized();
synchronizedUpdateCtxBuffer(baseState.ctx, null, src);
})
.withDoFinal((ignored) -> {
assertInitialized();
final byte[] result = new byte[HASH_SIZE];
synchronizedDoFinal(baseState.ctx, baseState.normalKey, result);
baseState.reset();
return result;
})
.withSinglePass((src, offset, length) -> {
assertInitialized();
final byte[] result = new byte[HASH_SIZE];
fastHmac(baseState.normalKey, src, offset, length, result);
baseState.reset();
return result;
});
buffer = setLambdas(new InputBuffer<byte[], Void>(1024));
}

private InputBuffer<byte[], Void> setLambdas(InputBuffer<byte[], Void> buffer) {
return buffer.withInitialUpdater((src, offset, length) -> {
assertInitialized();
synchronizedUpdateCtxArray(baseState.ctx, baseState.normalKey, src, offset, length);
return null;
})
.withInitialUpdater((src) -> {
assertInitialized();
synchronizedUpdateCtxBuffer(baseState.ctx, baseState.normalKey, src);
return null;
})
.withUpdater((ignored, src, offset, length) -> {
assertInitialized();
synchronizedUpdateCtxArray(baseState.ctx, null, src, offset, length);
})
.withUpdater((ignored, src) -> {
assertInitialized();
synchronizedUpdateCtxBuffer(baseState.ctx, null, src);
})
.withDoFinal((ignored) -> {
assertInitialized();
final byte[] result = new byte[HASH_SIZE];
synchronizedDoFinal(baseState.ctx, baseState.normalKey, result);
baseState.reset();
return result;
})
.withSinglePass((src, offset, length) -> {
assertInitialized();
final byte[] result = new byte[HASH_SIZE];
fastHmac(baseState.normalKey, src, offset, length, result);
baseState.reset();
return result;
});
}

@SuppressWarnings("unchecked")
@Override
public Object clone() throws CloneNotSupportedException {
TemplateHmacSpi clonedObject = (TemplateHmacSpi) super.clone();
clonedObject.oneByteArray = null; // This is lazily created if needed

clonedObject.baseState = clonedObject.baseState.copy(); // It's easier not to boostrap from clone in this case
clonedObject.buffer = (InputBuffer<byte[], Void>) clonedObject.buffer.clone();
clonedObject.setLambdas(clonedObject.buffer);

return clonedObject;
}

private void assertInitialized() {
Expand Down
43 changes: 43 additions & 0 deletions tst/com/amazon/corretto/crypto/provider/test/HmacTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ public void badParams() throws GeneralSecurityException {
private void testMac(Mac mac, SecretKey key, byte[] message, byte[] expected) throws Throwable {
sneakyInvoke(UTILS_CLASS, "testMac", mac, key, message, expected);
}

@Test
public void knownValue() throws Throwable {
try (final Scanner in = new Scanner(
Expand Down Expand Up @@ -368,6 +369,48 @@ public byte[] getEncoded() {
}
}

@Test
public void supportsCloneable() throws Exception {
TestUtil.assumeMinimumVersion("1.3.0", NATIVE_PROVIDER);
final byte[] prefix = new byte[123]; // Arbitrary odd size
for (int x = 0; x < prefix.length; x++) {
prefix[x] = (byte) (x & 0xFF);
}

final byte[] suffix1 = new byte[prefix.length];
final byte[] suffix2 = new byte[prefix.length];
for (int x = 0; x < suffix1.length; x++) {
// Just ensure these values are different from other patterns
suffix1[x] = (byte) ((x & 0xFF) ^ 0x13);
suffix2[x] = (byte) ((x & 0xFF) ^ 0xC7);
}

final SecretKeySpec key = new SecretKeySpec(new byte[4096], "Generic");
for (final String algorithm : SUPPORTED_HMACS) {
final Mac mac = Mac.getInstance(algorithm, NATIVE_PROVIDER);

mac.init(key);
final byte[] prefixExpectedMac = mac.doFinal(prefix);
mac.update(prefix);
final byte[] msg1ExpectedMac = mac.doFinal(suffix1);
mac.update(prefix);
final byte[] msg2ExpectedMac = mac.doFinal(suffix2);

mac.update(prefix, 0, prefix.length);
final Mac prefixClone = (Mac) mac.clone();
final Mac msg1Clone = (Mac) mac.clone();
final Mac msg2Clone = (Mac) msg1Clone.clone();

msg1Clone.update(suffix1);
msg2Clone.update(suffix2);

// Purposefully checking the prefix (shortest) one last
assertArrayEquals(algorithm + " msg1", msg1ExpectedMac, msg1Clone.doFinal());
assertArrayEquals(algorithm + " msg2", msg2ExpectedMac, msg2Clone.doFinal());
assertArrayEquals(algorithm + " prefix", prefixExpectedMac, prefixClone.doFinal());
}
}

@Test
public void selfTest() {
assertEquals(SelfTestStatus.PASSED, HmacSHA512Spi.runSelfTest().getStatus());
Expand Down

0 comments on commit 8a3767d

Please sign in to comment.