113 lines
		
	
	
	
		
			4.5 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			113 lines
		
	
	
	
		
			4.5 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| package net.minecraft.network.chat;
 | |
| 
 | |
| import com.mojang.logging.LogUtils;
 | |
| import java.time.Instant;
 | |
| import java.util.UUID;
 | |
| import java.util.function.BooleanSupplier;
 | |
| import net.minecraft.util.SignatureValidator;
 | |
| import net.minecraft.util.Signer;
 | |
| import net.minecraft.world.entity.player.ProfilePublicKey;
 | |
| import org.jetbrains.annotations.Nullable;
 | |
| import org.slf4j.Logger;
 | |
| 
 | |
| public class SignedMessageChain {
 | |
| 	static final Logger LOGGER = LogUtils.getLogger();
 | |
| 	@Nullable
 | |
| 	SignedMessageLink nextLink;
 | |
| 	Instant lastTimeStamp = Instant.EPOCH;
 | |
| 
 | |
| 	public SignedMessageChain(UUID sender, UUID sessionId) {
 | |
| 		this.nextLink = SignedMessageLink.root(sender, sessionId);
 | |
| 	}
 | |
| 
 | |
| 	public SignedMessageChain.Encoder encoder(Signer signer) {
 | |
| 		return signedMessageBody -> {
 | |
| 			SignedMessageLink signedMessageLink = this.nextLink;
 | |
| 			if (signedMessageLink == null) {
 | |
| 				return null;
 | |
| 			} else {
 | |
| 				this.nextLink = signedMessageLink.advance();
 | |
| 				return new MessageSignature(signer.sign(output -> PlayerChatMessage.updateSignature(output, signedMessageLink, signedMessageBody)));
 | |
| 			}
 | |
| 		};
 | |
| 	}
 | |
| 
 | |
| 	public SignedMessageChain.Decoder decoder(ProfilePublicKey publicKey) {
 | |
| 		final SignatureValidator signatureValidator = publicKey.createSignatureValidator();
 | |
| 		return new SignedMessageChain.Decoder() {
 | |
| 			@Override
 | |
| 			public PlayerChatMessage unpack(@Nullable MessageSignature messageSignature, SignedMessageBody signedMessageBody) throws SignedMessageChain.DecodeException {
 | |
| 				if (messageSignature == null) {
 | |
| 					throw new SignedMessageChain.DecodeException(SignedMessageChain.DecodeException.MISSING_PROFILE_KEY);
 | |
| 				} else if (publicKey.data().hasExpired()) {
 | |
| 					throw new SignedMessageChain.DecodeException(SignedMessageChain.DecodeException.EXPIRED_PROFILE_KEY);
 | |
| 				} else {
 | |
| 					SignedMessageLink signedMessageLink = SignedMessageChain.this.nextLink;
 | |
| 					if (signedMessageLink == null) {
 | |
| 						throw new SignedMessageChain.DecodeException(SignedMessageChain.DecodeException.CHAIN_BROKEN);
 | |
| 					} else if (signedMessageBody.timeStamp().isBefore(SignedMessageChain.this.lastTimeStamp)) {
 | |
| 						this.setChainBroken();
 | |
| 						throw new SignedMessageChain.DecodeException(SignedMessageChain.DecodeException.OUT_OF_ORDER_CHAT);
 | |
| 					} else {
 | |
| 						SignedMessageChain.this.lastTimeStamp = signedMessageBody.timeStamp();
 | |
| 						PlayerChatMessage playerChatMessage = new PlayerChatMessage(signedMessageLink, messageSignature, signedMessageBody, null, FilterMask.PASS_THROUGH);
 | |
| 						if (!playerChatMessage.verify(signatureValidator)) {
 | |
| 							this.setChainBroken();
 | |
| 							throw new SignedMessageChain.DecodeException(SignedMessageChain.DecodeException.INVALID_SIGNATURE);
 | |
| 						} else {
 | |
| 							if (playerChatMessage.hasExpiredServer(Instant.now())) {
 | |
| 								SignedMessageChain.LOGGER.warn("Received expired chat: '{}'. Is the client/server system time unsynchronized?", signedMessageBody.content());
 | |
| 							}
 | |
| 
 | |
| 							SignedMessageChain.this.nextLink = signedMessageLink.advance();
 | |
| 							return playerChatMessage;
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			@Override
 | |
| 			public void setChainBroken() {
 | |
| 				SignedMessageChain.this.nextLink = null;
 | |
| 			}
 | |
| 		};
 | |
| 	}
 | |
| 
 | |
| 	public static class DecodeException extends ThrowingComponent {
 | |
| 		static final Component MISSING_PROFILE_KEY = Component.translatable("chat.disabled.missingProfileKey");
 | |
| 		static final Component CHAIN_BROKEN = Component.translatable("chat.disabled.chain_broken");
 | |
| 		static final Component EXPIRED_PROFILE_KEY = Component.translatable("chat.disabled.expiredProfileKey");
 | |
| 		static final Component INVALID_SIGNATURE = Component.translatable("chat.disabled.invalid_signature");
 | |
| 		static final Component OUT_OF_ORDER_CHAT = Component.translatable("chat.disabled.out_of_order_chat");
 | |
| 
 | |
| 		public DecodeException(Component component) {
 | |
| 			super(component);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@FunctionalInterface
 | |
| 	public interface Decoder {
 | |
| 		static SignedMessageChain.Decoder unsigned(UUID uUID, BooleanSupplier booleanSupplier) {
 | |
| 			return (messageSignature, signedMessageBody) -> {
 | |
| 				if (booleanSupplier.getAsBoolean()) {
 | |
| 					throw new SignedMessageChain.DecodeException(SignedMessageChain.DecodeException.MISSING_PROFILE_KEY);
 | |
| 				} else {
 | |
| 					return PlayerChatMessage.unsigned(uUID, signedMessageBody.content());
 | |
| 				}
 | |
| 			};
 | |
| 		}
 | |
| 
 | |
| 		PlayerChatMessage unpack(@Nullable MessageSignature messageSignature, SignedMessageBody signedMessageBody) throws SignedMessageChain.DecodeException;
 | |
| 
 | |
| 		default void setChainBroken() {
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@FunctionalInterface
 | |
| 	public interface Encoder {
 | |
| 		SignedMessageChain.Encoder UNSIGNED = signedMessageBody -> null;
 | |
| 
 | |
| 		@Nullable
 | |
| 		MessageSignature pack(SignedMessageBody signedMessageBody);
 | |
| 	}
 | |
| }
 |