ch.ge.ve.commons.crypto.SensitiveDataCryptoUtilsST.java Source code

Java tutorial

Introduction

Here is the source code for ch.ge.ve.commons.crypto.SensitiveDataCryptoUtilsST.java

Source

/*
 * -
 * #%L
 * Common crypto utilities
 * %%
 * Copyright (C) 2016 Rpublique et Canton de Genve
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero 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 Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * #L%
 */

package ch.ge.ve.commons.crypto;

import com.google.common.base.Joiner;
import com.google.common.base.Stopwatch;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.BeforeClass;
import org.junit.Test;

import java.math.BigInteger;
import java.security.Security;
import java.util.concurrent.TimeUnit;

import static ch.ge.ve.commons.crypto.SensitiveDataCryptoUtils.generateStrongPasswordHash;
import static ch.ge.ve.commons.crypto.SensitiveDataCryptoUtils.validateStrongPasswordHash;

/**
 * This test suit aims at stress testing the {@link SensitiveDataCryptoUtils} utility class.
 */
public class SensitiveDataCryptoUtilsST {
    @BeforeClass
    public static void init() {
        Security.addProvider(new BouncyCastleProvider());
        SensitiveDataCryptoUtils.configure(new TestSensitiveDataCryptoUtilsConfiguration());
    }

    /**
     * Runs several times the {@link SensitiveDataCryptoUtils#validateStrongPasswordHash} method and logs the
     * average execution time to identify a potential timing attack. This verification is manual, nothing is checked in
     * this test case.
     */
    @Test
    public void analyzePotentialTimingAttacksOnPasswordHashVerification() {
        char[] passwd = "A random te$ting p#s$w0rd!!!".toCharArray();
        String passwordHash = generateStrongPasswordHash(passwd);

        int runs = 100;

        System.out.printf("%-25s | %15s | %s (%d runs)%n", "Test", "average (ms)", "total", runs);

        Stopwatch validPasswordChecking = Stopwatch.createStarted();
        for (int i = 0; i < runs; i++) {
            validateStrongPasswordHash(passwd, passwordHash);
        }
        validPasswordChecking.stop();
        logRuns("Valid password checks", validPasswordChecking, runs);

        Stopwatch invalidPasswordChecking = Stopwatch.createStarted();
        for (int i = 0; i < runs; i++) {
            validateStrongPasswordHash("randomsoinhvakdjailsjdf".toCharArray(), passwordHash);
        }
        invalidPasswordChecking.stop();
        logRuns("Invalid password checks", invalidPasswordChecking, runs);

        String[] parts = passwordHash.split(":");

        byte[] hash = fromHex(parts[2]);
        // invert last byte
        hash[hash.length - 1] = (byte) (0xff ^ hash[hash.length - 1]);

        String alteredHash = Joiner.on(":").join(parts[0], parts[1], toHex(hash));

        Stopwatch alteredHashByteChecking = Stopwatch.createStarted();
        for (int i = 0; i < runs; i++) {
            validateStrongPasswordHash(passwd, alteredHash);
        }
        alteredHashByteChecking.stop();
        logRuns("Altered hash checks", alteredHashByteChecking, runs);
    }

    private static String toHex(byte[] byteArray) {
        BigInteger bi = new BigInteger(1, byteArray);
        String hexString = bi.toString(16);
        // should be 2 chars per byte
        int paddingLength = (byteArray.length * 2) - hexString.length();
        String padding = "";
        if (paddingLength > 0) {
            padding = String.format("%0" + paddingLength + "d", 0);
        }
        return padding + hexString;
    }

    private static byte[] fromHex(String hex) {
        byte[] bytes = new byte[hex.length() / 2];
        for (int i = 0; i < bytes.length; i++) {
            bytes[i] = (byte) Integer.parseInt(hex.substring(2 * i, 2 * i + 2), 16);
        }

        return bytes;
    }

    private void logRuns(String label, Stopwatch stopwatch, int runs) {
        long total = stopwatch.elapsed(TimeUnit.MILLISECONDS);
        double average = (double) total / runs;
        System.out.printf("%-25s | %15.2f | %15d%n", label, average, total);

    }
}