All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.snf4j.tls.crypto.TranscriptHash Maven / Gradle / Ivy

The newest version!
/*
 * -------------------------------- MIT License --------------------------------
 * 
 * Copyright (c) 2022-2024 SNF4J contributors
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * -----------------------------------------------------------------------------
 */
package org.snf4j.tls.crypto;

import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

import org.snf4j.tls.Args;
import org.snf4j.tls.handshake.HandshakeType;

public class TranscriptHash implements ITranscriptHash {
	
	private final static HandshakeType[] TYPES = new HandshakeType[] {
			HandshakeType.CLIENT_HELLO,
			null,
			null,
			HandshakeType.SERVER_HELLO,
			HandshakeType.ENCRYPTED_EXTENSIONS,
			HandshakeType.CERTIFICATE_REQUEST,
			HandshakeType.CERTIFICATE,
			HandshakeType.CERTIFICATE_VERIFY,
			HandshakeType.FINISHED,
			HandshakeType.END_OF_EARLY_DATA,
			null,
			null,
			null
	};

	private final static HandshakeType[] TYPES2 = new HandshakeType[] {
			null,
			HandshakeType.SERVER_HELLO,
			HandshakeType.CLIENT_HELLO,
			null,
			null,
			null,
			null,
			null,
			null,
			HandshakeType.END_OF_EARLY_DATA,
			HandshakeType.CERTIFICATE,
			HandshakeType.CERTIFICATE_VERIFY,
			HandshakeType.FINISHED
	};

	private final static HandshakeType[] COMMON_TYPES = new HandshakeType[] {
			HandshakeType.CLIENT_HELLO,
			null,
			null,
			HandshakeType.SERVER_HELLO,
			HandshakeType.ENCRYPTED_EXTENSIONS,
			null,
			null,
			null,
			null,
			HandshakeType.END_OF_EARLY_DATA,
			null,
			null,
			null
	};
	
	private final static HandshakeType[] SERVER_TYPES = new HandshakeType[] {
			null,
			null,
			null,
			null,
			null,
			HandshakeType.CERTIFICATE_REQUEST,
			HandshakeType.CERTIFICATE,
			HandshakeType.CERTIFICATE_VERIFY,
			HandshakeType.FINISHED,
			null,
			null,
			null,
			null
	};

	private final static HandshakeType[] CLIENT_TYPES = new HandshakeType[] {
			null,
			null,
			null,
			null,
			null,
			null,
			null,
			null,
			null,
			null,
			HandshakeType.CERTIFICATE,
			HandshakeType.CERTIFICATE_VERIFY,
			HandshakeType.FINISHED
	};
	
	private final static int[] MAPPING;
	
	private final static int[] MAPPING2;

	private final static int[] COMMON_MAPPING;

	private final static int[] SERVER_MAPPING;

	private final static int[] CLIENT_MAPPING;
	
	private final static int CLIENT_HELLO1_INDEX;

	private final static int CLIENT_HELLO1_MASK;

	private final static int HELLO_RETRY_REQUEST_INDEX;
	
	private final static int HELLO_RETRY_REQUEST_MASK;
	
	private final static int SERVER_FINISHED_MASK;
	
	private static int[] mapping(int length, HandshakeType[] types) {
		int[] mapping = new int[length];
		Arrays.fill(mapping, -1);
		for (int i=0; i=0; --i) {
			item = items[i];
			if (item != null) {
				break;
			}
		}
		MessageDigest md = item != null ? clone(item.md) : this.md;
		if (message != null) {
			md.update(message);
		}
		else {
			for (ByteBuffer buffer: buffers) {
				md.update(buffer);
			}
		}
		items[index] = new Item(md);
		mask |= 1 << index;
	}
	
	@Override
	public String getAlgorithm() {
		return md.getAlgorithm();
	}
	
	@Override
	public MessageDigest getHashFunction() {
		try {
			MessageDigest md = (MessageDigest) this.md.clone();

			md.reset();
			return md;
		} catch (CloneNotSupportedException e) {
			try {
				return MessageDigest.getInstance(md.getAlgorithm(), md.getProvider());
			} catch (NoSuchAlgorithmException e1) {
				throw new UnsupportedOperationException();
			}
		}
	}
	
	@Override
	public int getHashLength() {
		return md.getDigestLength();
	}

	@Override
	public void update(HandshakeType type, byte[] message) {
		update(type, message, null);
	}

	@Override
	public void update(HandshakeType type, ByteBuffer[] message) {
		update(type, null, message);
	}
	
	private void update(HandshakeType type, byte[] message, ByteBuffer[] buffers) {
		int index = -1;
		
		if (type.value() == HandshakeType.CLIENT_HELLO.value()) {
			if ((mask & HELLO_RETRY_REQUEST_MASK) != 0) {
				index = MAPPING2[type.value()];
			}
		}
		if (index == -1) {
			if ((mask & SERVER_FINISHED_MASK) != 0) {
				index = MAPPING2[type.value()];
			}
			else {
				index = MAPPING[type.value()];
			}
		}
		if (index != -1) {
			update(index, message, buffers);
		}
		else {
			throw new IllegalArgumentException();
		}
	}

	@Override
	public void updateHelloRetryRequest(byte[] message) {
		updateHelloRetryRequest(message, null);
	}

	@Override
	public void updateHelloRetryRequest(ByteBuffer[] message) {
		updateHelloRetryRequest(null, message);
	}
	
	private void updateHelloRetryRequest(byte[] message, ByteBuffer[] buffers) {
		if (mask != CLIENT_HELLO1_MASK) {
			throw new IllegalStateException();
		}
		
		int i = CLIENT_HELLO1_INDEX;
		Item item = items[i];
		
		MessageDigest md = clone(item.md);
		md.reset();
		md.update((byte) HandshakeType.MESSAGE_HASH.value());
		md.update(new byte[] {0,0,(byte) md.getDigestLength()});
		md.update(item.hash());
		items[i] = new Item(md);
				
		i = HELLO_RETRY_REQUEST_INDEX;
		md = clone(md);
		if (message != null) {
			md.update(message);
		}
		else {
			for (ByteBuffer buffer: buffers) {
				md.update(buffer);
			}
		}
		items[i] = new Item(md);
		mask |= HELLO_RETRY_REQUEST_MASK;
	}

	private byte[] getHash(HandshakeType type, int[] mapping) {
		int index = mapping[type.value()];
		if (index != -1) {
			Item item = items[index];
			
			if (item == null) {
				for (int i=index-1; i>=0; --i) {
					item = items[i];
					if (item != null) {
						break;
					}
				}
				if (item == null) {
					return clone(md).digest();
				}
			}
			return item.hash();
		}
		throw new IllegalArgumentException();
	}

	private int[] mapping(HandshakeType type) {
		if (type.value() == HandshakeType.CLIENT_HELLO.value()) {
			if ((mask & HELLO_RETRY_REQUEST_MASK) != 0) {
				return MAPPING2;
			}
		}
		return COMMON_MAPPING;
	}
	
	@Override
	public byte[] getHash(HandshakeType type) {
		return getHash(type, mapping(type));
	}

	@Override
	public byte[] getHash(HandshakeType type, boolean client) {
		return getHash(type, client ? CLIENT_MAPPING : SERVER_MAPPING);
	}
	
	private MessageDigest md(HandshakeType type, int[] mapping) {
		int index = mapping[type.value()];
		if (index != -1) {
			Item item = null;
			
			for (int i=index-1; i>=0; --i) {
				item = items[i];
				if (item != null) {
					break;
				}
			}
			MessageDigest md;
			
			if (item != null) {
				md =  clone(item.md);
			}
			else {
				md = clone(this.md);
				md.reset();
			}
			return md;
		}		
		throw new IllegalArgumentException();
	}
	
	@Override
	public byte[] getHash(HandshakeType type, byte[] replacement, int length) {
		MessageDigest md = md(type, mapping(type));

		md.update(replacement, 0, length);
		return md.digest();
	}

	@Override
	public byte[] getHash(HandshakeType type, ByteBuffer[] replacement) {
		MessageDigest md = md(type, mapping(type));

		for (ByteBuffer buffer: replacement) {
			md.update(buffer);
		}
		return md.digest();
	}
	
	@Override
	public void reset() {
		md.reset();
		for (int i=0; i




© 2015 - 2024 Weber Informatics LLC | Privacy Policy