package the8472.mldht.cli.commands; import the8472.mldht.cli.CommandProcessor; import the8472.mldht.cli.ParseArgs; import lbms.plugins.mldht.kad.DHT; import lbms.plugins.mldht.kad.GenericStorage; import lbms.plugins.mldht.kad.GenericStorage.StorageItem; import lbms.plugins.mldht.kad.Key; import lbms.plugins.mldht.kad.tasks.GetLookupTask; import lbms.plugins.mldht.kad.tasks.PutTask; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.nio.file.attribute.PosixFilePermissions; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.Base64; import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; import net.i2p.crypto.eddsa.EdDSAPrivateKey; import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec; public class Put extends CommandProcessor { StorageItem it; boolean mutable; Path keyFile; EdDSAPrivateKey key; Object data; byte[] salt; @Override protected void process() { try { keyFile = ParseArgs.extractString(arguments, "-keyfile").map(Paths::get).orElse(null); salt = ParseArgs.extractString(arguments, "-salt").map(s -> s.getBytes(StandardCharsets.UTF_8)).orElse(null); mutable = keyFile != null || salt != null; loadKey(); loadData(); // TODO: sequence number if(mutable) { it = GenericStorage.buildMutable(data, key, salt, 1); assert(it.validateSig()); } else { it = GenericStorage.buildImmutable(data); } } catch(Exception e) { handleException(e); return; } startLookup(); } void loadData() throws IOException { Path dataFile = ParseArgs.extractString(arguments, "-f").map(Paths::get).orElse(null); if(dataFile != null) { data = Files.readAllBytes(dataFile); } else { data = arguments.get(0); } } void loadKey() throws IOException, NoSuchAlgorithmException { if(!mutable) return; if(keyFile == null) { keyFile = Paths.get(".", ".keys", "default.priv"); } keyFile = keyFile.toAbsolutePath().normalize(); Path dir = keyFile.getParent(); Files.createDirectories(dir); // TODO: platform detection try { Files.setPosixFilePermissions(dir, PosixFilePermissions.fromString("rwx------")); } catch (UnsupportedOperationException ex) { printErr("Warning: could not restrict access for private key storage directory (filesystem does not support posix permissions?). " + dir.toString() + "\n"); } byte[] seed; if(!Files.exists(keyFile)) { seed = SecureRandom.getInstanceStrong().generateSeed(32); Files.write(keyFile, Base64.getEncoder().encode(seed), StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE); println("Key does not exist, creating... saving at " + keyFile.toString()); } else { seed = Base64.getDecoder().decode(Files.readAllBytes(keyFile)); if(seed.length != 32) { throw new IllegalArgumentException("failed to decode private key, expected 32bytes after base64 decoding"); } } key = new EdDSAPrivateKey(new EdDSAPrivateKeySpec(seed, GenericStorage.StorageItem.spec)); } void startLookup() { Key k = it.fingerprint(); println(k.toString(false)); AtomicInteger completionCounter = new AtomicInteger(); dhts.stream().filter(DHT::isRunning).map(d -> d.getServerManager().getRandomActiveServer(false)).filter(Objects::nonNull).forEach(s -> { GetLookupTask g = new GetLookupTask(k, s, s.getDHT().getNode()); g.expectedSalt(salt); g.addListener(t -> { PutTask p = new PutTask(s, s.getDHT().getNode(), g.getTokens(), it); p.addListener(t2 -> { println(t2.getRPC().getDHT().getType()+": stored on "+t2.getRecvResponses()+" nodes"); if(completionCounter.decrementAndGet() == 0) exit(0); }); s.getDHT().getTaskManager().addTask(p); }); s.getDHT().getTaskManager().addTask(g); completionCounter.incrementAndGet(); }); } }