diff --git a/app/src/main/java/io/xeres/app/xrs/service/identity/IdentityServiceStorage.java b/app/src/main/java/io/xeres/app/xrs/service/identity/IdentityServiceStorage.java
new file mode 100644
index 00000000..7fe3b284
--- /dev/null
+++ b/app/src/main/java/io/xeres/app/xrs/service/identity/IdentityServiceStorage.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2024 by David Gerber - https://zapek.com
+ *
+ * This file is part of Xeres.
+ *
+ * Xeres is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Xeres is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Xeres. If not, see .
+ */
+
+package io.xeres.app.xrs.service.identity;
+
+import java.util.regex.Pattern;
+
+public class IdentityServiceStorage
+{
+ private static final Pattern SERVICE_STRING = Pattern.compile("^v2 \\{P:(.{1,1024}?)}\\{T:(.{1,1024}?)}\\{R:(.{1,1024}?)}$");
+
+ private Pgp pgp;
+ private Recognition recognition;
+ private Reputation reputation;
+
+ private boolean success;
+
+ public IdentityServiceStorage(long pgpIdentifier)
+ {
+ }
+
+ public IdentityServiceStorage(String storage)
+ {
+ success = in(storage);
+ }
+
+ private boolean in(String storage)
+ {
+ var matcher = SERVICE_STRING.matcher(storage);
+
+ if (!matcher.matches())
+ {
+ return false;
+ }
+
+ pgp = new Pgp(matcher.group(1));
+ if (!pgp.isSuccessful())
+ {
+ return false;
+ }
+
+ recognition = new Recognition(matcher.group(2));
+ if (!recognition.isSuccessful())
+ {
+ return false;
+ }
+
+ reputation = new Reputation(matcher.group(3));
+ if (!reputation.isSuccessful())
+ {
+ return false;
+ }
+ return true;
+ }
+
+ public String out()
+ {
+
+ return "v2 " + "{P:" +
+ pgp.out() +
+ "}" +
+ "{T:" +
+ recognition.out() +
+ "}" +
+ "{R:" +
+ reputation.out() +
+ "}";
+ }
+
+ public boolean isSuccess()
+ {
+ return success;
+ }
+
+}
diff --git a/app/src/main/java/io/xeres/app/xrs/service/identity/Pgp.java b/app/src/main/java/io/xeres/app/xrs/service/identity/Pgp.java
new file mode 100644
index 00000000..761c3e6e
--- /dev/null
+++ b/app/src/main/java/io/xeres/app/xrs/service/identity/Pgp.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2024 by David Gerber - https://zapek.com
+ *
+ * This file is part of Xeres.
+ *
+ * Xeres is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Xeres is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Xeres. If not, see .
+ */
+
+package io.xeres.app.xrs.service.identity;
+
+import io.xeres.common.id.Id;
+
+import java.time.Instant;
+import java.util.regex.Pattern;
+
+class Pgp
+{
+ private static final Pattern VALIDATED = Pattern.compile("^K:1 I:(\\p{XDigit}{16})$");
+ private static final Pattern UNVALIDATED_WITH_TIMESTAMP_ATTEMPTS_AND_ID = Pattern.compile("^K:0 T:(\\d{1,10}) C:(\\d{1,10}) I:(\\p{XDigit}{16})$");
+ private static final Pattern UNVALIDATED_WITH_TIMESTAMP_AND_ATTEMPTS = Pattern.compile("^K:0 T:(\\d{1,10}) C:(\\d{1,10})$");
+ private static final Pattern UNVALIDATED_WITH_TIMESTAMP = Pattern.compile("^K:0 T:(\\d{1,10})$");
+
+ private boolean validated;
+ private Instant lastCheck;
+ private int checkAttempt;
+ private long pgpIdentifier;
+
+ private boolean success;
+
+ public Pgp(long pgpIdentifier)
+ {
+ this.pgpIdentifier = pgpIdentifier;
+ validated = true;
+ }
+
+ public Pgp(String input)
+ {
+ success = in(input);
+ }
+
+ private boolean in(String input)
+ {
+ var matcher = VALIDATED.matcher(input);
+ if (matcher.matches())
+ {
+ validated = true;
+ pgpIdentifier = Long.parseUnsignedLong(matcher.group(1), 16);
+ return true;
+ }
+ matcher = UNVALIDATED_WITH_TIMESTAMP_ATTEMPTS_AND_ID.matcher(input);
+ if (matcher.matches())
+ {
+ lastCheck = Instant.ofEpochSecond(Long.parseLong(matcher.group(1)));
+ checkAttempt = Integer.parseUnsignedInt(matcher.group(2));
+ pgpIdentifier = Long.parseUnsignedLong(matcher.group(3), 16);
+ return true;
+ }
+ matcher = UNVALIDATED_WITH_TIMESTAMP_AND_ATTEMPTS.matcher(input);
+ if (matcher.matches())
+ {
+ lastCheck = Instant.ofEpochSecond(Long.parseLong(matcher.group(1)));
+ checkAttempt = Integer.parseUnsignedInt(matcher.group(2));
+ return true;
+ }
+ matcher = UNVALIDATED_WITH_TIMESTAMP.matcher(input);
+ if (matcher.matches())
+ {
+ lastCheck = Instant.ofEpochSecond(Long.parseLong(matcher.group(1)));
+ return true;
+ }
+ return false;
+ }
+
+ public String out()
+ {
+ if (validated)
+ {
+ return "K:1 I:" + Id.toString(pgpIdentifier);
+ }
+ else
+ {
+ var sb = new StringBuilder("K:0");
+ sb.append(" T:");
+ sb.append(lastCheck.getEpochSecond());
+ sb.append(" C:");
+ sb.append(checkAttempt);
+ if (pgpIdentifier != 0)
+ {
+ sb.append(" I:");
+ sb.append(Id.toString(pgpIdentifier));
+ }
+ return sb.toString();
+ }
+ }
+
+ public boolean isSuccessful()
+ {
+ return success;
+ }
+}
diff --git a/app/src/main/java/io/xeres/app/xrs/service/identity/Recognition.java b/app/src/main/java/io/xeres/app/xrs/service/identity/Recognition.java
new file mode 100644
index 00000000..6e708ea8
--- /dev/null
+++ b/app/src/main/java/io/xeres/app/xrs/service/identity/Recognition.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2024 by David Gerber - https://zapek.com
+ *
+ * This file is part of Xeres.
+ *
+ * Xeres is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Xeres is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Xeres. If not, see .
+ */
+
+package io.xeres.app.xrs.service.identity;
+
+import java.time.Instant;
+import java.util.regex.Pattern;
+
+class Recognition
+{
+ private static final Pattern RECOGNITION_PATTERN = Pattern.compile("^F:(\\d{1,10}) P:(\\d{1,10}) T:(\\d{1,10})$");
+
+ private int flags;
+ private Instant publish;
+ private Instant lastCheck;
+
+ private boolean success;
+
+ public Recognition(int flags, Instant publish, Instant lastCheck)
+ {
+ this.flags = flags;
+ this.publish = publish;
+ this.lastCheck = lastCheck;
+ }
+
+ public Recognition(String input)
+ {
+ success = in(input);
+ }
+
+ private boolean in(String input)
+ {
+ var matcher = RECOGNITION_PATTERN.matcher(input);
+ if (matcher.matches())
+ {
+ flags = Integer.parseUnsignedInt(matcher.group(1));
+ publish = Instant.ofEpochSecond(Long.parseLong(matcher.group(2)));
+ lastCheck = Instant.ofEpochSecond(Long.parseLong(matcher.group(3)));
+ return true;
+ }
+ return false;
+ }
+
+ public String out()
+ {
+ return "F:" +
+ flags +
+ " P:" +
+ publish.getEpochSecond() +
+ " T:" +
+ lastCheck.getEpochSecond();
+ }
+
+ public boolean isSuccessful()
+ {
+ return success;
+ }
+}
diff --git a/app/src/main/java/io/xeres/app/xrs/service/identity/Reputation.java b/app/src/main/java/io/xeres/app/xrs/service/identity/Reputation.java
new file mode 100644
index 00000000..2634298d
--- /dev/null
+++ b/app/src/main/java/io/xeres/app/xrs/service/identity/Reputation.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2024 by David Gerber - https://zapek.com
+ *
+ * This file is part of Xeres.
+ *
+ * Xeres is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Xeres is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Xeres. If not, see .
+ */
+
+package io.xeres.app.xrs.service.identity;
+
+import java.util.regex.Pattern;
+
+class Reputation
+{
+ private static final Pattern REPUTATION_PATTERN = Pattern.compile("^(-?\\d{1,10}) (-?\\d{1,10}) (-?\\d{1,10}) (-?\\d{1,10})$");
+
+ private int overallScore;
+ private int idScore;
+ private int ownOpinion;
+ private int peerOpinion;
+
+ private boolean success;
+
+ public Reputation(int overallScore, int idScore, int ownOpinion, int peerOpinion)
+ {
+ this.overallScore = overallScore;
+ this.idScore = idScore;
+ this.ownOpinion = ownOpinion;
+ this.peerOpinion = peerOpinion;
+ }
+
+ public Reputation(String input)
+ {
+ success = in(input);
+ }
+
+ private boolean in(String input)
+ {
+ var matcher = REPUTATION_PATTERN.matcher(input);
+ if (matcher.matches())
+ {
+ overallScore = Integer.parseInt(matcher.group(1));
+ idScore = Integer.parseInt(matcher.group(2));
+ ownOpinion = Integer.parseInt(matcher.group(3));
+ peerOpinion = Integer.parseInt(matcher.group(4));
+ return true;
+ }
+ return false;
+ }
+
+ public String out()
+ {
+ return String.format("%d %d %d %d", overallScore, idScore, ownOpinion, peerOpinion);
+ }
+
+ public boolean isSuccessful()
+ {
+ return success;
+ }
+}
\ No newline at end of file
diff --git a/app/src/test/java/io/xeres/app/xrs/service/identity/IdentityServiceStorageTest.java b/app/src/test/java/io/xeres/app/xrs/service/identity/IdentityServiceStorageTest.java
new file mode 100644
index 00000000..55a97479
--- /dev/null
+++ b/app/src/test/java/io/xeres/app/xrs/service/identity/IdentityServiceStorageTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2024 by David Gerber - https://zapek.com
+ *
+ * This file is part of Xeres.
+ *
+ * Xeres is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Xeres is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Xeres. If not, see .
+ */
+
+package io.xeres.app.xrs.service.identity;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class IdentityServiceStorageTest
+{
+ @Test
+ void ParseIdentityString_Success()
+ {
+ var input = "v2 {P:K:1 I:133E525084DE5D4D}{T:F:4096 P:1614029822 T:1614029841}{R:50 50 0 0}";
+
+ var identityServiceStorage = new IdentityServiceStorage(input);
+ assertTrue(identityServiceStorage.isSuccess());
+ }
+
+ @Test
+ void ParseIdentityString_Negative_Rating_Success()
+ {
+ var input = "v2 {P:K:1 I:133E525084DE5D4D}{T:F:4096 P:1614029822 T:1614029841}{R:50 -50 0 0}";
+
+ var identityServiceStorage = new IdentityServiceStorage(input);
+ assertTrue(identityServiceStorage.isSuccess());
+ }
+
+ @Test
+ void ParseIdentityString_WrongVersion_Failure()
+ {
+ var input = "v1 {P:K:1 I:133E525084DE5D4D}{T:F:4096 P:1614029822 T:1614029841}{R:50 50 0 0}";
+
+ var identityServiceStorage = new IdentityServiceStorage(input);
+ assertFalse(identityServiceStorage.isSuccess());
+ }
+
+ @Test
+ void ParseIdentityString_NegativePublish_Failure()
+ {
+ var input = "v2 {P:K:1 I:133E525084DE5D4D}{T:F:4096 P:-1 T:1614029841}{R:50 50 0 0}";
+
+ var identityServiceStorage = new IdentityServiceStorage(input);
+ assertFalse(identityServiceStorage.isSuccess());
+ }
+}
\ No newline at end of file