Spaces:
Build error
Build error
File size: 7,072 Bytes
d46f4a3 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
package net.minecraft.client.multiplayer;
import com.google.common.base.Strings;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.mojang.authlib.exceptions.MinecraftClientException;
import com.mojang.authlib.minecraft.UserApiService;
import com.mojang.authlib.minecraft.InsecurePublicKeyException.MissingException;
import com.mojang.authlib.yggdrasil.response.KeyPairResponse;
import com.mojang.authlib.yggdrasil.response.KeyPairResponse.KeyPair;
import com.mojang.logging.LogUtils;
import com.mojang.serialization.JsonOps;
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.PublicKey;
import java.time.DateTimeException;
import java.time.Duration;
import java.time.Instant;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import javax.annotation.Nullable;
import net.minecraft.SharedConstants;
import net.minecraft.Util;
import net.minecraft.util.Crypt;
import net.minecraft.util.CryptException;
import net.minecraft.world.entity.player.ProfileKeyPair;
import net.minecraft.world.entity.player.ProfilePublicKey;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import org.slf4j.Logger;
@OnlyIn(Dist.CLIENT)
public class AccountProfileKeyPairManager implements ProfileKeyPairManager {
private static final Logger LOGGER = LogUtils.getLogger();
private static final Duration MINIMUM_PROFILE_KEY_REFRESH_INTERVAL = Duration.ofHours(1L);
private static final Path PROFILE_KEY_PAIR_DIR = Path.of("profilekeys");
private final UserApiService userApiService;
private final Path profileKeyPairPath;
private CompletableFuture<Optional<ProfileKeyPair>> keyPair = CompletableFuture.completedFuture(Optional.empty());
private Instant nextProfileKeyRefreshTime = Instant.EPOCH;
public AccountProfileKeyPairManager(UserApiService p_253640_, UUID p_254415_, Path p_253813_) {
this.userApiService = p_253640_;
this.profileKeyPairPath = p_253813_.resolve(PROFILE_KEY_PAIR_DIR).resolve(p_254415_ + ".json");
}
@Override
public CompletableFuture<Optional<ProfileKeyPair>> prepareKeyPair() {
this.nextProfileKeyRefreshTime = Instant.now().plus(MINIMUM_PROFILE_KEY_REFRESH_INTERVAL);
this.keyPair = this.keyPair.thenCompose(this::readOrFetchProfileKeyPair);
return this.keyPair;
}
@Override
public boolean shouldRefreshKeyPair() {
return this.keyPair.isDone() && Instant.now().isAfter(this.nextProfileKeyRefreshTime) ? this.keyPair.join().map(ProfileKeyPair::dueRefresh).orElse(true) : false;
}
private CompletableFuture<Optional<ProfileKeyPair>> readOrFetchProfileKeyPair(Optional<ProfileKeyPair> p_254074_) {
return CompletableFuture.supplyAsync(() -> {
if (p_254074_.isPresent() && !p_254074_.get().dueRefresh()) {
if (!SharedConstants.IS_RUNNING_IN_IDE) {
this.writeProfileKeyPair(null);
}
return p_254074_;
} else {
try {
ProfileKeyPair profilekeypair = this.fetchProfileKeyPair(this.userApiService);
this.writeProfileKeyPair(profilekeypair);
return Optional.ofNullable(profilekeypair);
} catch (CryptException | MinecraftClientException | IOException ioexception) {
LOGGER.error("Failed to retrieve profile key pair", (Throwable)ioexception);
this.writeProfileKeyPair(null);
return p_254074_;
}
}
}, Util.nonCriticalIoPool());
}
private Optional<ProfileKeyPair> readProfileKeyPair() {
if (Files.notExists(this.profileKeyPairPath)) {
return Optional.empty();
} else {
try {
Optional optional;
try (BufferedReader bufferedreader = Files.newBufferedReader(this.profileKeyPairPath)) {
optional = ProfileKeyPair.CODEC.parse(JsonOps.INSTANCE, JsonParser.parseReader(bufferedreader)).result();
}
return optional;
} catch (Exception exception) {
LOGGER.error("Failed to read profile key pair file {}", this.profileKeyPairPath, exception);
return Optional.empty();
}
}
}
private void writeProfileKeyPair(@Nullable ProfileKeyPair p_254227_) {
try {
Files.deleteIfExists(this.profileKeyPairPath);
} catch (IOException ioexception) {
LOGGER.error("Failed to delete profile key pair file {}", this.profileKeyPairPath, ioexception);
}
if (p_254227_ != null) {
if (SharedConstants.IS_RUNNING_IN_IDE) {
ProfileKeyPair.CODEC.encodeStart(JsonOps.INSTANCE, p_254227_).ifSuccess(p_254406_ -> {
try {
Files.createDirectories(this.profileKeyPairPath.getParent());
Files.writeString(this.profileKeyPairPath, p_254406_.toString());
} catch (Exception exception) {
LOGGER.error("Failed to write profile key pair file {}", this.profileKeyPairPath, exception);
}
});
}
}
}
@Nullable
private ProfileKeyPair fetchProfileKeyPair(UserApiService p_253844_) throws CryptException, IOException {
KeyPairResponse keypairresponse = p_253844_.getKeyPair();
if (keypairresponse != null) {
ProfilePublicKey.Data profilepublickey$data = parsePublicKey(keypairresponse);
return new ProfileKeyPair(
Crypt.stringToPemRsaPrivateKey(keypairresponse.keyPair().privateKey()),
new ProfilePublicKey(profilepublickey$data),
Instant.parse(keypairresponse.refreshedAfter())
);
} else {
return null;
}
}
private static ProfilePublicKey.Data parsePublicKey(KeyPairResponse p_253834_) throws CryptException {
KeyPair keypair = p_253834_.keyPair();
if (keypair != null
&& !Strings.isNullOrEmpty(keypair.publicKey())
&& p_253834_.publicKeySignature() != null
&& p_253834_.publicKeySignature().array().length != 0) {
try {
Instant instant = Instant.parse(p_253834_.expiresAt());
PublicKey publickey = Crypt.stringToRsaPublicKey(keypair.publicKey());
ByteBuffer bytebuffer = p_253834_.publicKeySignature();
return new ProfilePublicKey.Data(instant, publickey, bytebuffer.array());
} catch (IllegalArgumentException | DateTimeException datetimeexception) {
throw new CryptException(datetimeexception);
}
} else {
throw new CryptException(new MissingException("Missing public key"));
}
}
} |