/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
package com.facebook.crypto.mac;
import java.io.IOException;
import com.facebook.crypto.exception.CryptoInitializationException;
import com.facebook.crypto.util.Assertions;
import com.facebook.crypto.util.NativeCryptoLibrary;
import com.facebook.crypto.proguard.annotations.DoNotStrip;
@DoNotStrip
public class NativeMac {
public static final String FAILURE = "Failure";
public static final int KEY_LENGTH = 64;
private static final String MAC_ALREADY_INIT = "Mac has already been initialized";
private static final String MAC_NOT_INIT = "Mac has not been initialized";
private static final String MAC_NOT_FINALIZED = "Mac has not been finalized";
private STATE mCurrentState = STATE.UNINITIALIZED;
private final NativeCryptoLibrary mNativeCryptoLibrary;
private enum STATE {
UNINITIALIZED,
INITIALIZED,
FINALIZED,
};
public NativeMac(NativeCryptoLibrary nativeCryptoLibrary) {
mNativeCryptoLibrary = nativeCryptoLibrary;
}
public void init(byte[] key, int len) throws CryptoInitializationException, IOException {
Assertions.checkState(mCurrentState == STATE.UNINITIALIZED, MAC_ALREADY_INIT);
mNativeCryptoLibrary.ensureCryptoLoaded();
if (nativeInit(key, len) == nativeFailure()) {
throw new IOException(FAILURE);
}
mCurrentState = STATE.INITIALIZED;
}
public void update(byte read) throws IOException {
Assertions.checkState(mCurrentState == STATE.INITIALIZED, MAC_NOT_INIT);
if (nativeUpdate(read) == nativeFailure()) {
throw new IOException(FAILURE);
}
}
public void update(byte[] buffer, int offset, int len) throws IOException {
Assertions.checkState(mCurrentState == STATE.INITIALIZED, MAC_NOT_INIT);
if (nativeUpdate(buffer, offset, len) == nativeFailure()) {
throw new IOException(FAILURE);
}
}
public byte[] doFinal() throws IOException {
Assertions.checkState(mCurrentState == STATE.INITIALIZED, MAC_NOT_INIT);
mCurrentState = STATE.FINALIZED;
byte[] toReturn = nativeDoFinal();
if (toReturn == null) {
throw new IOException(FAILURE);
}
return toReturn;
}
public void destroy() throws IOException {
Assertions.checkState(mCurrentState == STATE.FINALIZED, MAC_NOT_FINALIZED);
if (nativeDestroy() == nativeFailure()) {
throw new IOException(FAILURE);
}
mCurrentState = STATE.UNINITIALIZED;
}
public int getMacLength() {
return nativeGetMacLength();
}
// Used to store the HMAC context.
@DoNotStrip
private long mCtxPtr;
// The integer value representing failure in JNI world.
private static native int nativeFailure();
private native int nativeInit(byte[] key, int len);
private native int nativeUpdate(byte read);
private native int nativeUpdate(byte[] buffer, int offset, int len);
private native byte[] nativeDoFinal();
private native int nativeDestroy();
private native int nativeGetMacLength();
}