Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for cloning Hmac objects #78

Merged
merged 1 commit into from
Jan 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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