/** * TLS-Attacker - A Modular Penetration Testing Framework for TLS * * Copyright 2014-2016 Ruhr University Bochum / Hackmanit GmbH * * Licensed under Apache License 2.0 * http://www.apache.org/licenses/LICENSE-2.0 */ package de.rub.nds.tlsattacker.tls.crypto; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import de.rub.nds.tlsattacker.tls.constants.DigestAlgorithm; import de.rub.nds.tlsattacker.util.ArrayConverter; /** * Computes message digest for two algorithms at once, typically for MD5 and * SHA1 for TLS 1.0. At the end it returns MD5(value) || SHA1(value). For TLS * 1.2 SHA256 is used, as described in the RFC. Inspired by the Bouncy Castle * CombinedHash class. * * @author Juraj Somorovsky <juraj.somorovsky@rub.de> */ public final class TlsMessageDigest { private static final Logger LOGGER = LogManager.getLogger(TlsMessageDigest.class); private MessageDigest hash1; private MessageDigest hash2; private byte[] rawBytes = {}; private boolean initialized; /** * Default constructor. We use this in cases when we do not know yet, which * message digest is going to be computed. */ public TlsMessageDigest() { } /** * Constructor with TLS digest algorithm, incl. its initialization * * @param digestAlgorithm * @throws NoSuchAlgorithmException */ public TlsMessageDigest(DigestAlgorithm digestAlgorithm) throws NoSuchAlgorithmException { initializeDigestAlgorithm(digestAlgorithm); } /** * Initialization of the message digest algorithm(s). The function in * addition computes a digest over the data that is already contained in the * raw bytes. * * @param digestAlgorithm * @throws NoSuchAlgorithmException */ public void initializeDigestAlgorithm(DigestAlgorithm digestAlgorithm) throws NoSuchAlgorithmException { if (initialized) { LOGGER.warn("The TLS message digest algorithm has already been set"); return; } if (digestAlgorithm == DigestAlgorithm.LEGACY) { this.hash1 = MessageDigest.getInstance("MD5"); this.hash2 = MessageDigest.getInstance("SHA-1"); } else { this.hash1 = MessageDigest.getInstance(digestAlgorithm.getJavaName()); } initialized = true; updateDigest(rawBytes); } public String getAlgorithm() { String algorithm = hash1.getAlgorithm(); if (hash2 != null) { algorithm += " and " + hash2.getAlgorithm(); } return algorithm; } public int getDigestLength() { int digestLength = hash1.getDigestLength(); if (hash2 != null) { digestLength += hash2.getDigestLength(); } return digestLength; } public void update(byte in) { if (initialized) { updateDigest(in); } byte[] tmp = new byte[1]; tmp[0] = in; rawBytes = ArrayConverter.concatenate(rawBytes, tmp); } public void updateDigest(byte in) { // LOGGER.debug("Updating digest over the following data: \n {}", in); hash1.update(in); if (hash2 != null) { hash2.update(in); } } public void update(byte[] in, int inOff, int len) { if (initialized) { updateDigest(in, inOff, len); } rawBytes = ArrayConverter.concatenate(rawBytes, Arrays.copyOfRange(in, inOff, inOff + len)); } public void updateDigest(byte[] in, int inOff, int len) { // LOGGER.debug("Updating digest over the following data: \n {}", // ArrayConverter.bytesToHexString(Arrays.copyOfRange(in, inOff, len))); hash1.update(in, inOff, len); if (hash2 != null) { hash2.update(in, inOff, len); } } public void update(byte[] in) { if (initialized) { updateDigest(in); } rawBytes = ArrayConverter.concatenate(rawBytes, in); } public void updateDigest(byte[] in) { // LOGGER.debug("Updating digest over the following data: \n {}", // ArrayConverter.bytesToHexString(in)); hash1.update(in); if (hash2 != null) { hash2.update(in); } } public byte[] digest() { byte[] digest = hash1.digest(); if (hash2 != null) { byte[] d2 = hash2.digest(); digest = ArrayConverter.concatenate(digest, d2); } // LOGGER.debug("Digest over the data was computed: \n {}", // ArrayConverter.bytesToHexString(digest)); return digest; } public void reset() { hash1.reset(); if (hash2 != null) { hash2.reset(); } rawBytes = new byte[0]; } public byte[] getRawBytes() { return rawBytes; } public void setRawBytes(byte[] rawBytes) { reset(); if (rawBytes != null) { this.rawBytes = rawBytes; updateDigest(rawBytes); } else { this.rawBytes = new byte[0]; } } }