Initial Commit

This commit is contained in:
netkas 2024-09-30 02:56:17 -04:00
parent ead0504e9e
commit d4eb083d77
25 changed files with 1818 additions and 0 deletions

7
.idea/codeStyles/Project.xml generated Normal file
View file

@ -0,0 +1,7 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<ScalaCodeStyleSettings>
<option name="MULTILINE_STRING_CLOSING_QUOTES_ON_NEW_LINE" value="true" />
</ScalaCodeStyleSettings>
</code_scheme>
</component>

5
.idea/codeStyles/codeStyleConfig.xml generated Normal file
View file

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

7
.idea/encodings.xml generated Normal file
View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
</component>
</project>

View file

@ -0,0 +1,8 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="AutoCloseableResource" enabled="true" level="WARNING" enabled_by_default="true">
<option name="METHOD_MATCHER_CONFIG" value="java.util.Formatter,format,java.io.Writer,append,com.google.common.base.Preconditions,checkNotNull,org.hibernate.Session,close,java.io.PrintWriter,printf,java.io.PrintStream,printf,java.lang.foreign.Arena,ofAuto,java.lang.foreign.Arena,global,com.squareup.okhttp.Response,body" />
</inspection_tool>
</profile>
</component>

13
.idea/misc.xml generated Normal file
View file

@ -0,0 +1,13 @@
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="openjdk-22" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

124
.idea/uiDesigner.xml generated Normal file
View file

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Palette2">
<group name="Swing">
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
</item>
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
</item>
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.svg" removable="false" auto-create-binding="false" can-attach-label="true">
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
</item>
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
<initial-values>
<property name="text" value="Button" />
</initial-values>
</item>
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="RadioButton" />
</initial-values>
</item>
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="CheckBox" />
</initial-values>
</item>
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
<initial-values>
<property name="text" value="Label" />
</initial-values>
</item>
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
</item>
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
</item>
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
<preferred-size width="-1" height="20" />
</default-constraints>
</item>
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
</item>
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
</item>
</group>
</component>
</project>

6
.idea/vcs.xml generated Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

47
pom.xml Normal file
View file

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>net.nosial</groupId>
<artifactId>socialclient</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>22</maven.compiler.source>
<maven.compiler.target>22</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
<version>2.15.2</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp</groupId>
<artifactId>okhttp</artifactId>
<version>2.7.5</version>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,17 @@
package net.nosial;
//TIP To <b>Run</b> code, press <shortcut actionId="Run"/> or
// click the <icon src="AllIcons.Actions.Execute"/> icon in the gutter.
public class Main {
public static void main(String[] args) {
//TIP Press <shortcut actionId="ShowIntentionActions"/> with your caret at the highlighted text
// to see how IntelliJ IDEA suggests fixing it.
System.out.printf("Hello and welcome!");
for (int i = 1; i <= 5; i++) {
//TIP Press <shortcut actionId="Debug"/> to start debugging your code. We have set one <icon src="AllIcons.Debugger.Db_set_breakpoint"/> breakpoint
// for you, but you can always add more by pressing <shortcut actionId="ToggleLineBreakpoint"/>.
System.out.println("i = " + i);
}
}
}

View file

@ -0,0 +1,4 @@
package net.nosial.socialclient;
public class Client {
}

View file

@ -0,0 +1,39 @@
package net.nosial.socialclient.abstracts;
import net.nosial.socialclient.objects.RpcError;
import net.nosial.socialclient.objects.RpcResponse;
import java.util.Map;
public abstract class RpcResult
{
private final boolean success;
protected RpcResult(boolean success)
{
this.success = success;
}
public boolean isSuccess()
{
return this.success;
}
public abstract RpcError getErrorResponse();
public abstract RpcResponse getResponse();
public static RpcResult fromMap(Map<String, Object> data)
{
if(data.containsKey("error"))
{
return new RpcError(data);
}
else if(data.containsKey("id"))
{
return new RpcResponse(data);
}
// TODO: Use standard RpcException for this or something
throw new IllegalArgumentException("Cannot recognize RPC object");
}
}

View file

@ -0,0 +1,402 @@
package net.nosial.socialclient.classes;
import net.nosial.socialclient.exceptions.CryptographyException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
public final class Cryptography
{
private static final int TIME_BLOCK = 60;
private static final int KEY_SIZE = 2048;
private static final String ALGORITHM = "RSA";
private static final String SIGNATURE_ALGORITHM = "SHA256withRSA";
private static final String CIPHER_TRANSFORMATION = "RSA/ECB/OAEPWithSHA-1AndMGF1Padding";
private static final int MAX_ENCRYPT_BLOCK_SIZE = 214;
private static final int MAX_DECRYPT_BLOCK_SIZE = 256;
/**
* Returns the KeyFactory object for the specified algorithm.
*
* @return the KeyFactory object for the specified algorithm
* @throws NoSuchAlgorithmException if the requested algorithm is not available
*/
private static KeyFactory getKeyFactory() throws NoSuchAlgorithmException
{
return KeyFactory.getInstance(ALGORITHM);
}
/**
* Generates a key pair using the RSA algorithm.
*
* @return the generated key pair
* @throws CryptographyException if there is an error generating the key pair
*/
public static KeyPair generateKeyPair() throws CryptographyException
{
try
{
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);
keyPairGenerator.initialize(KEY_SIZE);
return keyPairGenerator.generateKeyPair();
}
catch (NoSuchAlgorithmException e)
{
throw new CryptographyException("Failed to generate key pair, RSA algorithm not found", e);
}
}
/**
* Signs the given content using the provided private key.
*
* @param content the content to be signed
* @param privateKey the private key used for signing
* @return the base64 encoded signature of the content
* @throws CryptographyException if an error occurs in the cryptography process
*/
public static String signContent(String content, String privateKey) throws CryptographyException
{
return signContent(content, importPrivateKey(privateKey));
}
/**
* Signs the given content using the provided private key.
*
* @param content the content to be signed
* @param privateKey the private key used for signing
* @return the base64 encoded signature of the content
* @throws CryptographyException if an error occurs in the cryptography process
*/
public static String signContent(String content, PrivateKey privateKey) throws CryptographyException
{
// Convert content to sha1 hash
try
{
final Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initSign(privateKey);
signature.update(MessageDigest.getInstance("SHA-1").digest(content.getBytes()));
return Base64.getEncoder().encodeToString(signature.sign());
}
catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e)
{
throw new CryptographyException("Failed to sign content", e);
}
}
/**
* Verifies the content using the provided signature and public key.
*
* @param content the content to be verified
* @param signature the signature of the content
* @param publicKey the public key used for verification
* @return true if the content is verified, false otherwise
* @throws CryptographyException if an error occurs in the cryptography process
*/
public static boolean verifyContent(String content, String signature, String publicKey) throws CryptographyException
{
return verifyContent(content, signature, importPublicKey(publicKey));
}
/**
* Verifies the signature of the given content using the provided public key.
*
* @param content the content to be verified
* @param signature the signature to be verified
* @param publicKey the public key used for verification
* @return true if the signature is verified, false otherwise
* @throws CryptographyException if there is an error during the verification process
*/
public static boolean verifyContent(String content, String signature, PublicKey publicKey) throws CryptographyException
{
try
{
final Signature sign = Signature.getInstance(SIGNATURE_ALGORITHM);
sign.initVerify(publicKey);
sign.update(MessageDigest.getInstance("SHA-1").digest(content.getBytes()));
return sign.verify(Base64.getDecoder().decode(signature));
}
catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e)
{
throw new CryptographyException("Failed to verify content", e);
}
}
/**
* Generates a temporary signature for the given content using the provided private key.
*
* @param content the content to be signed
* @param privateKey the private key used for signing
* @return the base64 encoded signature of the content
* @throws CryptographyException if an error occurs in the cryptography process
*/
public static String temporarySignature(String content, String privateKey) throws CryptographyException
{
return signContent(String.format("%s|%d", content, (System.currentTimeMillis() / 1000 / TIME_BLOCK)), privateKey);
}
/**
* Generates a temporary signature for the given content using the provided private key.
*
* @param content the content to be signed
* @param privateKey the private key used for signing
* @return the base64 encoded signature of the content
* @throws CryptographyException if an error occurs in the cryptography process
*/
public static String temporarySignature(String content, PrivateKey privateKey) throws CryptographyException
{
return signContent(String.format("%s|%d", content, (System.currentTimeMillis() / 1000 / TIME_BLOCK)), privateKey);
}
/**
* Verifies the temporary signature of the given content using the specified public key and number of frames.
*
* @param content The content to verify the signature for. Must not be null.
* @param signature The signature to verify. Must not be null.
* @param publicKey The public key used for verification. Must not be null.
* @param frames The number of frames to use for verification. Must be a positive integer.
* @return true if the signature is valid, false otherwise.
* @throws CryptographyException if an error occurs during verification.
*/
public static boolean verifyTemporarySignature(String content, String signature, String publicKey, int frames) throws CryptographyException
{
return verifyTemporarySignature(content, signature, importPublicKey(publicKey), frames);
}
/**
* Verifies the temporary signature of the given content using the specified public key and number of frames.
*
* @param content The content to verify the signature for. Must not be null.
* @param signature The signature to verify. Must not be null.
* @param publicKey The public key used for verification. Must not be null.
* @param frames The number of frames to use for verification. Must be a positive integer.
* @return true if the signature is valid, false otherwise.
* @throws CryptographyException if an error occurs during verification.
*/
public static boolean verifyTemporarySignature(String content, String signature, PublicKey publicKey, int frames) throws CryptographyException
{
if (frames <= 0) frames = 1;
long currentTime = System.currentTimeMillis() / 1000 / TIME_BLOCK;
for (int i = 0; i < frames; i++)
{
if (verifyContent(String.format("%s|%d", content, currentTime - i), signature, publicKey))
{
return true;
}
}
return false;
}
/**
* Encrypts the given content using the provided public key.
*
* @param content the content to be encrypted
* @param publicKey the public key used for encryption
* @return the encrypted content as a String
* @throws CryptographyException if an error occurs during the encryption process
*/
public static String encrypt(String content, String publicKey) throws CryptographyException
{
return encrypt(content, importPublicKey(publicKey));
}
/**
* Encrypts the given content using the provided public key.
*
* @param content the content to be encrypted
* @param publicKey the public key used for encryption
* @return the encrypted content as a Base64 encoded string
* @throws CryptographyException if an error occurs during the encryption process
*/
public static String encrypt(String content, PublicKey publicKey) throws CryptographyException
{
try
{
Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] data = content.getBytes();
int inputLength = data.length;
List<Byte> outputList = new ArrayList<>();
for (int i = 0; i < inputLength; i += MAX_ENCRYPT_BLOCK_SIZE)
{
int length = Math.min(inputLength - i, MAX_ENCRYPT_BLOCK_SIZE);
byte[] encryptedBlock = cipher.doFinal(data, i, length);
for (byte b : encryptedBlock)
{
outputList.add(b);
}
}
byte[] output = new byte[outputList.size()];
for (int i = 0; i < outputList.size(); i++)
{
output[i] = outputList.get(i);
}
return Base64.getEncoder().encodeToString(output);
}
catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e)
{
throw new CryptographyException("Failed to encrypt content", e);
}
}
/**
* Decrypts the given content using the provided private key.
*
* @param content the encrypted content to be decrypted
* @param privateKey the private key used for decryption
* @return the decrypted content as a String
* @throws CryptographyException if an error occurs during the decryption process
*/
public static String decrypt(String content, String privateKey) throws CryptographyException
{
return decrypt(content, importPrivateKey(privateKey));
}
/**
* Decrypts the given content using the provided private key.
*
* @param content the encrypted content to be decrypted
* @param privateKey the private key used for decryption
* @return the decrypted content as a String
* @throws CryptographyException if an error occurs during the decryption process
*/
public static String decrypt(String content, PrivateKey privateKey) throws CryptographyException
{
try
{
Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] data = Base64.getDecoder().decode(content);
int inputLength = data.length;
List<Byte> outputList = new ArrayList<>();
for (int i = 0; i < inputLength; i += MAX_DECRYPT_BLOCK_SIZE)
{
int length = Math.min(inputLength - i, MAX_DECRYPT_BLOCK_SIZE);
byte[] decryptedBlock = cipher.doFinal(data, i, length);
for (byte b : decryptedBlock)
{
outputList.add(b);
}
}
byte[] output = new byte[outputList.size()];
for (int i = 0; i < outputList.size(); i++)
{
output[i] = outputList.get(i);
}
return new String(output);
}
catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e)
{
throw new CryptographyException("Failed to decrypt content", e);
}
}
/**
* Exports the given private key as a Base64 encoded string.
*
* @param privateKey the private key to be exported
* @return the Base64 encoded string representation of the private key
*/
public static String exportPrivateKey(PrivateKey privateKey)
{
return Base64.getEncoder().encodeToString(privateKey.getEncoded());
}
/**
* Exports the given private key as a Base64 encoded string.
*
* @param keyPair the private key to be exported
* @return the Base64 encoded string representation of the private key
*/
public static String exportPrivateKey(KeyPair keyPair)
{
return exportPrivateKey(keyPair.getPrivate());
}
/**
* Exports the given public key as a Base64 encoded string.
*
* @param publicKey the public key to be exported
* @return a Base64 encoded string representation of the public key
*/
public static String exportPublicKey(PublicKey publicKey)
{
return Base64.getEncoder().encodeToString(publicKey.getEncoded());
}
/**
* Exports the given public key as a Base64 encoded string.
*
* @param keyPair the public key to be exported
* @return a Base64 encoded string representation of the public key
*/
public static String exportPublicKey(KeyPair keyPair)
{
return exportPublicKey(keyPair.getPublic());
}
/**
* Imports a public key from a Base64 encoded string representation.
*
* @param publicKey the Base64 encoded string representation of the public key
* @return the imported PublicKey object
* @throws CryptographyException if there is an error importing the public key
*/
public static PublicKey importPublicKey(String publicKey) throws CryptographyException
{
try
{
byte[] keyBytes = Base64.getDecoder().decode(publicKey);
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
return getKeyFactory().generatePublic(spec);
}
catch (InvalidKeySpecException | NoSuchAlgorithmException e)
{
throw new CryptographyException("Failed to import public key", e);
}
}
/**
* Imports a private key from a Base64 encoded string representation.
*
* @param privateKey the Base64 encoded string representation of the private key
* @return the imported PrivateKey object
* @throws CryptographyException if there is an error importing the private key
*/
public static PrivateKey importPrivateKey(String privateKey) throws CryptographyException
{
try
{
byte[] keyBytes = Base64.getDecoder().decode(privateKey);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
return getKeyFactory().generatePrivate(spec);
}
catch (InvalidKeySpecException | NoSuchAlgorithmException | IllegalArgumentException e)
{
throw new CryptographyException("Failed to import private key", e);
}
}
}

View file

@ -0,0 +1,95 @@
package net.nosial.socialclient.classes;
import net.nosial.socialclient.exceptions.ResolutionException;
import net.nosial.socialclient.objects.ResolvedServer;
import javax.naming.NamingException;
import javax.naming.directory.*;
import java.util.Hashtable;
// Improved Resolver class
public class Resolver
{
/**
* Resolves the domain to obtain endpoint and public key from its DNS TXT records.
*
* @param domain The domain to be resolved.
* @return ResolvedServer An instance of ResolvedServer containing the endpoint and public key.
* @throws ResolutionException If the DNS TXT records cannot be resolved or if required information is missing.
*/
public static ResolvedServer resolveDomain(String domain) throws ResolutionException
{
final String endpointPrefix = "socialbox=";
final String keyPrefix = "socialbox-key=";
// Disable caching
Hashtable<String, String> env = new Hashtable<>();
env.put("com.sun.jndi.dns.timeout.retries", "1");
env.put("com.sun.jndi.dns.cache.ttl", "0");
env.put("com.sun.jndi.dns.cache.negative.ttl", "0");
try
{
DirContext dirContext = new InitialDirContext(env);
Attributes attributes = dirContext.getAttributes("dns:/" + domain, new String[]{"TXT"});
Attribute attributeTXT = attributes.get("TXT");
if (attributeTXT == null)
{
throw new ResolutionException("Failed to resolve DNS TXT records for " + domain);
}
String endpoint = null;
StringBuilder publicKeyBuilder = new StringBuilder();
boolean publicKeyFound = false;
for (int i = 0; i < attributeTXT.size(); i++)
{
String value = (String) attributeTXT.get(i);
// Remove surrounding quotes if present
if (value.startsWith("\"") && value.endsWith("\""))
{
value = value.substring(1, value.length() - 1);
}
// Split the value into fragments to find the relevant keys
String[] fragments = value.split("\\s+");
for (String fragment : fragments)
{
if (fragment.startsWith(endpointPrefix))
{
endpoint = fragment.substring(endpointPrefix.length());
}
else if (fragment.startsWith(keyPrefix))
{
publicKeyBuilder.append(fragment.substring(keyPrefix.length()));
publicKeyFound = true;
}
else if (publicKeyFound)
{
// If the public key has already started, append the fragment
publicKeyBuilder.append(fragment);
}
}
}
if (endpoint == null)
{
throw new ResolutionException("Failed to resolve RPC endpoint for " + domain);
}
if (publicKeyBuilder.isEmpty())
{
throw new ResolutionException("Failed to resolve public key for " + domain);
}
String publicKey = publicKeyBuilder.toString();
return new ResolvedServer(endpoint, publicKey);
}
catch (NamingException e)
{
throw new ResolutionException("Error resolving domain: " + e.getMessage(), e);
}
}
}

View file

@ -0,0 +1,361 @@
package net.nosial.socialclient.classes;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.squareup.okhttp.*;
import net.nosial.socialclient.abstracts.RpcResult;
import net.nosial.socialclient.exceptions.CryptographyException;
import net.nosial.socialclient.exceptions.ResolutionException;
import net.nosial.socialclient.objects.ResolvedServer;
import net.nosial.socialclient.objects.RpcRequest;
import java.io.IOException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class RpcClient
{
private final static ObjectMapper OBJECT_MAPPER = new ObjectMapper();
private final static String CLIENT_NAME = "SocialClient Java";
private final static String CLIENT_VERSION = "1.0";
private final static MediaType MEDIA_TYPE_JSON = MediaType.parse("application/json; charset=utf-8");
private final String domain;
private final String endpoint;
private final PublicKey serverPublicKey;
private final OkHttpClient httpClient;
private String sessionUuid;
private PrivateKey privateKey;
/**
* Initializes a new instance of the RpcClient class using the specified domain.
* This constructor resolves the domain to obtain the RPC endpoint and public key.
*
* @param domain The domain to be resolved for the RPC endpoint and public key.
* @throws ResolutionException If the domain cannot be resolved or if required information is missing.
* @throws CryptographyException If an error occurs while importing the public key.
*/
protected RpcClient(String domain) throws ResolutionException, CryptographyException
{
this.domain = domain;
this.httpClient = new OkHttpClient();
// Resolve the domain to get the endpoint and public key
ResolvedServer resolvedServer = Resolver.resolveDomain(domain);
this.endpoint = resolvedServer.getEndpoint();
this.serverPublicKey = Cryptography.importPublicKey(resolvedServer.getPublicKey());
this.sessionUuid = null;
this.privateKey = null;
}
/**
* Constructs an RpcClient instance with the specified endpoint and public key.
*
* @param endpoint The endpoint to which RPC requests will be sent.
* @param serverPublicKey The public key used for cryptographic operations with the RPC server.
*/
public RpcClient(String endpoint, PublicKey serverPublicKey)
{
this.domain = null;
this.endpoint = endpoint;
this.serverPublicKey = serverPublicKey;
this.httpClient = new OkHttpClient();
this.sessionUuid = null;
this.privateKey = null;
}
/**
* Retrieves the domain associated with this RpcClient instance.
*
* @return the domain as a string.
*/
public String getDomain()
{
return this.domain;
}
/**
* Returns the RPC endpoint for the client.
*
* @return the endpoint as a String.
*/
public String getEndpoint()
{
return this.endpoint;
}
/**
* Returns the public key associated with the RpcClient instance.
*
* @return the public key as a PublicKey object.
*/
public PublicKey getServerPublicKey()
{
return this.serverPublicKey;
}
/**
* Retrieves the session UUID associated with the RpcClient instance.
*
* @return the session UUID as a string.
*/
public String getSessionUuid()
{
return this.sessionUuid;
}
/**
* Sets the session UUID and imports the provided private key.
*
* @param sessionUuid the unique identifier for this session
* @param privateKey the private key to be imported
* @throws CryptographyException if an error occurs while importing the private key
*/
public void setSession(String sessionUuid, String privateKey) throws CryptographyException
{
this.sessionUuid = sessionUuid;
this.privateKey = Cryptography.importPrivateKey(privateKey);
}
/**
* Sets the session details for the RPC client.
*
* @param sessionUuid the UUID of the session
* @param privateKey the private key associated with the session
*/
public void setSession(String sessionUuid, PrivateKey privateKey)
{
this.sessionUuid = sessionUuid;
this.privateKey = privateKey;
}
/**
* Clears the current session by setting the sessionUuid and privateKey fields to null.
* This method effectively logs out the user from the current session.
*/
public void clearSession()
{
this.sessionUuid = null;
this.privateKey = null;
}
/**
* Sends an RPC request with the provided JSON data and returns a list of RpcResult objects.
*
* @param jsonData the JSON-formatted string representing the RPC request data.
* @return a list of RpcResult objects representing the response(s) from the RPC server.
* @throws RuntimeException if there is an error during the request or response processing.
*/
@SuppressWarnings("unchecked")
public List<RpcResult> sendRequest(String jsonData)
{
final Request request = new Request.Builder()
{{
this.url(endpoint);
this.post(RequestBody.create(MEDIA_TYPE_JSON, jsonData));
this.addHeader("Client-Name", CLIENT_NAME);
this.addHeader("Client-Version", CLIENT_VERSION);
if(sessionUuid != null)
{
this.addHeader("Session-UUID", sessionUuid);
}
if(privateKey != null)
{
try
{
this.addHeader("Signature", Cryptography.signContent(jsonData, privateKey));
}
catch(CryptographyException e)
{
throw new RuntimeException(e);
}
}
}}.build();
try
{
final Response response = this.httpClient.newCall(request).execute();
final String responseString = response.body().string();
if (!response.isSuccessful())
{
if(!responseString.isEmpty())
{
throw new RuntimeException(responseString);
}
throw new RuntimeException("Failed to send request");
}
if(response.code() == 204)
{
// The response is empty
return new ArrayList<>();
}
Object decoded = decode(responseString);
// Singular object response
if(decoded instanceof List<?>)
{
List<Map<String, Object>> responseList = (List<Map<String, Object>>) decoded;
List<RpcResult> results = new ArrayList<>(responseList.size());
for(Map<String, Object> responseMap : responseList)
{
results.add(RpcResult.fromMap(responseMap));
}
return results;
}
if(decoded instanceof Map<?, ?>)
{
// Singular object response
List<RpcResult> results = new ArrayList<>(1);
results.add(RpcResult.fromMap((Map<String, Object>) decoded));
return results;
}
throw new RuntimeException("Failed to decode response");
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
/**
* Sends an RPC request to the configured endpoint and returns the first result.
*
* @param request the RPC request to be sent
* @return the first RPC result received from the response
*/
public RpcResult sendRequest(RpcRequest request)
{
return sendRequest(encode(request.toMap())).getFirst();
}
/**
* Sends a list of RPC requests to the server and returns their results as a list of RpcResult objects.
*
* @param requests the list of RPC requests to be sent
* @return a list of RpcResult objects containing the responses from the server
*/
public List<RpcResult> sendRequests(List<RpcRequest> requests)
{
return sendRequest(encode(requests));
}
/**
* Decodes a JSON string into a corresponding Java object. If the JSON string
* represents an object, it decodes it into a Map. If the JSON string represents
* an array, it decodes it into a List.
*
* @param json the JSON string to be decoded
* @return a decoded Java object which can be a Map or a List depending on the JSON structure
* @throws RuntimeException if the JSON string is neither an object nor an array, or if decoding fails
*/
private static Object decode(String json)
{
try
{
JsonNode jsonNode = OBJECT_MAPPER.readTree(json);
if (jsonNode.isObject())
{
return decodeJson(OBJECT_MAPPER.readValue(json, new TypeReference<>() {}));
}
else if (jsonNode.isArray())
{
return decodeList(OBJECT_MAPPER.readValue(json, new TypeReference<>() {}));
}
else
{
throw new RuntimeException("JSON is neither an object nor an array");
}
}
catch (JsonProcessingException e)
{
throw new RuntimeException("Failed to decode input", e);
}
}
/**
* Recursively decodes a JSON structure represented as a Map. If the value
* of an entry is another Map, it recursively decodes that Map. If the value
* is a List, it passes the List to the decodeList method.
*
* @param map the JSON structure to be decoded represented as a Map
* @return the decoded JSON structure as a Map
*/
@SuppressWarnings("unchecked")
private static Map<String, Object> decodeJson(Map<String, Object> map)
{
for (Map.Entry<String, Object> entry : map.entrySet())
{
if (entry.getValue() instanceof Map)
{
entry.setValue(decodeJson((Map<String, Object>) entry.getValue()));
}
else if (entry.getValue() instanceof List)
{
entry.setValue(decodeList((List<Object>) entry.getValue()));
}
}
return map;
}
/**
* Recursively decodes a list of objects, transforming any nested maps or lists within it.
*
* @param list the list of objects to be decoded
* @return the decoded list of objects, with all nested maps and lists transformed
*/
@SuppressWarnings("unchecked")
private static List<Object> decodeList(List<Object> list)
{
for (int i = 0; i < list.size(); i++)
{
final Object item = list.get(i);
if (item instanceof Map)
{
list.set(i, decodeJson((Map<String, Object>) item));
}
else if (item instanceof List)
{
list.set(i, decodeList((List<Object>) item));
}
}
return list;
}
/**
* Encodes the given input object to its JSON string representation.
*
* @param input the object to encode
* @return the JSON string representation of the input object
*/
private static String encode(Object input)
{
try
{
return OBJECT_MAPPER.writeValueAsString(input);
}
catch(JsonProcessingException e)
{
throw new RuntimeException("Failed to encode input to JSON Data", e);
}
}
}

View file

@ -0,0 +1,41 @@
package net.nosial.socialclient.enums;
public enum StandardErrorCodes
{
UNKNOWN(-1),
RPC_METHOD_NOT_FOUND(-1000),
RPC_INVALID_ARGUMENTS(-1001),
INTERNAL_SERVER_ERROR(-2000),
SERVER_UNAVAILABLE(-2001),
INVALID_PUBLIC_KEY(-3000),
SESSION_NOT_FOUND(-3001),
UNSUPPORTED_AUTHENTICATION_TYPE(-3002);
private final int code;
StandardErrorCodes(int code)
{
this.code = code;
}
public int getCode()
{
return this.code;
}
public static StandardErrorCodes resolveCode(int input)
{
for (StandardErrorCodes errorCode : StandardErrorCodes.values())
{
if (errorCode.getCode() == input)
{
return errorCode;
}
}
return UNKNOWN;
}
}

View file

@ -0,0 +1,14 @@
package net.nosial.socialclient.exceptions;
public class CryptographyException extends Exception
{
public CryptographyException(String message)
{
super(message);
}
public CryptographyException(String message, Throwable cause)
{
super(message, cause);
}
}

View file

@ -0,0 +1,14 @@
package net.nosial.socialclient.exceptions;
public class ResolutionException extends Exception
{
public ResolutionException(String message)
{
super(message);
}
public ResolutionException(String message, Throwable cause)
{
super(message, cause);
}
}

View file

@ -0,0 +1,6 @@
package net.nosial.socialclient.exceptions;
public class RpcException extends Exception
{
}

View file

@ -0,0 +1,6 @@
package net.nosial.socialclient.objects;
public class KeyPair
{
}

View file

@ -0,0 +1,23 @@
package net.nosial.socialclient.objects;
public class ResolvedServer
{
private final String endpoint;
private final String publicKey;
public ResolvedServer(String endpoint, String publicKey)
{
this.endpoint = endpoint;
this.publicKey = publicKey;
}
public String getEndpoint()
{
return this.endpoint;
}
public String getPublicKey()
{
return this.publicKey;
}
}

View file

@ -0,0 +1,79 @@
package net.nosial.socialclient.objects;
import net.nosial.socialclient.abstracts.RpcResult;
import net.nosial.socialclient.enums.StandardErrorCodes;
import java.util.Map;
public class RpcError extends RpcResult
{
private final String id;
private final String error;
private final StandardErrorCodes code;
/**
* Constructs an instance of RpcError using data from the given map.
*
* @param data a map containing the data for the error. Must include keys "id" with a String value,
* "error" with a String value, and "code" with an integer value which represents the error code.
*/
public RpcError(Map<String, Object> data)
{
super(false);
this.id = (String) data.get("id");
this.error = (String) data.get("error");
this.code = StandardErrorCodes.resolveCode((int)data.get("code"));
}
/**
* Retrieves the ID of the RPC error.
*
* @return the ID of the RPC error.
*/
public String getId()
{
return this.id;
}
/**
* Retrieves the error message associated with this RPC error.
*
* @return the error message as a String.
*/
public String getError()
{
return this.error;
}
/**
* Retrieves the standard error code associated with this RPC error.
*
* @return the standard error code.
*/
public StandardErrorCodes getCode()
{
return this.code;
}
/**
* Returns the current RpcError instance as the error response.
*
* @return the current RpcError instance.
*/
@Override
public RpcError getErrorResponse()
{
return this;
}
/**
* Returns null as RpcError does not provide a successful response.
*
* @return null since RpcError instances do not represent successful responses.
*/
@Override
public RpcResponse getResponse()
{
return null;
}
}

View file

@ -0,0 +1,134 @@
package net.nosial.socialclient.objects;
import java.util.HashMap;
import java.util.Map;
public class RpcRequest
{
private final String method;
private final String id;
private final Map<String, Object> params;
/**
* Initializes a new RpcRequest object using the provided data map.
*
* @param data a map containing the initial values for the RpcRequest.
* The map should have the following expected keys:
* - "method": a String representing the RPC method name
* - "id": a String representing the unique request identifier
* - "params": a Map<String, Object> containing parameters for the method call
*/
@SuppressWarnings("unchecked")
public RpcRequest(Map<String, Object> data)
{
this.method = (String) data.get("method");
this.id = (String) data.get("id");
this.params = (Map<String, Object>) data.get("params");
}
/**
* Constructs an RpcRequest with the specified method name, request id, and parameters.
*
* @param method the RPC method to be called
* @param id the unique identifier for this request
* @param params the parameters for the RPC method
*/
public RpcRequest(String method, String id, Map<String, Object> params)
{
this.method = method;
this.id = id;
this.params = params;
}
/**
* Constructs an RPC request with a specified method and parameters.
* The request ID will be set to null.
*
* @param method the name of the method being called
* @param params the parameters to be sent with the method call
*/
public RpcRequest(String method, Map<String, Object> params)
{
this.id = null;
this.method = method;
this.params = params;
}
/**
* Constructs an RpcRequest with the specified method and id.
*
* @param method The method name for the RPC request.
* @param id The id of the RPC request.
*/
public RpcRequest(String method, String id)
{
this.method = method;
this.id = id;
this.params = null;
}
/**
* Constructs an RpcRequest with a specified method and no parameters or ID.
*
* @param method the RPC method name for the request
*/
public RpcRequest(String method)
{
this.id = null;
this.method = method;
this.params = null;
}
/**
* Retrieves the identifier of the RPC request.
*
* @return the identifier of the request.
*/
public String getId()
{
return this.id;
}
/**
* Gets the method name associated with the RPC request.
*
* @return the method name as a String.
*/
public String getMethod()
{
return this.method;
}
/**
* Retrieves the parameters map associated with the RPC request.
*
* @return a Map containing the parameters of the RPC request.
*/
public Map<String, Object> getParams()
{
return this.params;
}
/**
* Converts the current RpcRequest object into a map representation.
*
* @return a map containing the method, id, and params of the RpcRequest, if present.
*/
public Map<String, Object> toMap()
{
HashMap<String, Object> map = new HashMap<>();
map.put("method", this.method);
if(this.id != null)
{
map.put("id", this.id);
}
if(this.params != null)
{
map.put("params", this.params);
}
return map;
}
}

View file

@ -0,0 +1,68 @@
package net.nosial.socialclient.objects;
import net.nosial.socialclient.abstracts.RpcResult;
import java.util.Map;
public class RpcResponse extends RpcResult
{
private final String id;
private final Object result;
/**
* Constructs an instance of RpcResponse using data from the given map.
*
* @param data a map containing the data for the response. Must include an "id" key with a String value
* and optionally a "result" key with any Object value.
*/
public RpcResponse(Map<String, Object> data)
{
super(true);
this.id = (String) data.get("id");
this.result = data.get("result");
}
/**
* Retrieves the ID of the RPC response.
*
* @return the ID of the RPC response.
*/
public String getId()
{
return this.id;
}
/**
* Retrieves the result object from the RPC response.
*
* @return the result object, which can be of any type
*/
public Object getResult()
{
return this.result;
}
/**
* Returns the error response for an RPC operation. For the
* RpcResponse class, an error response is always null as
* it's assumed to be successful with no errors.
*
* @return null as RpcResponse does not have an error response.
*/
@Override
public RpcError getErrorResponse()
{
return null;
}
/**
* Retrieves the current RpcResponse instance.
*
* @return the current RpcResponse instance.
*/
@Override
public RpcResponse getResponse()
{
return this;
}
}

View file

@ -0,0 +1,262 @@
package net.nosial.socialclient.classes;
import net.nosial.socialclient.exceptions.CryptographyException;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.security.KeyPair;
import java.util.Collections;
import static org.junit.jupiter.api.Assertions.*;
class CryptographyTest
{
/**
* Method under test: Cryptography::generateKeyPair
*/
@Test
public void testGenerateKeyPair_Success() throws CryptographyException {
// action
KeyPair keyPair = Cryptography.generateKeyPair();
// assert
Assertions.assertNotNull(keyPair, "KeyPair should not be null");
Assertions.assertNotNull(keyPair.getPrivate(), "PrivateKey should not be null");
Assertions.assertTrue(keyPair.getPrivate().getAlgorithm().contains("RSA"), "PrivateKey algorithm should be RSA");
Assertions.assertNotNull(keyPair.getPublic(), "PublicKey should not be null");
Assertions.assertTrue(keyPair.getPublic().getAlgorithm().contains("RSA"), "PublicKey algorithm should be RSA");
}
@Test
public void testSignContent_Success() throws CryptographyException {
// Arrange
String content = "ExampleContent";
KeyPair keyPair = Cryptography.generateKeyPair();
String privateKey = Cryptography.exportPrivateKey(keyPair);
// Act
String signature = Cryptography.signContent(content, privateKey);
// Assert
assertNotNull(signature, "Signature should not be null");
assertTrue(Cryptography.verifyContent(content, signature, Cryptography.exportPublicKey(keyPair)), "Signed content should be verified successfully");
}
@Test
public void testSignContent_WithInvalidPrivateKey_ShouldThrowException() {
// Arrange
String content = "ExampleContent";
String invalidPrivateKey = "InvalidPrivateKey";
// Assert
assertThrows(CryptographyException.class, () -> Cryptography.signContent(content, invalidPrivateKey), "SignContent should throw CryptographyException with invalid private key");
}
@Test
public void testVerifyContent_Success() throws CryptographyException {
// Arrange
String content = "ExampleContent";
KeyPair keyPair = Cryptography.generateKeyPair();
String privateKey = Cryptography.exportPrivateKey(keyPair);
String signature = Cryptography.signContent(content, privateKey);
String publicKey = Cryptography.exportPublicKey(keyPair);
// Act & assert
assertTrue(Cryptography.verifyContent(content, signature, publicKey), "Content should be verified successfully with valid inputs");
}
@Test
public void testVerifyContent_FailureWithInvalidSignature() throws CryptographyException {
// Arrange
String content = "ExampleContent";
String invalidSignature = "InvalidSignature";
KeyPair keyPair = Cryptography.generateKeyPair();
String publicKey = Cryptography.exportPublicKey(keyPair);
// Act & assert
assertThrows(CryptographyException.class, () -> Cryptography.verifyContent(content, invalidSignature, publicKey), "verifyContent should throw CryptographyException with invalid signature");
}
@Test
public void testVerifyContent_FailureWithInvalidPublicKey() throws CryptographyException {
// Arrange
String content = "ExampleContent";
String invalidPublicKey = "InvalidPublicKey";
KeyPair keyPair = Cryptography.generateKeyPair();
String privateKey = Cryptography.exportPrivateKey(keyPair);
String signature = Cryptography.signContent(content, privateKey);
// Act & assert
assertThrows(CryptographyException.class, () -> Cryptography.verifyContent(content, signature, invalidPublicKey), "verifyContent should throw CryptographyException with invalid public key");
}
@Test
public void testTemporarySignature_Success() throws CryptographyException {
// Arrange
String content = "ExampleContent";
KeyPair keyPair = Cryptography.generateKeyPair();
String privateKey = Cryptography.exportPrivateKey(keyPair);
// Act
String signature = Cryptography.temporarySignature(content, privateKey);
// Assert
Assertions.assertNotNull(signature, "Signature should not be null");
Assertions.assertTrue(Cryptography.verifyTemporarySignature(content, signature, Cryptography.exportPublicKey(keyPair), 1), "Signed content should be verified successfully");
}
@Test
public void testEncrypt_FailureWithNullPublicKey() {
// Arrange
String content = "ExampleContent";
String nullPublicKey = null;
// Act & Assert
Assertions.assertThrows(NullPointerException.class, () -> Cryptography.encrypt(content, nullPublicKey), "encrypt should throw NullPointerException when public key is null");
}
@Test
public void testTemporarySignature_FailureWithInvalidPrivateKey() {
// Arrange
String content = "ExampleContent";
String invalidPrivateKey = "InvalidPrivateKey";
// Assert
Assertions.assertThrows(CryptographyException.class, () -> Cryptography.temporarySignature(content, invalidPrivateKey), "temporarySignature should throw CryptographyException with invalid private key");
}
@Test
public void testTemporarySignature_FailureWithInvalidContent() throws CryptographyException {
// Arrange
String content = "ExampleContent";
String invalidContent = "InvalidExampleContent";
KeyPair keyPair = Cryptography.generateKeyPair();
String privateKey = Cryptography.exportPrivateKey(keyPair);
// Act
String signature = Cryptography.temporarySignature(content, privateKey);
// Assert
Assertions.assertFalse(Cryptography.verifyTemporarySignature(invalidContent, signature, Cryptography.exportPublicKey(keyPair), 1), "Temporary signature should fail to verify with invalid content");
}
/**
* Method under test: Cryptography::encrypt
*/
@Test
public void testEncrypt_Success() throws CryptographyException {
// Arrange
String content = "ExampleContent";
KeyPair keyPair = Cryptography.generateKeyPair();
String publicKey = Cryptography.exportPublicKey(keyPair);
// Act
String encryptedContent = Cryptography.encrypt(content, publicKey);
// Assert
Assertions.assertNotNull(encryptedContent, "Encrypted content should not be null");
}
@Test
public void testEncrypt_FailureWithInvalidPublicKey() {
// Arrange
String content = "ExampleContent";
String invalidPublicKey = "InvalidPublicKey";
// Act & Assert
Assertions.assertThrows(CryptographyException.class, () -> Cryptography.encrypt(content, invalidPublicKey), "encrypt should throw CryptographyException with invalid public key");
}
/**
* Method under test: Cryptography::decrypt
*/
@Test
public void testDecrypt_Success() throws CryptographyException {
// Arrange
String originalContent = "ExampleContent";
KeyPair keyPair = Cryptography.generateKeyPair();
String publicKey = Cryptography.exportPublicKey(keyPair);
String privateKey = Cryptography.exportPrivateKey(keyPair);
String encryptedContent = Cryptography.encrypt(originalContent, publicKey);
//Act
String decryptedContent = Cryptography.decrypt(encryptedContent, privateKey);
// Assert
Assertions.assertNotNull(decryptedContent, "Decrypted content should not be null");
Assertions.assertEquals(originalContent, decryptedContent, "Decrypted content should be equals to original content");
}
@Test
public void testDecrypt_FailureWithInvalidPrivateKey() {
// Arrange
String encryptedContent = "ExampleEncryptedContent";
String invalidPrivateKey = "InvalidPrivateKey";
// Act & Assert
Assertions.assertThrows(CryptographyException.class, () -> Cryptography.decrypt(encryptedContent, invalidPrivateKey), "decrypt should throw CryptographyException with invalid private key");
}
@Test
public void testDecrypt_FailureWithNullPrivateKey() {
// Arrange
String encryptedContent = "ExampleEncryptedContent";
String nullPrivateKey = null;
// Act & Assert
Assertions.assertThrows(NullPointerException.class, () -> Cryptography.decrypt(encryptedContent, nullPrivateKey), "decrypt should throw NullPointerException when private key is null");
}
@Test
public void testEncrypt_NullContent_shouldThrowCryptographyException() throws CryptographyException {
// Arrange
String content = null;
KeyPair keyPair = Cryptography.generateKeyPair();
String publicKey = Cryptography.exportPublicKey(keyPair);
// Assert
assertThrows(NullPointerException.class, () -> Cryptography.encrypt(content, publicKey), "Encrypt should throw NullPointerException when content is null");
}
@Test
public void testEncrypt_EmptyContent_shouldRunSuccessfully() throws CryptographyException {
// Arrange
String content = "";
KeyPair keyPair = Cryptography.generateKeyPair();
String publicKey = Cryptography.exportPublicKey(keyPair);
// Act
String encryptedContent = Cryptography.encrypt(content, publicKey);
// Assert
assertNotNull(encryptedContent, "Encrypted content should not be null");
}
@Test
public void testEncrypt_LargeContent_shouldRunSuccessfully() throws CryptographyException {
// Arrange
String content = String.join("", Collections.nCopies(50000, "a"));
KeyPair keyPair = Cryptography.generateKeyPair();
String publicKey = Cryptography.exportPublicKey(keyPair);
// Act
String encryptedContent = Cryptography.encrypt(content, publicKey);
// Assert
assertNotNull(encryptedContent, "Encrypted content should not be null");
}
@Test
public void testEncrypt_InvalidPublicKey_ShouldThrowException() {
// Arrange
String content = "ExampleContent";
String invalidPublicKey = "InvalidPublicKey";
// Assert
assertThrows(CryptographyException.class, () -> Cryptography.encrypt(content, invalidPublicKey), "Encrypt should throw CryptographyException when public key is invalid");
}
}

View file

@ -0,0 +1,36 @@
package net.nosial.socialclient.classes;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.squareup.okhttp.*;
import net.nosial.socialclient.abstracts.RpcResult;
import net.nosial.socialclient.exceptions.CryptographyException;
import net.nosial.socialclient.exceptions.ResolutionException;
import net.nosial.socialclient.objects.RpcRequest;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
public class RpcClientTest {
@Test
public void testSendRequest() {
RpcClient rpcClient = null;
try
{
rpcClient = new RpcClient("n64.cc");
}
catch (Exception e)
{
fail(e.getMessage(), e);
}
RpcResult result = rpcClient.sendRequest(new RpcRequest("ping", "abcd123"));
assertNotNull(result);
}
}