Signal-Android/library/src/org/whispersystems/textsecure/crypto/kdf/NKDF.java

87 wiersze
2.7 KiB
Java

/**
* Copyright (C) 2013 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.whispersystems.textsecure.crypto.kdf;
import android.util.Log;
import org.whispersystems.textsecure.util.Conversions;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import javax.crypto.spec.SecretKeySpec;
public class NKDF {
public static final int LEGACY_CIPHER_KEY_LENGTH = 16;
public static final int LEGACY_MAC_KEY_LENGTH = 20;
public DerivedSecrets deriveSecrets(byte[] sharedSecret, boolean isLowEnd)
{
SecretKeySpec cipherKey = deriveCipherSecret(isLowEnd, sharedSecret);
SecretKeySpec macKey = deriveMacSecret(cipherKey);
return new DerivedSecrets(cipherKey, macKey);
}
private SecretKeySpec deriveCipherSecret(boolean isLowEnd, byte[] sharedSecret) {
byte[] derivedBytes = deriveBytes(sharedSecret, LEGACY_CIPHER_KEY_LENGTH * 2);
byte[] cipherSecret = new byte[LEGACY_CIPHER_KEY_LENGTH];
if (isLowEnd) {
System.arraycopy(derivedBytes, LEGACY_CIPHER_KEY_LENGTH, cipherSecret, 0, LEGACY_CIPHER_KEY_LENGTH);
} else {
System.arraycopy(derivedBytes, 0, cipherSecret, 0, LEGACY_CIPHER_KEY_LENGTH);
}
return new SecretKeySpec(cipherSecret, "AES");
}
private SecretKeySpec deriveMacSecret(SecretKeySpec key) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-1");
byte[] secret = md.digest(key.getEncoded());
return new SecretKeySpec(secret, "HmacSHA1");
} catch (NoSuchAlgorithmException e) {
throw new IllegalArgumentException("SHA-1 Not Supported!",e);
}
}
private byte[] deriveBytes(byte[] seed, int bytesNeeded) {
MessageDigest md;
try {
md = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
Log.w("NKDF", e);
throw new IllegalArgumentException("SHA-256 Not Supported!");
}
int rounds = bytesNeeded / md.getDigestLength();
for (int i=1;i<=rounds;i++) {
byte[] roundBytes = Conversions.intToByteArray(i);
md.update(roundBytes);
md.update(seed);
}
return md.digest();
}
}