diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/KdfCalculator.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/KdfCalculator.java new file mode 100644 index 000000000..81dc65f8b --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/KdfCalculator.java @@ -0,0 +1,56 @@ +package org.sufficientlysecure.keychain.securitytoken; + +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.digests.SHA256Digest; +import org.bouncycastle.crypto.digests.SHA512Digest; + +import java.util.Arrays; + +// References: +// [0] RFC 4880 `OpenPGP Message Format` +class KdfCalculator { + public static class KdfCalculatorArguments { + public KdfParameters.HashType digestAlgorithm; + public byte[] salt; + public int iterations; + } + + public static byte[] calculateKdf(KdfCalculatorArguments kdfCalculatorArguments, byte[] pin) { + Digest digester; + switch (kdfCalculatorArguments.digestAlgorithm) { + case SHA256: + digester = new SHA256Digest(); + break; + case SHA512: + digester = new SHA512Digest(); + break; + default: + throw new RuntimeException("Unknown hash algorithm!"); + } + byte[] salt = kdfCalculatorArguments.salt; + int iterations = kdfCalculatorArguments.iterations; + + // prepare input to hash function + byte[] data = new byte[salt.length + pin.length]; + System.arraycopy(salt, 0, data, 0, salt.length); + System.arraycopy(pin, 0, data, salt.length, pin.length); + + // hash data repeatedly + // the iteration count is actually the number of octets to be hashed + // see 3.7.1.2 of [0] + int q = iterations / data.length; + int r = iterations % data.length; + for (int i = 0; i < q; i++) { + digester.update(data, 0, data.length); + } + digester.update(data, 0, r); + + byte[] digest = new byte[digester.getDigestSize()]; + digester.doFinal(digest, 0); + + // delete secrets from memory + Arrays.fill(data, (byte) 0); + + return digest; + } +}