diff --git a/.github/workflows/e2e_pr.yaml b/.github/workflows/e2e_pr.yaml
new file mode 100644
index 0000000..080fe19
--- /dev/null
+++ b/.github/workflows/e2e_pr.yaml
@@ -0,0 +1,149 @@
+# name: CI for ssh-mapper
+# on: [push, pull_request]
+
+# jobs:
+# ssh-mapper-dropbear-server:
+# if: ${{ !contains(github.event.head_commit.message, 'ci skip') }}
+# runs-on: ubuntu-latest
+# # strategy:
+# # fail-fast: true
+# steps:
+# - name: Checkout code
+# uses: actions/checkout@v4
+
+# - name: Start learning DropbearSSH server
+# uses: hoverkraft-tech/compose-action@v2.0.1
+# with:
+# cwd: experiments/orchestration
+# compose-file: "docker-compose-dropbear.yaml"
+# up-flags: "--build -d"
+
+# - name: Verify Result
+# run: |
+# # wait for the learning to be over
+# while [ "$( docker container inspect -f '{{.State.Running}}' dropbear-learner )" = "true" ]; do date; echo "still learning"; sleep 5; done
+# docker logs dropbear-ssh
+# docker logs dropbear-mapper
+# echo "pwd: "
+# pwd
+# echo "ls: "
+# ls experiments/orchestration/learner_output_dropbear
+# experiments/scripts/diff_hyps.sh experiments/results/servers/dropbear experiments/orchestration/learner_output_dropbear 3
+
+# ssh-mapper-openssh7-server:
+# if: ${{ !contains(github.event.head_commit.message, 'ci skip') }}
+# runs-on: ubuntu-latest
+# # strategy:
+# # fail-fast: true
+# steps:
+# - name: Checkout code
+# uses: actions/checkout@v4
+
+# - name: Start learning OpenSSH7 server
+# run: pushd experiments/scripts/ && ./start_learning.sh openssh7 && popd
+
+# - name: Verify Result
+# run: |
+# # wait for the learning to be over
+# while [ "$( docker container inspect -f '{{.State.Running}}' openssh-learner7 )" = "true" ]; do date; echo "still learning"; sleep 5; done
+# experiments/scripts/diff_hyps.sh experiments/results/servers/openssh7 experiments/orchestration/learner_output_openssh7 3
+
+# ssh-mapper-openssh8-server:
+# if: ${{ !contains(github.event.head_commit.message, 'ci skip') }}
+# runs-on: ubuntu-latest
+# # strategy:
+# # fail-fast: true
+# steps:
+# - name: Checkout code
+# uses: actions/checkout@v4
+
+# - name: Start learning OpenSSH8 server
+# run: pushd experiments/scripts/ && ./start_learning.sh openssh8 && popd
+
+# - name: Verify Result
+# run: |
+# # wait for the learning to be over
+# while [ "$( docker container inspect -f '{{.State.Running}}' openssh-learner8 )" = "true" ]; do date; echo "still learning"; sleep 5; done
+# experiments/scripts/diff_hyps.sh experiments/results/servers/openssh8 experiments/orchestration/learner_output_openssh8 2
+
+name: CI for SSH Mapper (Mealy Learning)
+on: [push, pull_request]
+
+jobs:
+ # This job performs Mealy learning for the Dropbear SSH server
+ ssh-mapper-dropbear-mealy:
+ # Skip this job if the commit message contains 'ci skip'
+ if: ${{ !contains(github.event.head_commit.message, 'ci skip') }}
+
+ # Use the latest Ubuntu runner provided by GitHub
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ # Uses the official GitHub Action to check out your repository code
+ uses: actions/checkout@v4
+
+ - name: Start learning Dropbear SSH server (Mealy Mode)
+ # Navigate to the scripts directory, execute start_learning.sh for Dropbear, then return.
+ # The start_learning.sh script handles its own pathing to the orchestration directory.
+ run: |
+ echo "Changing directory to experiments/scripts/ and starting learning..."
+ pushd experiments/scripts/
+ ./start_learning.sh dropbear # 'dropbear' as the single argument triggers Mealy learning
+ popd
+ echo "Learning process initiated."
+
+ - name: Verify Learning Result
+ # This step waits for the learner to complete and then verifies its output.
+ run: |
+ echo "--- Verifying Learning Results ---"
+
+ # IMPORTANT: We need to get the exact container ID for robust inspection.
+ # We assume the Docker Compose service for the learner is named 'learner' in your docker-compose-dropbear.yaml.
+ # The docker compose command must be run from the directory where docker-compose.yaml resides.
+ echo "Retrieving Learner Container ID..."
+ pushd experiments/orchestration/ # Temporarily change to the orchestration directory
+ # 'ps -q learner' gets the ID of the 'learner' service in the background
+ LEARNER_CONTAINER_ID=$(docker compose ps -q learner)
+ popd # Return to the repository root
+
+ if [ -z "${LEARNER_CONTAINER_ID}" ]; then
+ echo "Error: Learner container ID could not be found." >&2
+ echo "Please ensure your 'docker-compose-dropbear.yaml' has a service named 'learner' and it starts successfully." >&2
+ exit 1
+ fi
+ echo "Learner Container ID: ${LEARNER_CONTAINER_ID}"
+
+ # Wait for the learning process to be over (i.e., the container stops running)
+ echo "Waiting for learner container to stop..."
+ # The '2>/dev/null || echo "false"' part handles cases where the container might have already stopped
+ # or if 'docker inspect' fails for some other reason, preventing the script from breaking.
+ while [ "$(docker container inspect -f '{{.State.Running}}' "${LEARNER_CONTAINER_ID}" 2>/dev/null || echo "false")" = "true" ]; do
+ date # Print current date/time for progress tracking
+ echo "Still learning... (waiting for container ${LEARNER_CONTAINER_ID} to stop)"
+ sleep 5 # Wait for 5 seconds before checking again
+ done
+ echo "Learner container has stopped."
+
+ # --- Debugging Information (Essential for troubleshooting if verification fails) ---
+ echo "--- Debugging Learner Container Logs ---"
+ echo "Logs for container ${LEARNER_CONTAINER_ID}:"
+ docker logs "${LEARNER_CONTAINER_ID}" || echo "Could not retrieve logs for ${LEARNER_CONTAINER_ID} (it might have been removed)." >&2
+
+ echo "Exit Code for container ${LEARNER_CONTAINER_ID}:"
+ # Get the exit code; || true prevents this debug line from failing the step itself
+ docker inspect -f '{{.State.ExitCode}}' "${LEARNER_CONTAINER_ID}" || true
+ echo "--- End Debugging Learner Container ---"
+
+ # List contents of the output directory to verify files were generated
+ echo "Current working directory before listing output: $(pwd)"
+ echo "Listing contents of output directory: experiments/orchestration/learner_output_dropbear/"
+ # '-laR' provides long listing, all files, and recursive listing for subdirectories
+ ls -laR experiments/orchestration/learner_output_dropbear/ || { echo "Output directory not found or empty." >&2; exit 1; }
+
+ # Run the diff script to verify the generated hypothesis against a golden reference
+ echo "Running hypothesis comparison script..."
+ # This command's exit code determines the success/failure of this entire step
+ experiments/scripts/diff_hyps.sh experiments/results/servers/dropbear experiments/orchestration/learner_output_dropbear 3
+
+ echo "--- CI Verification Complete Successfully! ---"
diff --git a/.gitignore b/.gitignore
index 8a982ce..48d1f6a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,4 +27,5 @@ ssh-mapper/paramiko.egg-info/**
ssh-mapper/build/**
ssh-mapper/dist/**
ssh-mapper/mapper/__pycache__/**
-ssh-learner/target/**
\ No newline at end of file
+ssh-learner/target/**
+__pycache__/
\ No newline at end of file
diff --git a/README.md b/README.md
index f5867ad..435c1d9 100644
--- a/README.md
+++ b/README.md
@@ -29,7 +29,54 @@ The hacked version of paramiko can be found in manualparamiko directory. This fi
Timing variances were a nuisance. Timing is controlled at various places:
- The time.sleep() in mapper/mapper.py (controls set timeout between runs)
- read_multiple_responses() in manualparamiko/transport.py (has both a timeout between responses and total timeout -in case of multiple responses- argument).
-- Timeout based on the type of message can be set in the individual read_multiple_responses-calls.
+- Timeout based on the type of message can be set in the individual read_multiple_responses-calls.
+
+# Orchestration
+
+Since the components were containerized to make it easy to run and not worry about missing dependencies.
+
+The Mapper and Learner have their Dockerfiles in their own directories. The ssh servers, dropbear and openssh, have their dockerfiles sitting in the `experiments/orchestration/dockerfiles` directory.
+
+In order to start learning most of the setup has been orchestrated with the help of the docker-compose files. All the learners have volume mappings so the learner outputs are available on the host machine. If you need to change or add any extra arguments for any learner or the mapper, you will have to edit the corresponding compose file.
+
+There is a script to simplify the starting of the learning setup where the `ssh-key` pair is also generated which can be used by the mapper. The containers are built locally and then the learning starts.
+
+
+`cd experiments/scripts`
+
+`./start_learning.sh`
+
+
+Usage:
+ ./start_learning.sh
+ ./start_learning.sh
+
+ : Required. The SSH server to experiment with.
+ Must be one of: 'openssh7', 'openssh8', 'dropbear'.
+ : Optional. Specifies the Register Automata (RA) learning algorithm.
+ If provided, RA learning mode is activated.
+ Known algorithms: RALAMBDA RASTAR. Ignored if not applicable.
+
+Examples:
+Mealy Learning:
+./start_learning.sh openssh8
+./start_learning.sh dropbear
+
+RA Learning:
+./start_learning.sh dropbear RALAMBDA
+./start_learning.sh openssh8 RASTAR
+
+
+Whichever learner setup is run, based on the docker-compose file, the results will be generated in the volume mapped in each file, for instance:
+
+Mealy learning dropbear: `experiments/orchestration/learner_output_dropbear`
+
+Mealy learning openssh7: `experiments/orchestration/learner_output_openssh7`
+
+RA learning dropbear: `experiments/orchestration/learner_output_ra_dropbear`
+
+RA learning openssh7: `experiments/orchestration/learner_output_openssh7_ra`
+
# Trimming script (mypydot)
The python pydot package was altered to merge edges between the same nodes.
diff --git a/docker-compose.yaml b/docker-compose.yaml
deleted file mode 100644
index f30ea29..0000000
--- a/docker-compose.yaml
+++ /dev/null
@@ -1,51 +0,0 @@
-version: "3.8"
-
-services:
- # Dropbear service
- dropbear-ssh:
- build:
- context: .
- dockerfile: Dockerfile.dropbear
- container_name: dropbear-ssh
- ports:
- - "2222:22"
- networks:
- - fuzzer_network
- volumes:
- - ./ssh-keys:${HOME}/.ssh/:ro
-
- # SSH Mapper service
- ssh-mapper:
- build:
- context: ./ssh-mapper
- dockerfile: Dockerfile
- container_name: ssh-mapper
- ports:
- - "8080:8080"
- depends_on:
- - dropbear
- networks:
- - fuzzer_network
- volumes:
- - ./ssh-keys/learner-ssh.pub:/root/.ssh/id_rsa.pub:ro
- - ./ssh-keys/learner-ssh:/root/.ssh/id_rsa:ro
- command: -l 0.0.0.0:8080 -s dropbear-ssh:22 -f server
-
- # SSH Learner service
- ssh-learner:
- build:
- context: ./ssh-learner
- dockerfile: Dockerfile
- container_name: ssh-learner
- networks:
- - fuzzer_network
- depends_on:
- - dropbear
- - ssh-mapper
- volumes:
- - ./learner_output:/app/output_folder
- command: ["state-fuzzer-server", "-connect", "ssh-mapper:8080", "-alphabet", "/app/inputs/alphabets/servers/trans_auth.xml", "-ceReruns", "3", "-depth", "2", "-learningAlgorithm", "LSTAR", "-output", "/app/output_folder", "-ros", "-sshMapperAddress", "ssh-mapper:8080"]
-
-networks:
- fuzzer_network:
- driver: bridge
diff --git a/experiments/orchestration/docker-compose-dropbear-ra.yaml b/experiments/orchestration/docker-compose-dropbear-ra.yaml
new file mode 100644
index 0000000..95a7d21
--- /dev/null
+++ b/experiments/orchestration/docker-compose-dropbear-ra.yaml
@@ -0,0 +1,52 @@
+version: "3.8"
+
+services:
+ dropbear-ssh-ra:
+ build:
+ context: .
+ dockerfile: dockerfiles/Dockerfile.dropbear
+ container_name: dropbear-ssh-ra
+ ports:
+ - "2222:22"
+ networks:
+ - dropbear_network_ra
+ volumes:
+ - ./ssh-keys/learner-ssh.pub:/root/.ssh/id_rsa.pub:ro
+ - ./ssh-keys/learner-ssh:/root/.ssh/id_rsa:ro
+ entrypoint: ["sh", "-c", "cat /root/.ssh/id_rsa.pub >> /root/.ssh/authorized_keys && /usr/local/sbin/dropbear -F -E -j -k -s"]
+
+
+ dropbear-mapper-ra:
+ build:
+ context: ../../ssh-mapper
+ dockerfile: Dockerfile
+ container_name: dropbear-mapper-ra
+ ports:
+ - "8080:8080"
+ depends_on:
+ - dropbear-ssh-ra
+ networks:
+ - dropbear_network_ra
+ volumes:
+ - ./ssh-keys/learner-ssh.pub:/root/.ssh/id_rsa.pub:ro
+ - ./ssh-keys/learner-ssh:/root/.ssh/id_rsa:ro
+ command: -l 0.0.0.0:8080 -s dropbear-ssh-ra:22 -f server
+
+ dropbear-learner-ra:
+ build:
+ context: ../../ssh-learner
+ dockerfile: Dockerfile
+ container_name: dropbear-learner-ra
+ networks:
+ - dropbear_network_ra
+ depends_on:
+ - dropbear-ssh-ra
+ - dropbear-mapper-ra
+ volumes:
+ - ./learner_output_ra_dropbear:/app/output_folder
+ - ../../ssh-learner/inputs/alphabets/servers/:/app/inputs/alphabets/servers/
+ command: ["java", "-cp", "./ssh-learner.jar:./lib/*", "learner.RALearnerMain", "state-fuzzer-server", "-connect", "dropbear-mapper-ra:8080", "-alphabet", "/app/inputs/alphabets/servers/ra_input.xml", "-output", "/app/output_folder", "-sshMapperAddress", "dropbear-mapper-ra:8080", "-debug", "-learningAlgorithm", "${LEARNING_ALGORITHM}", "-equivalenceAlgorithms", "IO_RANDOM_WALK"]
+
+networks:
+ dropbear_network_ra:
+ driver: bridge
diff --git a/experiments/orchestration/docker-compose-dropbear.yaml b/experiments/orchestration/docker-compose-dropbear.yaml
new file mode 100644
index 0000000..9df445e
--- /dev/null
+++ b/experiments/orchestration/docker-compose-dropbear.yaml
@@ -0,0 +1,52 @@
+version: "3.8"
+
+services:
+ dropbear-ssh:
+ build:
+ context: .
+ dockerfile: dockerfiles/Dockerfile.dropbear
+ container_name: dropbear-ssh
+ # ports:
+ # - "2222:22"
+ networks:
+ - dropbear_network
+ volumes:
+ - ./ssh-keys/learner-ssh.pub:/root/.ssh/id_rsa.pub:ro
+ - ./ssh-keys/learner-ssh:/root/.ssh/id_rsa:ro
+ entrypoint: ["sh", "-c", "cat /root/.ssh/id_rsa.pub >> /root/.ssh/authorized_keys && /usr/local/sbin/dropbear -F -E -j -k -s"]
+
+
+ dropbear-mapper:
+ build:
+ context: ../../ssh-mapper
+ dockerfile: Dockerfile
+ container_name: dropbear-mapper
+ # ports:
+ # - "8080:8080"
+ depends_on:
+ - dropbear-ssh
+ networks:
+ - dropbear_network
+ volumes:
+ - ./ssh-keys/learner-ssh.pub:/root/.ssh/id_rsa.pub:ro
+ - ./ssh-keys/learner-ssh:/root/.ssh/id_rsa:ro
+ command: -l 0.0.0.0:8080 -s dropbear-ssh:22 -f server
+
+ dropbear-learner:
+ build:
+ context: ../../ssh-learner
+ dockerfile: Dockerfile
+ container_name: dropbear-learner
+ networks:
+ - dropbear_network
+ depends_on:
+ - dropbear-ssh
+ - dropbear-mapper
+ volumes:
+ - ./learner_output_dropbear:/app/output_folder
+ - ../../ssh-learner/inputs/alphabets/servers/:/app/inputs/alphabets/servers/
+ command: ["java", "-cp", "./ssh-learner.jar:./lib/*", "learner.Main", "state-fuzzer-server", "-connect", "dropbear-mapper:8080", "-alphabet", "/app/inputs/alphabets/servers/trans_auth.xml", "-output", "/app/output_folder", "-sshMapperAddress", "dropbear-mapper:8080", "-debug", "-roundLimit", "3"]
+
+networks:
+ dropbear_network:
+ driver: bridge
\ No newline at end of file
diff --git a/experiments/orchestration/docker-compose-openssh7-ra.yaml b/experiments/orchestration/docker-compose-openssh7-ra.yaml
new file mode 100644
index 0000000..3ed6ef6
--- /dev/null
+++ b/experiments/orchestration/docker-compose-openssh7-ra.yaml
@@ -0,0 +1,52 @@
+version: "3.8"
+
+services:
+ openssh-server7-ra:
+ build:
+ context: .
+ dockerfile: dockerfiles/Dockerfile.openssh7
+ container_name: openssh-server7-ra
+ # ports:
+ # - "2222:22"
+ networks:
+ - openssh_network7_ra
+ volumes:
+ - ./ssh-keys/learner-ssh.pub:/root/.ssh/id_rsa.pub:ro
+ - ./ssh-keys/learner-ssh:/root/.ssh/id_rsa:ro
+ entrypoint: ["sh", "-c", "cat /root/.ssh/id_rsa.pub >> /root/.ssh/authorized_keys && /usr/sbin/sshd -D -e"]
+
+
+ openssh-mapper7-ra:
+ build:
+ context: ../../ssh-mapper
+ dockerfile: Dockerfile
+ container_name: openssh-mapper7-ra
+ # ports:
+ # - "8080:8080"
+ depends_on:
+ - openssh-server7-ra
+ networks:
+ - openssh_network7_ra
+ volumes:
+ - ./ssh-keys/learner-ssh.pub:/root/.ssh/id_rsa.pub:ro
+ - ./ssh-keys/learner-ssh:/root/.ssh/id_rsa:ro
+ command: -l 0.0.0.0:8080 -s openssh-server7-ra:22 -c OpenSSH -f server
+
+ openssh-learner7-ra:
+ build:
+ context: ../../ssh-learner
+ dockerfile: Dockerfile
+ container_name: openssh-learner7-ra
+ networks:
+ - openssh_network7_ra
+ depends_on:
+ - openssh-server7-ra
+ - openssh-mapper7-ra
+ volumes:
+ - ./learner_output_openssh7_ra:/app/output_folder
+ - ../../ssh-learner/inputs/alphabets/servers/:/app/inputs/alphabets/servers/
+ command: ["java", "-cp", "./ssh-learner.jar:./lib/*", "learner.RALearnerMain", "state-fuzzer-server", "-connect", "openssh-mapper7-ra:8080", "-alphabet", "/app/inputs/alphabets/servers/ra_input.xml", "-output", "/app/output_folder", "-sshMapperAddress", "openssh-mapper7-ra:8080", "-debug", "-learningAlgorithm", "${LEARNING_ALGORITHM}", "-equivalenceAlgorithms", "IO_RANDOM_WALK"]
+
+networks:
+ openssh_network7_ra:
+ driver: bridge
diff --git a/experiments/orchestration/docker-compose-openssh7.yaml b/experiments/orchestration/docker-compose-openssh7.yaml
new file mode 100644
index 0000000..c3ef73c
--- /dev/null
+++ b/experiments/orchestration/docker-compose-openssh7.yaml
@@ -0,0 +1,52 @@
+version: "3.8"
+
+services:
+ openssh-server7:
+ build:
+ context: .
+ dockerfile: dockerfiles/Dockerfile.openssh7
+ container_name: openssh-server7
+ # ports:
+ # - "2222:22"
+ networks:
+ - openssh_network7
+ volumes:
+ - ./ssh-keys/learner-ssh.pub:/root/.ssh/id_rsa.pub:ro
+ - ./ssh-keys/learner-ssh:/root/.ssh/id_rsa:ro
+ entrypoint: ["sh", "-c", "cat /root/.ssh/id_rsa.pub >> /root/.ssh/authorized_keys && /usr/sbin/sshd -D -e"]
+
+
+ openssh-mapper7:
+ build:
+ context: ../../ssh-mapper
+ dockerfile: Dockerfile
+ container_name: openssh-mapper7
+ # ports:
+ # - "8080:8080"
+ depends_on:
+ - openssh-server7
+ networks:
+ - openssh_network7
+ volumes:
+ - ./ssh-keys/learner-ssh.pub:/root/.ssh/id_rsa.pub:ro
+ - ./ssh-keys/learner-ssh:/root/.ssh/id_rsa:ro
+ command: -l 0.0.0.0:8080 -s openssh-server7:22 -c OpenSSH -f server
+
+ openssh-learner7:
+ build:
+ context: ../../ssh-learner
+ dockerfile: Dockerfile
+ container_name: openssh-learner7
+ networks:
+ - openssh_network7
+ depends_on:
+ - openssh-server7
+ - openssh-mapper7
+ volumes:
+ - ./learner_output_openssh7:/app/output_folder
+ - ../../ssh-learner/inputs/alphabets/servers/:/app/inputs/alphabets/servers/
+ command: ["java", "-cp", "./ssh-learner.jar:./lib/*", "learner.Main", "state-fuzzer-server", "-connect", "openssh-mapper7:8080", "-alphabet", "/app/inputs/alphabets/servers/trans_auth.xml", "-output", "/app/output_folder", "-sshMapperAddress", "openssh-mapper7:8080","-debug", "-roundLimit", "3"]
+
+networks:
+ openssh_network7:
+ driver: bridge
diff --git a/experiments/orchestration/docker-compose-openssh8-ra.yaml b/experiments/orchestration/docker-compose-openssh8-ra.yaml
new file mode 100644
index 0000000..0647a2f
--- /dev/null
+++ b/experiments/orchestration/docker-compose-openssh8-ra.yaml
@@ -0,0 +1,52 @@
+version: "3.8"
+
+services:
+ openssh-server8-ra:
+ build:
+ context: .
+ dockerfile: dockerfiles/Dockerfile.openssh8
+ container_name: openssh-server8-ra
+ # ports:
+ # - "2222:22"
+ networks:
+ - openssh_network8_ra
+ volumes:
+ - ./ssh-keys/learner-ssh.pub:/root/.ssh/id_rsa.pub:ro
+ - ./ssh-keys/learner-ssh:/root/.ssh/id_rsa:ro
+ entrypoint: ["sh", "-c", "cat /root/.ssh/id_rsa.pub >> /root/.ssh/authorized_keys && /usr/sbin/sshd -D -e"]
+
+
+ openssh-mapper8-ra:
+ build:
+ context: ../../ssh-mapper
+ dockerfile: Dockerfile
+ container_name: openssh-mapper8-ra
+ # ports:
+ # - "8080:8080"
+ depends_on:
+ - openssh-server8-ra
+ networks:
+ - openssh_network8_ra
+ volumes:
+ - ./ssh-keys/learner-ssh.pub:/root/.ssh/id_rsa.pub:ro
+ - ./ssh-keys/learner-ssh:/root/.ssh/id_rsa:ro
+ command: -l 0.0.0.0:8080 -s openssh-server8-ra:22 -c OpenSSH -f server
+
+ openssh-learner8-ra:
+ build:
+ context: ../../ssh-learner
+ dockerfile: Dockerfile
+ container_name: openssh-learner8-ra
+ networks:
+ - openssh_network8_ra
+ depends_on:
+ - openssh-server8-ra
+ - openssh-mapper8-ra
+ volumes:
+ - ./learner_output_openssh8_ra:/app/output_folder
+ - ../../ssh-learner/inputs/alphabets/servers/:/app/inputs/alphabets/servers/
+ command: ["java", "-cp", "./ssh-learner.jar:./lib/*", "learner.RALearnerMain", "state-fuzzer-server", "-connect", "openssh-mapper8-ra:8080", "-alphabet", "/app/inputs/alphabets/servers/ra_input.xml", "-output", "/app/output_folder", "-sshMapperAddress", "openssh-mapper8-ra:8080", "-debug", "-learningAlgorithm", "${LEARNING_ALGORITHM}", "-equivalenceAlgorithms", "IO_RANDOM_WALK"]
+
+networks:
+ openssh_network8_ra:
+ driver: bridge
diff --git a/experiments/orchestration/docker-compose-openssh8.yaml b/experiments/orchestration/docker-compose-openssh8.yaml
new file mode 100644
index 0000000..55cefbc
--- /dev/null
+++ b/experiments/orchestration/docker-compose-openssh8.yaml
@@ -0,0 +1,52 @@
+version: "3.8"
+
+services:
+ openssh-server8:
+ build:
+ context: .
+ dockerfile: dockerfiles/Dockerfile.openssh8
+ container_name: openssh-server8
+ # ports:
+ # - "2222:22"
+ networks:
+ - openssh_network8
+ volumes:
+ - ./ssh-keys/learner-ssh.pub:/root/.ssh/id_rsa.pub:ro
+ - ./ssh-keys/learner-ssh:/root/.ssh/id_rsa:ro
+ entrypoint: ["sh", "-c", "cat /root/.ssh/id_rsa.pub >> /root/.ssh/authorized_keys && /usr/sbin/sshd -D -e"]
+
+
+ openssh-mapper8:
+ build:
+ context: ../../ssh-mapper
+ dockerfile: Dockerfile
+ container_name: openssh-mapper8
+ # ports:
+ # - "8080:8080"
+ depends_on:
+ - openssh-server8
+ networks:
+ - openssh_network8
+ volumes:
+ - ./ssh-keys/learner-ssh.pub:/root/.ssh/id_rsa.pub:ro
+ - ./ssh-keys/learner-ssh:/root/.ssh/id_rsa:ro
+ command: -l 0.0.0.0:8080 -s openssh-server8:22 -c OpenSSH -f server
+
+ openssh-learner8:
+ build:
+ context: ../../ssh-learner
+ dockerfile: Dockerfile
+ container_name: openssh-learner8
+ networks:
+ - openssh_network8
+ depends_on:
+ - openssh-server8
+ - openssh-mapper8
+ volumes:
+ - ./learner_output_openssh8:/app/output_folder
+ - ../../ssh-learner/inputs/alphabets/servers/:/app/inputs/alphabets/servers/
+ command: ["java", "-cp", "./ssh-learner.jar:./lib/*", "learner.Main", "state-fuzzer-server", "-connect", "openssh-mapper8:8080", "-alphabet", "/app/inputs/alphabets/servers/trans_auth.xml", "-output", "/app/output_folder", "-sshMapperAddress", "openssh-mapper8:8080","-debug", "-roundLimit", "3"]
+
+networks:
+ openssh_network8:
+ driver: bridge
diff --git a/Dockerfile.dropbear b/experiments/orchestration/dockerfiles/Dockerfile.dropbear
similarity index 93%
rename from Dockerfile.dropbear
rename to experiments/orchestration/dockerfiles/Dockerfile.dropbear
index 4b866a3..3d6422c 100644
--- a/Dockerfile.dropbear
+++ b/experiments/orchestration/dockerfiles/Dockerfile.dropbear
@@ -42,9 +42,7 @@ RUN dropbearkey -t rsa -f /etc/dropbear/dropbear_rsa_host_key && \
# Set Dropbear server to run on port 22 (override in `docker run -p`)
EXPOSE 22
-COPY ssh-keys/learner-ssh.pub .
-
-RUN mkdir ~/.ssh; cat learner-ssh.pub >> ~/.ssh/authorized_keys
+RUN mkdir ~/.ssh; chmod 700 ~/.ssh
# Command to run Dropbear SSH server
CMD ["/usr/local/sbin/dropbear", "-F", "-E", "-j", "-k", "-s"]
diff --git a/experiments/orchestration/dockerfiles/Dockerfile.openssh7 b/experiments/orchestration/dockerfiles/Dockerfile.openssh7
new file mode 100644
index 0000000..97ee2b0
--- /dev/null
+++ b/experiments/orchestration/dockerfiles/Dockerfile.openssh7
@@ -0,0 +1,16 @@
+FROM ubuntu:bionic
+
+USER root
+
+RUN apt-get update && apt-get install -y systemd openssh-server sudo vim
+
+RUN mkdir /run/sshd
+
+RUN echo "LogLevel DEBUG3" >> /etc/ssh/sshd_config
+
+RUN echo "KexAlgorithms +curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group14-sha256,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1" >> /etc/ssh/sshd_config && \
+ echo "PubkeyAcceptedKeyTypes=+ssh-rsa" >> /etc/ssh/sshd_config
+
+RUN systemctl enable ssh
+
+CMD ["/usr/sbin/sshd", "-D", "-e"]
diff --git a/experiments/orchestration/dockerfiles/Dockerfile.openssh8 b/experiments/orchestration/dockerfiles/Dockerfile.openssh8
new file mode 100644
index 0000000..f60427a
--- /dev/null
+++ b/experiments/orchestration/dockerfiles/Dockerfile.openssh8
@@ -0,0 +1,16 @@
+FROM ubuntu:focal
+
+USER root
+
+RUN apt-get update && apt-get install -y systemd openssh-server sudo vim
+
+RUN mkdir /run/sshd
+
+RUN echo "LogLevel DEBUG3" >> /etc/ssh/sshd_config
+
+RUN echo "KexAlgorithms +curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group14-sha256,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1" >> /etc/ssh/sshd_config && \
+ echo "PubkeyAcceptedKeyTypes=+ssh-rsa" >> /etc/ssh/sshd_config
+
+RUN systemctl enable ssh
+
+CMD ["/usr/sbin/sshd", "-D", "-e"]
diff --git a/experiments/orchestration/learner_output_dropbear/.gitignore b/experiments/orchestration/learner_output_dropbear/.gitignore
new file mode 100644
index 0000000..5e7d273
--- /dev/null
+++ b/experiments/orchestration/learner_output_dropbear/.gitignore
@@ -0,0 +1,4 @@
+# Ignore everything in this directory
+*
+# Except this file
+!.gitignore
diff --git a/experiments/orchestration/learner_output_openssh7/.gitignore b/experiments/orchestration/learner_output_openssh7/.gitignore
new file mode 100644
index 0000000..5e7d273
--- /dev/null
+++ b/experiments/orchestration/learner_output_openssh7/.gitignore
@@ -0,0 +1,4 @@
+# Ignore everything in this directory
+*
+# Except this file
+!.gitignore
diff --git a/experiments/orchestration/learner_output_openssh8/.gitignore b/experiments/orchestration/learner_output_openssh8/.gitignore
new file mode 100644
index 0000000..5e7d273
--- /dev/null
+++ b/experiments/orchestration/learner_output_openssh8/.gitignore
@@ -0,0 +1,4 @@
+# Ignore everything in this directory
+*
+# Except this file
+!.gitignore
diff --git a/experiments/results/servers/dropbear/alphabet.xml b/experiments/results/servers/dropbear/alphabet.xml
new file mode 100644
index 0000000..92ea4e1
--- /dev/null
+++ b/experiments/results/servers/dropbear/alphabet.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/experiments/results/servers/dropbear/command.args b/experiments/results/servers/dropbear/command.args
new file mode 100644
index 0000000..91964e8
--- /dev/null
+++ b/experiments/results/servers/dropbear/command.args
@@ -0,0 +1,11 @@
+state-fuzzer-server
+-connect
+ssh-mapper:8080
+-alphabet
+/app/inputs/alphabets/servers/trans_auth.xml
+-output
+/app/output_folder
+-sshMapperAddress
+ssh-mapper:8080
+-roundLimit
+3
diff --git a/experiments/results/servers/dropbear/hyp1.dot b/experiments/results/servers/dropbear/hyp1.dot
new file mode 100644
index 0000000..f56ec16
--- /dev/null
+++ b/experiments/results/servers/dropbear/hyp1.dot
@@ -0,0 +1,15 @@
+digraph g {
+
+ s0 [shape="circle" label="s0"];
+ s0 -> s0 [label="KEXINIT / KEXINIT"];
+ s0 -> s0 [label="KEX30 / KEXINIT+UNIMPL"];
+ s0 -> s0 [label="NEWKEYS / KEXINIT+UNIMPL"];
+ s0 -> s0 [label="SR_AUTH / KEXINIT"];
+ s0 -> s0 [label="SR_CONN / KEXINIT"];
+ s0 -> s0 [label="UA_PK_OK / KEXINIT"];
+ s0 -> s0 [label="UA_PK_NOK / KEXINIT"];
+
+__start0 [label="" shape="none" width="0" height="0"];
+__start0 -> s0;
+
+}
diff --git a/experiments/results/servers/dropbear/hyp2.dot b/experiments/results/servers/dropbear/hyp2.dot
new file mode 100644
index 0000000..ca96235
--- /dev/null
+++ b/experiments/results/servers/dropbear/hyp2.dot
@@ -0,0 +1,39 @@
+digraph g {
+
+ s0 [shape="circle" label="s0"];
+ s1 [shape="circle" label="s1"];
+ s2 [shape="circle" label="s2"];
+ s3 [shape="circle" label="s3"];
+ s0 -> s1 [label="KEXINIT / KEXINIT"];
+ s0 -> s1 [label="KEX30 / KEXINIT+UNIMPL"];
+ s0 -> s1 [label="NEWKEYS / KEXINIT+UNIMPL"];
+ s0 -> s2 [label="SR_AUTH / KEXINIT"];
+ s0 -> s2 [label="SR_CONN / KEXINIT"];
+ s0 -> s2 [label="UA_PK_OK / KEXINIT"];
+ s0 -> s2 [label="UA_PK_NOK / KEXINIT"];
+ s1 -> s2 [label="KEXINIT / NO_CONN"];
+ s1 -> s3 [label="KEX30 / KEX31+NEWKEYS+BUFFERED"];
+ s1 -> s1 [label="NEWKEYS / UNIMPL"];
+ s1 -> s2 [label="SR_AUTH / NO_CONN"];
+ s1 -> s2 [label="SR_CONN / NO_CONN"];
+ s1 -> s2 [label="UA_PK_OK / NO_CONN"];
+ s1 -> s2 [label="UA_PK_NOK / NO_CONN"];
+ s2 -> s2 [label="KEXINIT / NO_CONN"];
+ s2 -> s2 [label="KEX30 / NO_CONN"];
+ s2 -> s2 [label="NEWKEYS / NO_CONN"];
+ s2 -> s2 [label="SR_AUTH / NO_CONN"];
+ s2 -> s2 [label="SR_CONN / NO_CONN"];
+ s2 -> s2 [label="UA_PK_OK / NO_CONN"];
+ s2 -> s2 [label="UA_PK_NOK / NO_CONN"];
+ s3 -> s2 [label="KEXINIT / NO_CONN"];
+ s3 -> s3 [label="KEX30 / UNIMPL"];
+ s3 -> s0 [label="NEWKEYS / NO_RESP"];
+ s3 -> s2 [label="SR_AUTH / NO_CONN"];
+ s3 -> s2 [label="SR_CONN / NO_CONN"];
+ s3 -> s2 [label="UA_PK_OK / NO_CONN"];
+ s3 -> s2 [label="UA_PK_NOK / NO_CONN"];
+
+__start0 [label="" shape="none" width="0" height="0"];
+__start0 -> s0;
+
+}
diff --git a/experiments/results/servers/dropbear/hyp3.dot b/experiments/results/servers/dropbear/hyp3.dot
new file mode 100644
index 0000000..87c75d9
--- /dev/null
+++ b/experiments/results/servers/dropbear/hyp3.dot
@@ -0,0 +1,55 @@
+digraph g {
+
+ s0 [shape="circle" label="s0"];
+ s1 [shape="circle" label="s1"];
+ s2 [shape="circle" label="s2"];
+ s3 [shape="circle" label="s3"];
+ s4 [shape="circle" label="s4"];
+ s5 [shape="circle" label="s5"];
+ s0 -> s1 [label="KEXINIT / KEXINIT"];
+ s0 -> s1 [label="KEX30 / KEXINIT+UNIMPL"];
+ s0 -> s1 [label="NEWKEYS / KEXINIT+UNIMPL"];
+ s0 -> s2 [label="SR_AUTH / KEXINIT"];
+ s0 -> s2 [label="SR_CONN / KEXINIT"];
+ s0 -> s2 [label="UA_PK_OK / KEXINIT"];
+ s0 -> s2 [label="UA_PK_NOK / KEXINIT"];
+ s1 -> s2 [label="KEXINIT / NO_CONN"];
+ s1 -> s3 [label="KEX30 / KEX31+NEWKEYS+BUFFERED"];
+ s1 -> s1 [label="NEWKEYS / UNIMPL"];
+ s1 -> s2 [label="SR_AUTH / NO_CONN"];
+ s1 -> s2 [label="SR_CONN / NO_CONN"];
+ s1 -> s2 [label="UA_PK_OK / NO_CONN"];
+ s1 -> s2 [label="UA_PK_NOK / NO_CONN"];
+ s2 -> s2 [label="KEXINIT / NO_CONN"];
+ s2 -> s2 [label="KEX30 / NO_CONN"];
+ s2 -> s2 [label="NEWKEYS / NO_CONN"];
+ s2 -> s2 [label="SR_AUTH / NO_CONN"];
+ s2 -> s2 [label="SR_CONN / NO_CONN"];
+ s2 -> s2 [label="UA_PK_OK / NO_CONN"];
+ s2 -> s2 [label="UA_PK_NOK / NO_CONN"];
+ s3 -> s2 [label="KEXINIT / NO_CONN"];
+ s3 -> s3 [label="KEX30 / UNIMPL"];
+ s3 -> s4 [label="NEWKEYS / NO_RESP"];
+ s3 -> s2 [label="SR_AUTH / NO_CONN"];
+ s3 -> s2 [label="SR_CONN / NO_CONN"];
+ s3 -> s2 [label="UA_PK_OK / NO_CONN"];
+ s3 -> s2 [label="UA_PK_NOK / NO_CONN"];
+ s4 -> s5 [label="KEXINIT / KEXINIT"];
+ s4 -> s2 [label="KEX30 / NO_CONN"];
+ s4 -> s2 [label="NEWKEYS / NO_CONN"];
+ s4 -> s4 [label="SR_AUTH / SR_ACCEPT"];
+ s4 -> s2 [label="SR_CONN / NO_CONN"];
+ s4 -> s4 [label="UA_PK_OK / UA_FAILURE"];
+ s4 -> s4 [label="UA_PK_NOK / UA_FAILURE"];
+ s5 -> s2 [label="KEXINIT / NO_CONN"];
+ s5 -> s3 [label="KEX30 / KEX31+NEWKEYS"];
+ s5 -> s2 [label="NEWKEYS / UNIMPL"];
+ s5 -> s2 [label="SR_AUTH / NO_CONN"];
+ s5 -> s2 [label="SR_CONN / NO_CONN"];
+ s5 -> s2 [label="UA_PK_OK / NO_CONN"];
+ s5 -> s2 [label="UA_PK_NOK / NO_CONN"];
+
+__start0 [label="" shape="none" width="0" height="0"];
+__start0 -> s0;
+
+}
diff --git a/experiments/results/servers/dropbear/learnedModel.dot b/experiments/results/servers/dropbear/learnedModel.dot
new file mode 100644
index 0000000..87c75d9
--- /dev/null
+++ b/experiments/results/servers/dropbear/learnedModel.dot
@@ -0,0 +1,55 @@
+digraph g {
+
+ s0 [shape="circle" label="s0"];
+ s1 [shape="circle" label="s1"];
+ s2 [shape="circle" label="s2"];
+ s3 [shape="circle" label="s3"];
+ s4 [shape="circle" label="s4"];
+ s5 [shape="circle" label="s5"];
+ s0 -> s1 [label="KEXINIT / KEXINIT"];
+ s0 -> s1 [label="KEX30 / KEXINIT+UNIMPL"];
+ s0 -> s1 [label="NEWKEYS / KEXINIT+UNIMPL"];
+ s0 -> s2 [label="SR_AUTH / KEXINIT"];
+ s0 -> s2 [label="SR_CONN / KEXINIT"];
+ s0 -> s2 [label="UA_PK_OK / KEXINIT"];
+ s0 -> s2 [label="UA_PK_NOK / KEXINIT"];
+ s1 -> s2 [label="KEXINIT / NO_CONN"];
+ s1 -> s3 [label="KEX30 / KEX31+NEWKEYS+BUFFERED"];
+ s1 -> s1 [label="NEWKEYS / UNIMPL"];
+ s1 -> s2 [label="SR_AUTH / NO_CONN"];
+ s1 -> s2 [label="SR_CONN / NO_CONN"];
+ s1 -> s2 [label="UA_PK_OK / NO_CONN"];
+ s1 -> s2 [label="UA_PK_NOK / NO_CONN"];
+ s2 -> s2 [label="KEXINIT / NO_CONN"];
+ s2 -> s2 [label="KEX30 / NO_CONN"];
+ s2 -> s2 [label="NEWKEYS / NO_CONN"];
+ s2 -> s2 [label="SR_AUTH / NO_CONN"];
+ s2 -> s2 [label="SR_CONN / NO_CONN"];
+ s2 -> s2 [label="UA_PK_OK / NO_CONN"];
+ s2 -> s2 [label="UA_PK_NOK / NO_CONN"];
+ s3 -> s2 [label="KEXINIT / NO_CONN"];
+ s3 -> s3 [label="KEX30 / UNIMPL"];
+ s3 -> s4 [label="NEWKEYS / NO_RESP"];
+ s3 -> s2 [label="SR_AUTH / NO_CONN"];
+ s3 -> s2 [label="SR_CONN / NO_CONN"];
+ s3 -> s2 [label="UA_PK_OK / NO_CONN"];
+ s3 -> s2 [label="UA_PK_NOK / NO_CONN"];
+ s4 -> s5 [label="KEXINIT / KEXINIT"];
+ s4 -> s2 [label="KEX30 / NO_CONN"];
+ s4 -> s2 [label="NEWKEYS / NO_CONN"];
+ s4 -> s4 [label="SR_AUTH / SR_ACCEPT"];
+ s4 -> s2 [label="SR_CONN / NO_CONN"];
+ s4 -> s4 [label="UA_PK_OK / UA_FAILURE"];
+ s4 -> s4 [label="UA_PK_NOK / UA_FAILURE"];
+ s5 -> s2 [label="KEXINIT / NO_CONN"];
+ s5 -> s3 [label="KEX30 / KEX31+NEWKEYS"];
+ s5 -> s2 [label="NEWKEYS / UNIMPL"];
+ s5 -> s2 [label="SR_AUTH / NO_CONN"];
+ s5 -> s2 [label="SR_CONN / NO_CONN"];
+ s5 -> s2 [label="UA_PK_OK / NO_CONN"];
+ s5 -> s2 [label="UA_PK_NOK / NO_CONN"];
+
+__start0 [label="" shape="none" width="0" height="0"];
+__start0 -> s0;
+
+}
diff --git a/experiments/results/servers/dropbear/statistics.txt b/experiments/results/servers/dropbear/statistics.txt
new file mode 100644
index 0000000..f866c84
--- /dev/null
+++ b/experiments/results/servers/dropbear/statistics.txt
@@ -0,0 +1,89 @@
+=== RUN DESCRIPTION ===
+Alphabet: [KEXINIT, KEX30, NEWKEYS, SR_AUTH, SR_CONN, UA_PK_OK, UA_PK_NOK]
+
+StateFuzzerConfig Parameters
+Help: false
+Debug: false
+Quiet: false
+Output Directory: /app/output_folder
+Fuzzing Client: false
+
+LearnerConfig Parameters
+Alphabet: /app/inputs/alphabets/servers/trans_auth.xml
+Learning Algorithm: TTT
+Equivalence Algorithms: [RANDOM_WP_METHOD]
+Max Depth: 1
+Min Length: 5
+Max Length: 15
+Max Equivalence Queries: 1000
+Runs Per Membership Query: 1
+Random Length: 5
+Membership Query Retries: 3
+Log Queries: false
+Prob Reset: 0.0
+Test File: null
+Seed: 0
+Cache Tests: false
+Ce Sanitization: true
+Skip Non Det Tests: false
+Ce Reruns: 3
+Probabilistic Sanitization: true
+Time Limit: null
+Test Limit: null
+Round Limit: 3
+IOMode: true
+Probability of Choosing a New DataValue: 0.1
+Max Runs: 1
+Max Depth for Register Automata: 1
+Reset Runs: true
+Seed transitions: true
+Draw symbols uniformly: true
+
+SulConfig Parameters
+Fuzzing Role: client
+Fuzzing Client: true
+Response Wait: 100
+Input Response Timeout: null
+Command: null
+Terminate Command: null
+Process Dir: null
+Redirect Output Streams: false
+Process Trigger: NEW_TEST
+Start Wait: 0
+
+SulServerConfigStandard Parameters
+Connect to: ssh-mapper:8080
+
+MapperConfig Parameters
+Mapper Connection Config: null
+Repeating Outputs: null
+Socket Closed as Timeout: false
+Disabled as Timeout: false
+Merge Repeating: true
+
+SulAdapterConfig Parameters
+Adapter Port: null
+Adapter Address: localhost
+
+=== STATISTICS ===
+Learning finished: false
+Reason: hypothesis construction round limit reached
+Size of the input alphabet: 7
+Number of states: 6
+Number of hypotheses: 3
+Number of inputs: 584
+Number of tests: 130
+Number of learning inputs: 428
+Number of learning tests: 116
+Number of inputs up to last hypothesis: 584
+Number of tests up to last hypothesis: 130
+Time (ms) to learn model: 833275
+Counterexamples:
+CE 1:Query[ε|NEWKEYS SR_CONN NEWKEYS SR_CONN KEXINIT UA_PK_NOK NEWKEYS SR_CONN KEXINIT KEX30 SR_CONN / KEXINIT+UNIMPL NO_CONN NO_CONN NO_CONN NO_CONN NO_CONN NO_CONN NO_CONN NO_CONN NO_CONN NO_CONN]
+CE 2:Query[ε|KEXINIT KEX30 NEWKEYS KEX30 UA_PK_NOK SR_CONN SR_AUTH SR_AUTH KEX30 / KEXINIT KEX31+NEWKEYS+BUFFERED NO_RESP NO_CONN NO_CONN NO_CONN NO_CONN NO_CONN NO_CONN]
+Number of inputs when hypothesis was generated: [7, 269, 584]
+Number of tests when hypothesis was generated: [7, 81, 130]
+Time (ms) when hypothesis was generated: [13198, 334418, 833271]
+Number of inputs when counterexample was found: [51, 381]
+Number of tests when counterexample was found: [11, 91]
+Time (ms) when counterexample was found: [33044, 414891]
diff --git a/experiments/results/servers/openssh7/alphabet.xml b/experiments/results/servers/openssh7/alphabet.xml
new file mode 100644
index 0000000..f6ebcb5
--- /dev/null
+++ b/experiments/results/servers/openssh7/alphabet.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/experiments/results/servers/openssh7/command.args b/experiments/results/servers/openssh7/command.args
new file mode 100644
index 0000000..f280f2f
--- /dev/null
+++ b/experiments/results/servers/openssh7/command.args
@@ -0,0 +1,12 @@
+state-fuzzer-server
+-connect
+openssh-mapper7:8080
+-alphabet
+/app/inputs/alphabets/servers/trans_auth.xml
+-output
+/app/output_folder
+-sshMapperAddress
+openssh-mapper7:8080
+-debug
+-roundLimit
+3
diff --git a/experiments/results/servers/openssh7/error.msg b/experiments/results/servers/openssh7/error.msg
new file mode 100644
index 0000000..042f5fe
--- /dev/null
+++ b/experiments/results/servers/openssh7/error.msg
@@ -0,0 +1,21 @@
+Cannot invoke "String.hashCode()" because the return value of "com.github.protocolfuzzing.protocolstatefuzzer.components.sul.mapper.abstractsymbols.AbstractOutput.getName()" is null
+java.lang.NullPointerException: Cannot invoke "String.hashCode()" because the return value of "com.github.protocolfuzzing.protocolstatefuzzer.components.sul.mapper.abstractsymbols.AbstractOutput.getName()" is null
+ at com.github.protocolfuzzing.protocolstatefuzzer.components.sul.mapper.abstractsymbols.AbstractOutput.hashCode(AbstractOutput.java:218)
+ at net.automatalib.word.Word.hashCode(Word.java:242)
+ at java.base/java.util.HashMap.hash(HashMap.java:338)
+ at java.base/java.util.HashMap.computeIfAbsent(HashMap.java:1191)
+ at de.learnlib.datastructure.discriminationtree.SplitData.getIncoming(SplitData.java:110)
+ at de.learnlib.algorithm.ttt.base.AbstractTTTLearner.prepareSplit(AbstractTTTLearner.java:624)
+ at de.learnlib.algorithm.ttt.base.AbstractTTTLearner.finalizeDiscriminator(AbstractTTTLearner.java:554)
+ at de.learnlib.algorithm.ttt.base.AbstractTTTLearner.finalizeAny(AbstractTTTLearner.java:305)
+ at de.learnlib.algorithm.ttt.base.AbstractTTTLearner.refineHypothesisSingle(AbstractTTTLearner.java:215)
+ at de.learnlib.algorithm.ttt.mealy.TTTLearnerMealy.refineHypothesisSingle(TTTLearnerMealy.java:67)
+ at de.learnlib.algorithm.ttt.base.AbstractTTTLearner.refineHypothesis(AbstractTTTLearner.java:163)
+ at com.github.protocolfuzzing.protocolstatefuzzer.statefuzzer.core.StateFuzzerStandard.inferStateMachine(StateFuzzerStandard.java:164)
+ at com.github.protocolfuzzing.protocolstatefuzzer.statefuzzer.core.StateFuzzerStandard.startFuzzing(StateFuzzerStandard.java:84)
+ at com.github.protocolfuzzing.protocolstatefuzzer.entrypoints.CommandLineParser.executeCommand(CommandLineParser.java:355)
+ at com.github.protocolfuzzing.protocolstatefuzzer.entrypoints.CommandLineParser.parseAndExecuteCommand(CommandLineParser.java:226)
+ at com.github.protocolfuzzing.protocolstatefuzzer.entrypoints.CommandLineParser.parse(CommandLineParser.java:168)
+ at com.github.protocolfuzzing.protocolstatefuzzer.entrypoints.CommandLineParser.parse(CommandLineParser.java:212)
+ at learner.Main.runMealyLearner(Main.java:32)
+ at learner.Main.main(Main.java:21)
diff --git a/experiments/results/servers/openssh7/hyp1.dot b/experiments/results/servers/openssh7/hyp1.dot
new file mode 100644
index 0000000..99bfa7f
--- /dev/null
+++ b/experiments/results/servers/openssh7/hyp1.dot
@@ -0,0 +1,15 @@
+digraph g {
+
+ s0 [shape="circle" label="s0"];
+ s0 -> s0 [label="KEXINIT / KEXINIT"];
+ s0 -> s0 [label="KEX30 / KEXINIT+UNIMPL"];
+ s0 -> s0 [label="NEWKEYS / KEXINIT+UNIMPL"];
+ s0 -> s0 [label="SR_AUTH / KEXINIT+UNIMPL"];
+ s0 -> s0 [label="SR_CONN / KEXINIT+UNIMPL"];
+ s0 -> s0 [label="UA_PK_OK / KEXINIT"];
+ s0 -> s0 [label="UA_PK_NOK / KEXINIT"];
+
+__start0 [label="" shape="none" width="0" height="0"];
+__start0 -> s0;
+
+}
diff --git a/experiments/results/servers/openssh7/hyp2.dot b/experiments/results/servers/openssh7/hyp2.dot
new file mode 100644
index 0000000..cfb4db3
--- /dev/null
+++ b/experiments/results/servers/openssh7/hyp2.dot
@@ -0,0 +1,39 @@
+digraph g {
+
+ s0 [shape="circle" label="s0"];
+ s1 [shape="circle" label="s1"];
+ s2 [shape="circle" label="s2"];
+ s3 [shape="circle" label="s3"];
+ s0 -> s1 [label="KEXINIT / KEXINIT"];
+ s0 -> s1 [label="KEX30 / KEXINIT+UNIMPL"];
+ s0 -> s1 [label="NEWKEYS / KEXINIT+UNIMPL"];
+ s0 -> s1 [label="SR_AUTH / KEXINIT+UNIMPL"];
+ s0 -> s1 [label="SR_CONN / KEXINIT+UNIMPL"];
+ s0 -> s3 [label="UA_PK_OK / KEXINIT"];
+ s0 -> s3 [label="UA_PK_NOK / KEXINIT"];
+ s1 -> s3 [label="KEXINIT / NO_CONN"];
+ s1 -> s2 [label="KEX30 / KEX31+NEWKEYS+BUFFERED"];
+ s1 -> s1 [label="NEWKEYS / UNIMPL"];
+ s1 -> s1 [label="SR_AUTH / UNIMPL"];
+ s1 -> s1 [label="SR_CONN / UNIMPL"];
+ s1 -> s3 [label="UA_PK_OK / NO_CONN"];
+ s1 -> s3 [label="UA_PK_NOK / NO_CONN"];
+ s2 -> s2 [label="KEXINIT / UNIMPL"];
+ s2 -> s2 [label="KEX30 / UNIMPL"];
+ s2 -> s2 [label="NEWKEYS / NO_RESP"];
+ s2 -> s2 [label="SR_AUTH / UNIMPL"];
+ s2 -> s2 [label="SR_CONN / UNIMPL"];
+ s2 -> s3 [label="UA_PK_OK / NO_CONN"];
+ s2 -> s3 [label="UA_PK_NOK / NO_CONN"];
+ s3 -> s3 [label="KEXINIT / NO_CONN"];
+ s3 -> s3 [label="KEX30 / NO_CONN"];
+ s3 -> s3 [label="NEWKEYS / NO_CONN"];
+ s3 -> s3 [label="SR_AUTH / NO_CONN"];
+ s3 -> s3 [label="SR_CONN / NO_CONN"];
+ s3 -> s3 [label="UA_PK_OK / NO_CONN"];
+ s3 -> s3 [label="UA_PK_NOK / NO_CONN"];
+
+__start0 [label="" shape="none" width="0" height="0"];
+__start0 -> s0;
+
+}
diff --git a/experiments/results/servers/openssh7/hyp3.dot b/experiments/results/servers/openssh7/hyp3.dot
new file mode 100644
index 0000000..942e677
--- /dev/null
+++ b/experiments/results/servers/openssh7/hyp3.dot
@@ -0,0 +1,47 @@
+digraph g {
+
+ s0 [shape="circle" label="s0"];
+ s1 [shape="circle" label="s1"];
+ s2 [shape="circle" label="s2"];
+ s3 [shape="circle" label="s3"];
+ s4 [shape="circle" label="s4"];
+ s0 -> s1 [label="KEXINIT / KEXINIT"];
+ s0 -> s1 [label="KEX30 / KEXINIT+UNIMPL"];
+ s0 -> s1 [label="NEWKEYS / KEXINIT+UNIMPL"];
+ s0 -> s1 [label="SR_AUTH / KEXINIT+UNIMPL"];
+ s0 -> s1 [label="SR_CONN / KEXINIT+UNIMPL"];
+ s0 -> s3 [label="UA_PK_OK / KEXINIT"];
+ s0 -> s3 [label="UA_PK_NOK / KEXINIT"];
+ s1 -> s3 [label="KEXINIT / NO_CONN"];
+ s1 -> s2 [label="KEX30 / KEX31+NEWKEYS+BUFFERED"];
+ s1 -> s1 [label="NEWKEYS / UNIMPL"];
+ s1 -> s1 [label="SR_AUTH / UNIMPL"];
+ s1 -> s1 [label="SR_CONN / UNIMPL"];
+ s1 -> s3 [label="UA_PK_OK / NO_CONN"];
+ s1 -> s3 [label="UA_PK_NOK / NO_CONN"];
+ s2 -> s2 [label="KEXINIT / UNIMPL"];
+ s2 -> s2 [label="KEX30 / UNIMPL"];
+ s2 -> s4 [label="NEWKEYS / NO_RESP"];
+ s2 -> s2 [label="SR_AUTH / UNIMPL"];
+ s2 -> s2 [label="SR_CONN / UNIMPL"];
+ s2 -> s3 [label="UA_PK_OK / NO_CONN"];
+ s2 -> s3 [label="UA_PK_NOK / NO_CONN"];
+ s3 -> s3 [label="KEXINIT / NO_CONN"];
+ s3 -> s3 [label="KEX30 / NO_CONN"];
+ s3 -> s3 [label="NEWKEYS / NO_CONN"];
+ s3 -> s3 [label="SR_AUTH / NO_CONN"];
+ s3 -> s3 [label="SR_CONN / NO_CONN"];
+ s3 -> s3 [label="UA_PK_OK / NO_CONN"];
+ s3 -> s3 [label="UA_PK_NOK / NO_CONN"];
+ s4 -> s4 [label="KEXINIT / UNIMPL"];
+ s4 -> s4 [label="KEX30 / UNIMPL"];
+ s4 -> s3 [label="NEWKEYS / UNIMPL"];
+ s4 -> s4 [label="SR_AUTH / SR_ACCEPT"];
+ s4 -> s3 [label="SR_CONN / DISCONNECT"];
+ s4 -> s4 [label="UA_PK_OK / UNIMPL"];
+ s4 -> s4 [label="UA_PK_NOK / UNIMPL"];
+
+__start0 [label="" shape="none" width="0" height="0"];
+__start0 -> s0;
+
+}
diff --git a/experiments/results/servers/openssh7/learnedModel.dot b/experiments/results/servers/openssh7/learnedModel.dot
new file mode 100644
index 0000000..942e677
--- /dev/null
+++ b/experiments/results/servers/openssh7/learnedModel.dot
@@ -0,0 +1,47 @@
+digraph g {
+
+ s0 [shape="circle" label="s0"];
+ s1 [shape="circle" label="s1"];
+ s2 [shape="circle" label="s2"];
+ s3 [shape="circle" label="s3"];
+ s4 [shape="circle" label="s4"];
+ s0 -> s1 [label="KEXINIT / KEXINIT"];
+ s0 -> s1 [label="KEX30 / KEXINIT+UNIMPL"];
+ s0 -> s1 [label="NEWKEYS / KEXINIT+UNIMPL"];
+ s0 -> s1 [label="SR_AUTH / KEXINIT+UNIMPL"];
+ s0 -> s1 [label="SR_CONN / KEXINIT+UNIMPL"];
+ s0 -> s3 [label="UA_PK_OK / KEXINIT"];
+ s0 -> s3 [label="UA_PK_NOK / KEXINIT"];
+ s1 -> s3 [label="KEXINIT / NO_CONN"];
+ s1 -> s2 [label="KEX30 / KEX31+NEWKEYS+BUFFERED"];
+ s1 -> s1 [label="NEWKEYS / UNIMPL"];
+ s1 -> s1 [label="SR_AUTH / UNIMPL"];
+ s1 -> s1 [label="SR_CONN / UNIMPL"];
+ s1 -> s3 [label="UA_PK_OK / NO_CONN"];
+ s1 -> s3 [label="UA_PK_NOK / NO_CONN"];
+ s2 -> s2 [label="KEXINIT / UNIMPL"];
+ s2 -> s2 [label="KEX30 / UNIMPL"];
+ s2 -> s4 [label="NEWKEYS / NO_RESP"];
+ s2 -> s2 [label="SR_AUTH / UNIMPL"];
+ s2 -> s2 [label="SR_CONN / UNIMPL"];
+ s2 -> s3 [label="UA_PK_OK / NO_CONN"];
+ s2 -> s3 [label="UA_PK_NOK / NO_CONN"];
+ s3 -> s3 [label="KEXINIT / NO_CONN"];
+ s3 -> s3 [label="KEX30 / NO_CONN"];
+ s3 -> s3 [label="NEWKEYS / NO_CONN"];
+ s3 -> s3 [label="SR_AUTH / NO_CONN"];
+ s3 -> s3 [label="SR_CONN / NO_CONN"];
+ s3 -> s3 [label="UA_PK_OK / NO_CONN"];
+ s3 -> s3 [label="UA_PK_NOK / NO_CONN"];
+ s4 -> s4 [label="KEXINIT / UNIMPL"];
+ s4 -> s4 [label="KEX30 / UNIMPL"];
+ s4 -> s3 [label="NEWKEYS / UNIMPL"];
+ s4 -> s4 [label="SR_AUTH / SR_ACCEPT"];
+ s4 -> s3 [label="SR_CONN / DISCONNECT"];
+ s4 -> s4 [label="UA_PK_OK / UNIMPL"];
+ s4 -> s4 [label="UA_PK_NOK / UNIMPL"];
+
+__start0 [label="" shape="none" width="0" height="0"];
+__start0 -> s0;
+
+}
diff --git a/experiments/results/servers/openssh7/statistics.txt b/experiments/results/servers/openssh7/statistics.txt
new file mode 100644
index 0000000..3652fa6
--- /dev/null
+++ b/experiments/results/servers/openssh7/statistics.txt
@@ -0,0 +1,89 @@
+=== RUN DESCRIPTION ===
+Alphabet: [KEXINIT, KEX30, NEWKEYS, SR_AUTH, SR_CONN, UA_PK_OK, UA_PK_NOK]
+
+StateFuzzerConfig Parameters
+Help: false
+Debug: true
+Quiet: false
+Output Directory: /app/output_folder
+Fuzzing Client: false
+
+LearnerConfig Parameters
+Alphabet: /app/inputs/alphabets/servers/trans_auth.xml
+Learning Algorithm: TTT
+Equivalence Algorithms: [RANDOM_WP_METHOD]
+Max Depth: 1
+Min Length: 5
+Max Length: 15
+Max Equivalence Queries: 1000
+Runs Per Membership Query: 1
+Random Length: 5
+Membership Query Retries: 3
+Log Queries: false
+Prob Reset: 0.0
+Test File: null
+Seed: 0
+Cache Tests: false
+Ce Sanitization: true
+Skip Non Det Tests: false
+Ce Reruns: 3
+Probabilistic Sanitization: true
+Time Limit: null
+Test Limit: null
+Round Limit: 3
+IOMode: true
+Probability of Choosing a New DataValue: 0.1
+Max Runs: 1
+Max Depth for Register Automata: 1
+Reset Runs: true
+Seed transitions: true
+Draw symbols uniformly: true
+
+SulConfig Parameters
+Fuzzing Role: client
+Fuzzing Client: true
+Response Wait: 100
+Input Response Timeout: null
+Command: null
+Terminate Command: null
+Process Dir: null
+Redirect Output Streams: false
+Process Trigger: NEW_TEST
+Start Wait: 0
+
+SulServerConfigStandard Parameters
+Connect to: openssh-mapper7:8080
+
+MapperConfig Parameters
+Mapper Connection Config: null
+Repeating Outputs: null
+Socket Closed as Timeout: false
+Disabled as Timeout: false
+Merge Repeating: true
+
+SulAdapterConfig Parameters
+Adapter Port: null
+Adapter Address: localhost
+
+=== STATISTICS ===
+Learning finished: false
+Reason: hypothesis construction round limit reached
+Size of the input alphabet: 7
+Number of states: 5
+Number of hypotheses: 3
+Number of inputs: 412
+Number of tests: 107
+Number of learning inputs: 328
+Number of learning tests: 99
+Number of inputs up to last hypothesis: 412
+Number of tests up to last hypothesis: 107
+Time (ms) to learn model: 709454
+Counterexamples:
+CE 1:Query[ε|NEWKEYS SR_CONN NEWKEYS SR_CONN KEXINIT UA_PK_NOK NEWKEYS SR_CONN KEXINIT KEX30 SR_CONN / KEXINIT+UNIMPL UNIMPL UNIMPL UNIMPL NO_CONN NO_CONN NO_CONN NO_CONN NO_CONN NO_CONN NO_CONN]
+CE 2:Query[ε|KEXINIT KEX30 NEWKEYS SR_CONN KEXINIT SR_AUTH KEXINIT KEX30 SR_CONN KEXINIT / KEXINIT KEX31+NEWKEYS+BUFFERED NO_RESP DISCONNECT NO_CONN NO_CONN NO_CONN NO_CONN NO_CONN NO_CONN]
+Number of inputs when hypothesis was generated: [7, 262, 412]
+Number of tests when hypothesis was generated: [7, 79, 107]
+Time (ms) when hypothesis was generated: [15333, 394630, 709440]
+Number of inputs when counterexample was found: [51, 302]
+Number of tests when counterexample was found: [11, 83]
+Time (ms) when counterexample was found: [57851, 439134]
diff --git a/experiments/results/servers/openssh8/alphabet.xml b/experiments/results/servers/openssh8/alphabet.xml
new file mode 100644
index 0000000..92ea4e1
--- /dev/null
+++ b/experiments/results/servers/openssh8/alphabet.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/experiments/results/servers/openssh8/command.args b/experiments/results/servers/openssh8/command.args
new file mode 100644
index 0000000..b176dfe
--- /dev/null
+++ b/experiments/results/servers/openssh8/command.args
@@ -0,0 +1,12 @@
+state-fuzzer-server
+-connect
+openssh-mapper8:8080
+-alphabet
+/app/inputs/alphabets/servers/trans_auth.xml
+-output
+/app/output_folder
+-sshMapperAddress
+openssh-mapper8:8080
+-debug
+-roundLimit
+3
diff --git a/experiments/results/servers/openssh8/error.msg b/experiments/results/servers/openssh8/error.msg
new file mode 100644
index 0000000..042f5fe
--- /dev/null
+++ b/experiments/results/servers/openssh8/error.msg
@@ -0,0 +1,21 @@
+Cannot invoke "String.hashCode()" because the return value of "com.github.protocolfuzzing.protocolstatefuzzer.components.sul.mapper.abstractsymbols.AbstractOutput.getName()" is null
+java.lang.NullPointerException: Cannot invoke "String.hashCode()" because the return value of "com.github.protocolfuzzing.protocolstatefuzzer.components.sul.mapper.abstractsymbols.AbstractOutput.getName()" is null
+ at com.github.protocolfuzzing.protocolstatefuzzer.components.sul.mapper.abstractsymbols.AbstractOutput.hashCode(AbstractOutput.java:218)
+ at net.automatalib.word.Word.hashCode(Word.java:242)
+ at java.base/java.util.HashMap.hash(HashMap.java:338)
+ at java.base/java.util.HashMap.computeIfAbsent(HashMap.java:1191)
+ at de.learnlib.datastructure.discriminationtree.SplitData.getIncoming(SplitData.java:110)
+ at de.learnlib.algorithm.ttt.base.AbstractTTTLearner.prepareSplit(AbstractTTTLearner.java:624)
+ at de.learnlib.algorithm.ttt.base.AbstractTTTLearner.finalizeDiscriminator(AbstractTTTLearner.java:554)
+ at de.learnlib.algorithm.ttt.base.AbstractTTTLearner.finalizeAny(AbstractTTTLearner.java:305)
+ at de.learnlib.algorithm.ttt.base.AbstractTTTLearner.refineHypothesisSingle(AbstractTTTLearner.java:215)
+ at de.learnlib.algorithm.ttt.mealy.TTTLearnerMealy.refineHypothesisSingle(TTTLearnerMealy.java:67)
+ at de.learnlib.algorithm.ttt.base.AbstractTTTLearner.refineHypothesis(AbstractTTTLearner.java:163)
+ at com.github.protocolfuzzing.protocolstatefuzzer.statefuzzer.core.StateFuzzerStandard.inferStateMachine(StateFuzzerStandard.java:164)
+ at com.github.protocolfuzzing.protocolstatefuzzer.statefuzzer.core.StateFuzzerStandard.startFuzzing(StateFuzzerStandard.java:84)
+ at com.github.protocolfuzzing.protocolstatefuzzer.entrypoints.CommandLineParser.executeCommand(CommandLineParser.java:355)
+ at com.github.protocolfuzzing.protocolstatefuzzer.entrypoints.CommandLineParser.parseAndExecuteCommand(CommandLineParser.java:226)
+ at com.github.protocolfuzzing.protocolstatefuzzer.entrypoints.CommandLineParser.parse(CommandLineParser.java:168)
+ at com.github.protocolfuzzing.protocolstatefuzzer.entrypoints.CommandLineParser.parse(CommandLineParser.java:212)
+ at learner.Main.runMealyLearner(Main.java:32)
+ at learner.Main.main(Main.java:21)
diff --git a/experiments/results/servers/openssh8/hyp1.dot b/experiments/results/servers/openssh8/hyp1.dot
new file mode 100644
index 0000000..99bfa7f
--- /dev/null
+++ b/experiments/results/servers/openssh8/hyp1.dot
@@ -0,0 +1,15 @@
+digraph g {
+
+ s0 [shape="circle" label="s0"];
+ s0 -> s0 [label="KEXINIT / KEXINIT"];
+ s0 -> s0 [label="KEX30 / KEXINIT+UNIMPL"];
+ s0 -> s0 [label="NEWKEYS / KEXINIT+UNIMPL"];
+ s0 -> s0 [label="SR_AUTH / KEXINIT+UNIMPL"];
+ s0 -> s0 [label="SR_CONN / KEXINIT+UNIMPL"];
+ s0 -> s0 [label="UA_PK_OK / KEXINIT"];
+ s0 -> s0 [label="UA_PK_NOK / KEXINIT"];
+
+__start0 [label="" shape="none" width="0" height="0"];
+__start0 -> s0;
+
+}
diff --git a/experiments/results/servers/openssh8/hyp2.dot b/experiments/results/servers/openssh8/hyp2.dot
new file mode 100644
index 0000000..4e505b7
--- /dev/null
+++ b/experiments/results/servers/openssh8/hyp2.dot
@@ -0,0 +1,31 @@
+digraph g {
+
+ s0 [shape="circle" label="s0"];
+ s1 [shape="circle" label="s1"];
+ s2 [shape="circle" label="s2"];
+ s0 -> s1 [label="KEXINIT / KEXINIT"];
+ s0 -> s1 [label="KEX30 / KEXINIT+UNIMPL"];
+ s0 -> s1 [label="NEWKEYS / KEXINIT+UNIMPL"];
+ s0 -> s1 [label="SR_AUTH / KEXINIT+UNIMPL"];
+ s0 -> s1 [label="SR_CONN / KEXINIT+UNIMPL"];
+ s0 -> s2 [label="UA_PK_OK / KEXINIT"];
+ s0 -> s2 [label="UA_PK_NOK / KEXINIT"];
+ s1 -> s1 [label="KEXINIT / UNIMPL"];
+ s1 -> s1 [label="KEX30 / KEX31+NEWKEYS+BUFFERED"];
+ s1 -> s1 [label="NEWKEYS / UNIMPL"];
+ s1 -> s1 [label="SR_AUTH / UNIMPL"];
+ s1 -> s1 [label="SR_CONN / UNIMPL"];
+ s1 -> s2 [label="UA_PK_OK / NO_CONN"];
+ s1 -> s2 [label="UA_PK_NOK / NO_CONN"];
+ s2 -> s2 [label="KEXINIT / NO_CONN"];
+ s2 -> s2 [label="KEX30 / NO_CONN"];
+ s2 -> s2 [label="NEWKEYS / NO_CONN"];
+ s2 -> s2 [label="SR_AUTH / NO_CONN"];
+ s2 -> s2 [label="SR_CONN / NO_CONN"];
+ s2 -> s2 [label="UA_PK_OK / NO_CONN"];
+ s2 -> s2 [label="UA_PK_NOK / NO_CONN"];
+
+__start0 [label="" shape="none" width="0" height="0"];
+__start0 -> s0;
+
+}
diff --git a/experiments/results/servers/openssh8/hyp3.dot b/experiments/results/servers/openssh8/hyp3.dot
new file mode 100644
index 0000000..732715e
--- /dev/null
+++ b/experiments/results/servers/openssh8/hyp3.dot
@@ -0,0 +1,79 @@
+digraph g {
+
+ s0 [shape="circle" label="s0"];
+ s1 [shape="circle" label="s1"];
+ s2 [shape="circle" label="s2"];
+ s3 [shape="circle" label="s3"];
+ s4 [shape="circle" label="s4"];
+ s5 [shape="circle" label="s5"];
+ s6 [shape="circle" label="s6"];
+ s7 [shape="circle" label="s7"];
+ s8 [shape="circle" label="s8"];
+ s0 -> s1 [label="KEXINIT / KEXINIT"];
+ s0 -> s1 [label="KEX30 / KEXINIT+UNIMPL"];
+ s0 -> s1 [label="NEWKEYS / KEXINIT+UNIMPL"];
+ s0 -> s1 [label="SR_AUTH / KEXINIT+UNIMPL"];
+ s0 -> s1 [label="SR_CONN / KEXINIT+UNIMPL"];
+ s0 -> s2 [label="UA_PK_OK / KEXINIT"];
+ s0 -> s2 [label="UA_PK_NOK / KEXINIT"];
+ s1 -> s4 [label="KEXINIT / UNIMPL"];
+ s1 -> s3 [label="KEX30 / KEX31+NEWKEYS+BUFFERED"];
+ s1 -> s1 [label="NEWKEYS / UNIMPL"];
+ s1 -> s1 [label="SR_AUTH / UNIMPL"];
+ s1 -> s1 [label="SR_CONN / UNIMPL"];
+ s1 -> s2 [label="UA_PK_OK / NO_CONN"];
+ s1 -> s2 [label="UA_PK_NOK / NO_CONN"];
+ s2 -> s2 [label="KEXINIT / NO_CONN"];
+ s2 -> s2 [label="KEX30 / NO_CONN"];
+ s2 -> s2 [label="NEWKEYS / NO_CONN"];
+ s2 -> s2 [label="SR_AUTH / NO_CONN"];
+ s2 -> s2 [label="SR_CONN / NO_CONN"];
+ s2 -> s2 [label="UA_PK_OK / NO_CONN"];
+ s2 -> s2 [label="UA_PK_NOK / NO_CONN"];
+ s3 -> s3 [label="KEXINIT / UNIMPL"];
+ s3 -> s3 [label="KEX30 / UNIMPL"];
+ s3 -> s6 [label="NEWKEYS / NO_RESP"];
+ s3 -> s3 [label="SR_AUTH / UNIMPL"];
+ s3 -> s3 [label="SR_CONN / UNIMPL"];
+ s3 -> s2 [label="UA_PK_OK / NO_CONN"];
+ s3 -> s2 [label="UA_PK_NOK / NO_CONN"];
+ s4 -> s4 [label="KEXINIT / UNIMPL"];
+ s4 -> s5 [label="KEX30 / KEX31+NEWKEYS"];
+ s4 -> s4 [label="NEWKEYS / UNIMPL"];
+ s4 -> s4 [label="SR_AUTH / UNIMPL"];
+ s4 -> s4 [label="SR_CONN / UNIMPL"];
+ s4 -> s2 [label="UA_PK_OK / NO_CONN"];
+ s4 -> s2 [label="UA_PK_NOK / NO_CONN"];
+ s5 -> s5 [label="KEXINIT / NO_RESP"];
+ s5 -> s5 [label="KEX30 / NO_RESP"];
+ s5 -> s2 [label="NEWKEYS / NO_RESP"];
+ s5 -> s5 [label="SR_AUTH / NO_RESP"];
+ s5 -> s5 [label="SR_CONN / NO_RESP"];
+ s5 -> s2 [label="UA_PK_OK / NO_CONN"];
+ s5 -> s2 [label="UA_PK_NOK / NO_CONN"];
+ s6 -> s6 [label="KEXINIT / UNIMPL"];
+ s6 -> s6 [label="KEX30 / UNIMPL"];
+ s6 -> s2 [label="NEWKEYS / UNIMPL"];
+ s6 -> s7 [label="SR_AUTH / SR_ACCEPT"];
+ s6 -> s2 [label="SR_CONN / DISCONNECT"];
+ s6 -> s6 [label="UA_PK_OK / UNIMPL"];
+ s6 -> s6 [label="UA_PK_NOK / UNIMPL"];
+ s7 -> s7 [label="KEXINIT / UNIMPL"];
+ s7 -> s7 [label="KEX30 / UNIMPL"];
+ s7 -> s2 [label="NEWKEYS / UNIMPL"];
+ s7 -> s7 [label="SR_AUTH / SR_ACCEPT"];
+ s7 -> s2 [label="SR_CONN / DISCONNECT"];
+ s7 -> s7 [label="UA_PK_OK / UA_FAILURE"];
+ s7 -> s8 [label="UA_PK_NOK / UA_FAILURE"];
+ s8 -> s8 [label="KEXINIT / UNIMPL"];
+ s8 -> s8 [label="KEX30 / UNIMPL"];
+ s8 -> s2 [label="NEWKEYS / UNIMPL"];
+ s8 -> s8 [label="SR_AUTH / SR_ACCEPT"];
+ s8 -> s2 [label="SR_CONN / DISCONNECT"];
+ s8 -> s2 [label="UA_PK_OK / DISCONNECT"];
+ s8 -> s8 [label="UA_PK_NOK / UA_FAILURE"];
+
+__start0 [label="" shape="none" width="0" height="0"];
+__start0 -> s0;
+
+}
diff --git a/experiments/results/servers/openssh8/learnedModel.dot b/experiments/results/servers/openssh8/learnedModel.dot
new file mode 100644
index 0000000..8c77c41
--- /dev/null
+++ b/experiments/results/servers/openssh8/learnedModel.dot
@@ -0,0 +1,31 @@
+digraph g {
+
+ s0 [shape="circle" label="0"];
+ s1 [shape="circle" label="1"];
+ s2 [shape="circle" label="2"];
+ s0 -> s1 [label="KEXINIT / KEXINIT"];
+ s0 -> s1 [label="KEX30 / KEXINIT+UNIMPL"];
+ s0 -> s1 [label="NEWKEYS / KEXINIT+UNIMPL"];
+ s0 -> s1 [label="SR_AUTH / KEXINIT+UNIMPL"];
+ s0 -> s1 [label="SR_CONN / KEXINIT+UNIMPL"];
+ s0 -> s2 [label="UA_PK_OK / KEXINIT"];
+ s0 -> s2 [label="UA_PK_NOK / KEXINIT"];
+ s1 -> s1 [label="KEXINIT / UNIMPL"];
+ s1 -> s1 [label="KEX30 / KEX31+NEWKEYS+BUFFERED"];
+ s1 -> s1 [label="NEWKEYS / UNIMPL"];
+ s1 -> s1 [label="SR_AUTH / UNIMPL"];
+ s1 -> s1 [label="SR_CONN / UNIMPL"];
+ s1 -> s2 [label="UA_PK_OK / NO_CONN"];
+ s1 -> s2 [label="UA_PK_NOK / NO_CONN"];
+ s2 -> s2 [label="KEXINIT / NO_CONN"];
+ s2 -> s2 [label="KEX30 / NO_CONN"];
+ s2 -> s2 [label="NEWKEYS / NO_CONN"];
+ s2 -> s2 [label="SR_AUTH / NO_CONN"];
+ s2 -> s2 [label="SR_CONN / NO_CONN"];
+ s2 -> s2 [label="UA_PK_OK / NO_CONN"];
+ s2 -> s2 [label="UA_PK_NOK / NO_CONN"];
+
+__start0 [label="" shape="none" width="0" height="0"];
+__start0 -> s0;
+
+}
diff --git a/experiments/results/servers/openssh8/statistics.txt b/experiments/results/servers/openssh8/statistics.txt
new file mode 100644
index 0000000..7f32be3
--- /dev/null
+++ b/experiments/results/servers/openssh8/statistics.txt
@@ -0,0 +1,89 @@
+=== RUN DESCRIPTION ===
+Alphabet: [KEXINIT, KEX30, NEWKEYS, SR_AUTH, SR_CONN, UA_PK_OK, UA_PK_NOK]
+
+StateFuzzerConfig Parameters
+Help: false
+Debug: true
+Quiet: false
+Output Directory: /app/output_folder
+Fuzzing Client: false
+
+LearnerConfig Parameters
+Alphabet: /app/inputs/alphabets/servers/trans_auth.xml
+Learning Algorithm: TTT
+Equivalence Algorithms: [RANDOM_WP_METHOD]
+Max Depth: 1
+Min Length: 5
+Max Length: 15
+Max Equivalence Queries: 1000
+Runs Per Membership Query: 1
+Random Length: 5
+Membership Query Retries: 3
+Log Queries: false
+Prob Reset: 0.0
+Test File: null
+Seed: 0
+Cache Tests: false
+Ce Sanitization: true
+Skip Non Det Tests: false
+Ce Reruns: 3
+Probabilistic Sanitization: true
+Time Limit: null
+Test Limit: null
+Round Limit: 3
+IOMode: true
+Probability of Choosing a New DataValue: 0.1
+Max Runs: 1
+Max Depth for Register Automata: 1
+Reset Runs: true
+Seed transitions: true
+Draw symbols uniformly: true
+
+SulConfig Parameters
+Fuzzing Role: client
+Fuzzing Client: true
+Response Wait: 100
+Input Response Timeout: null
+Command: null
+Terminate Command: null
+Process Dir: null
+Redirect Output Streams: false
+Process Trigger: NEW_TEST
+Start Wait: 0
+
+SulServerConfigStandard Parameters
+Connect to: openssh-mapper8:8080
+
+MapperConfig Parameters
+Mapper Connection Config: null
+Repeating Outputs: null
+Socket Closed as Timeout: false
+Disabled as Timeout: false
+Merge Repeating: true
+
+SulAdapterConfig Parameters
+Adapter Port: null
+Adapter Address: localhost
+
+=== STATISTICS ===
+Learning finished: false
+Reason: Cannot invoke "String.hashCode()" because the return value of "com.github.protocolfuzzing.protocolstatefuzzer.components.sul.mapper.abstractsymbols.AbstractOutput.getName()" is null
+Size of the input alphabet: 7
+Number of states: 3
+Number of hypotheses: 2
+Number of inputs: 300
+Number of tests: 78
+Number of learning inputs: 87
+Number of learning tests: 40
+Number of inputs up to last hypothesis: 131
+Number of tests up to last hypothesis: 44
+Time (ms) to learn model: 528199
+Counterexamples:
+CE 1:Query[ε|NEWKEYS SR_CONN NEWKEYS SR_CONN KEXINIT UA_PK_NOK NEWKEYS SR_CONN KEXINIT KEX30 SR_CONN / KEXINIT+UNIMPL UNIMPL UNIMPL UNIMPL UNIMPL NO_CONN NO_CONN NO_CONN NO_CONN NO_CONN NO_CONN]
+CE 2:Query[ε|KEX30 NEWKEYS KEX30 NEWKEYS UA_PK_NOK SR_AUTH UA_PK_NOK SR_AUTH UA_PK_NOK NEWKEYS UA_PK_NOK KEXINIT / KEXINIT+UNIMPL UNIMPL KEX31+NEWKEYS+BUFFERED NO_RESP UNIMPL SR_ACCEPT UA_FAILURE SR_ACCEPT UA_FAILURE UNIMPL NO_CONN NO_CONN]
+Number of inputs when hypothesis was generated: [7, 131]
+Number of tests when hypothesis was generated: [7, 44]
+Time (ms) when hypothesis was generated: [15171, 184755]
+Number of inputs when counterexample was found: [51, 213]
+Number of tests when counterexample was found: [11, 52]
+Time (ms) when counterexample was found: [64539, 307408]
diff --git a/experiments/scripts/diff_hyps.sh b/experiments/scripts/diff_hyps.sh
new file mode 100755
index 0000000..54ac29a
--- /dev/null
+++ b/experiments/scripts/diff_hyps.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+
+dir1=$1
+dir2=$2
+num_rounds=$3
+
+if [ $# != 3 ]; then
+ echo "Usage: ${0##*/} dir1 dir2 num_rounds"
+ echo "Diffs the first num_rounds hypotheses stored in two dirs"
+ exit 1
+fi
+
+if [[ ! -d $dir1 ]]; then
+ echo "$dir1"": No such directory"
+ exit 1
+fi
+if [[ ! -d $dir2 ]]; then
+ echo "$dir2"": No such directory"
+ exit 1
+fi
+
+for ((i = 1 ; i <= num_rounds ; i++)); do
+ if ! diff --unified=0 "$dir1"/hyp$i.dot "$dir2"/hyp$i.dot; then
+ exit 1
+ fi
+done
\ No newline at end of file
diff --git a/experiments/scripts/start_learning.sh b/experiments/scripts/start_learning.sh
new file mode 100755
index 0000000..b803aa6
--- /dev/null
+++ b/experiments/scripts/start_learning.sh
@@ -0,0 +1,105 @@
+#!/bin/bash
+
+# Define paths
+SSH_KEY_DIR="../orchestration/ssh-keys"
+SSH_KEY_NAME="learner-ssh"
+DOCKER_COMPOSE_DIR="../orchestration"
+
+# Ensure the ssh-keys directory exists
+mkdir -p "${SSH_KEY_DIR}"
+
+declare -a KNOWN_RA_ALGOS=("RALAMBDA" "RASTAR")
+
+is_known_ra_algo() {
+ local arg="$1"
+ for algo in "${KNOWN_RA_ALGOS[@]}"; do
+ if [[ "${arg}" == "${algo}" ]]; then
+ return 0 # True, it's a known RA algo
+ fi
+ done
+ return 1 # False
+}
+
+print_usage() {
+ echo "Usage:"
+ echo " ./start_learning.sh "
+ echo " ./start_learning.sh "
+ echo
+ echo " : Required. The SSH server to experiment with."
+ echo " Must be one of: 'openssh7', 'openssh8', 'dropbear'."
+ echo " : Optional. Specifies the Register Automata (RA) learning algorithm."
+ echo " If provided, RA learning mode is activated."
+ echo " Known algorithms: ${KNOWN_RA_ALGOS[*]}. Ignored if not applicable."
+ echo
+ echo "Examples:"
+ echo "Mealy Learning:"
+ echo "./start_learning.sh openssh8"
+ echo "./start_learning.sh dropbear"
+ echo
+ echo "RA Learning:"
+ echo "./start_learning.sh dropbear RALAMBDA"
+ echo "./start_learning.sh openssh8 RASTAR"
+ exit 1
+}
+
+SUT=""
+MODE="mealy" # Default mode
+ALGO=""
+
+case $# in
+ 1)
+ SUT="$1"
+ # MODE remains "mealy", ALGO remains ""
+ ;;
+ 2)
+ SUT="$1"
+ if is_known_ra_algo "$2"; then
+ MODE="ra"
+ ALGO="$2"
+ else
+ echo "Error: Invalid second argument '$2'." >&2
+ echo "For Mealy learning, only one argument (SUT name) is expected." >&2
+ echo "For RA learning, the second argument must be a known learning algorithm (${KNOWN_RA_ALGOS[*]})." >&2
+ print_usage
+ fi
+ ;;
+ *)
+ echo "Error: Incorrect number of arguments." >&2
+ print_usage
+ ;;
+esac
+
+
+# Validate input and start corresponding docker-compose
+if [[ ! "${SUT}" =~ ^(openssh7|openssh8|dropbear)$ ]]; then
+ echo "Error: Invalid SUT name '${SUT}'."
+ print_usage
+fi
+
+# Determine which compose file to use
+if [[ "${MODE}" == "ra" ]]; then
+ if [[ -z "${ALGO}" ]]; then
+ echo "Error: RA mode requires a learning algorithm name."
+ print_usage
+ fi
+ COMPOSE_FILE="docker-compose-${SUT}-ra.yaml"
+else
+ COMPOSE_FILE="docker-compose-${SUT}.yaml"
+fi
+
+# Run the appropriate docker compose setup
+if [[ -f "${DOCKER_COMPOSE_DIR}/${COMPOSE_FILE}" ]]; then
+ pushd "${DOCKER_COMPOSE_DIR}" > /dev/null
+ echo "Starting ${MODE:-mealy} learning experiment for ${SUT}..."
+
+ if [[ "${MODE}" == "ra" ]]; then
+ LEARNING_ALGORITHM=${ALGO} docker compose -f "${COMPOSE_FILE}" up --build -d
+ else
+ docker compose -f "${COMPOSE_FILE}" up --build -d
+ fi
+
+ popd > /dev/null
+else
+ echo "Error: ${COMPOSE_FILE} not found in ${DOCKER_COMPOSE_DIR}"
+ exit 1
+fi
diff --git a/ssh-learner/.gitignore b/ssh-learner/.gitignore
new file mode 100644
index 0000000..b83d222
--- /dev/null
+++ b/ssh-learner/.gitignore
@@ -0,0 +1 @@
+/target/
diff --git a/ssh-learner/Dockerfile b/ssh-learner/Dockerfile
index 1609480..bee910f 100644
--- a/ssh-learner/Dockerfile
+++ b/ssh-learner/Dockerfile
@@ -2,10 +2,10 @@ FROM maven:3.9-eclipse-temurin-17 AS build
WORKDIR /app
RUN git clone https://github.com/protocol-fuzzing/protocol-state-fuzzer.git psf \
&& \
- cd psf && bash install.sh && cd ../..
+ cd psf && bash install.sh && cd /app
COPY pom.xml .
COPY src ./src
-RUN mvn clean package
+RUN mvn -X clean package -DskipTests
@@ -14,7 +14,6 @@ FROM bitnami/java:17
WORKDIR /app
COPY --from=build /app/target/ssh-learner.jar ./ssh-learner.jar
COPY --from=build /app/target/lib/ ./lib/
-COPY resources/ resources/
-COPY inputs/alphabets/ inputs/alphabets/
-ENTRYPOINT ["java", "-cp", "./ssh-learner.jar:./lib/*", "learner.Main"]
+
+CMD ["java", "-cp", "./ssh-learner.jar:./lib/*", "learner.Main"]
diff --git a/ssh-learner/inputs/alphabets/servers/openssh.xml b/ssh-learner/inputs/alphabets/servers/openssh.xml
new file mode 100644
index 0000000..a06a5a2
--- /dev/null
+++ b/ssh-learner/inputs/alphabets/servers/openssh.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ssh-learner/inputs/alphabets/servers/ra_input.xml b/ssh-learner/inputs/alphabets/servers/ra_input.xml
new file mode 100644
index 0000000..e7cd23a
--- /dev/null
+++ b/ssh-learner/inputs/alphabets/servers/ra_input.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ssh-learner/inputs/args/dropbear_ra.args b/ssh-learner/inputs/args/dropbear_ra.args
new file mode 100644
index 0000000..be16d43
--- /dev/null
+++ b/ssh-learner/inputs/args/dropbear_ra.args
@@ -0,0 +1,14 @@
+state-fuzzer-server
+-connect
+127.0.0.1:8080
+-alphabet
+inputs/alphabets/servers/ra_input.xml
+-output
+output_folder_ssh_1
+-sshMapperAddress
+127.0.0.1:8080
+-debug
+-learningAlgorithm
+RASTAR
+-equivalenceAlgorithms
+IO_RANDOM_WALK
\ No newline at end of file
diff --git a/ssh-learner/pom.xml b/ssh-learner/pom.xml
index daa42fc..2cfc203 100644
--- a/ssh-learner/pom.xml
+++ b/ssh-learner/pom.xml
@@ -18,6 +18,11 @@
protocolstatefuzzer
1.0.0
+
+ com.google.code.gson
+ gson
+ 2.10.1
+
diff --git a/ssh-learner/src/learner/Main.java b/ssh-learner/src/learner/Main.java
index 7d071b3..bf3a13f 100644
--- a/ssh-learner/src/learner/Main.java
+++ b/ssh-learner/src/learner/Main.java
@@ -1,34 +1,22 @@
package learner;
-import com.beust.jcommander.Parameter;
import com.github.protocolfuzzing.protocolstatefuzzer.components.learner.LearnerResult;
import com.github.protocolfuzzing.protocolstatefuzzer.components.learner.statistics.MealyMachineWrapper;
-import com.github.protocolfuzzing.protocolstatefuzzer.components.learner.statistics.RegisterAutomatonWrapper;
import com.github.protocolfuzzing.protocolstatefuzzer.entrypoints.CommandLineParser;
+
import java.io.IOException;
import java.util.List;
public class Main {
- @Parameter(names = "-ra", description = "whether to run Mealy Machine learner or the RA learner?")
- static boolean isRaLearner = false;
-
public static void main(String[] args) throws IOException {
- if (isRaLearner) {
- System.exit(0);
- // logic for RA learner
- } else {
- runMealyLearner(args);
- }
-
- }
-
- static void runMealyLearner(String[] args) {
// multibuilder implements all necessary builders
MultiBuilder mb = new MultiBuilder();
CommandLineParser> commandLineParser = new CommandLineParser<>(mb, mb,
mb, mb);
List>> results = commandLineParser.parse(args);
+ System.out.println("Done with Mealy Machine learning");
+
}
}
diff --git a/ssh-learner/src/learner/RALearnerMain.java b/ssh-learner/src/learner/RALearnerMain.java
new file mode 100644
index 0000000..37c6edc
--- /dev/null
+++ b/ssh-learner/src/learner/RALearnerMain.java
@@ -0,0 +1,25 @@
+package learner;
+
+import java.io.IOException;
+import java.util.List;
+
+import com.github.protocolfuzzing.protocolstatefuzzer.components.learner.LearnerResult;
+import com.github.protocolfuzzing.protocolstatefuzzer.components.learner.statistics.RegisterAutomatonWrapper;
+import com.github.protocolfuzzing.protocolstatefuzzer.entrypoints.CommandLineParser;
+
+import de.learnlib.ralib.words.PSymbolInstance;
+import de.learnlib.ralib.words.ParameterizedSymbol;
+
+public class RALearnerMain {
+ public static void main(String[] args) throws IOException {
+ RAMultiBuilder mb = new RAMultiBuilder();
+
+ CommandLineParser> commandLineParser = new CommandLineParser<>(
+ mb, mb,
+ mb, mb);
+ List>> results = commandLineParser
+ .parse(args);
+
+ System.out.println("✅ Done with RA learning");
+ }
+}
diff --git a/ssh-learner/src/learner/RAMultiBuilder.java b/ssh-learner/src/learner/RAMultiBuilder.java
new file mode 100644
index 0000000..3e53523
--- /dev/null
+++ b/ssh-learner/src/learner/RAMultiBuilder.java
@@ -0,0 +1,87 @@
+package learner;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import com.github.protocolfuzzing.protocolstatefuzzer.components.learner.alphabet.AlphabetBuilder;
+import com.github.protocolfuzzing.protocolstatefuzzer.components.learner.alphabet.AlphabetBuilderStandard;
+import com.github.protocolfuzzing.protocolstatefuzzer.components.learner.config.LearnerConfigRA;
+import com.github.protocolfuzzing.protocolstatefuzzer.components.learner.statistics.RegisterAutomatonWrapper;
+import com.github.protocolfuzzing.protocolstatefuzzer.components.sul.core.SulBuilder;
+import com.github.protocolfuzzing.protocolstatefuzzer.components.sul.core.SulWrapper;
+import com.github.protocolfuzzing.protocolstatefuzzer.components.sul.core.SulWrapperStandard;
+import com.github.protocolfuzzing.protocolstatefuzzer.statefuzzer.core.StateFuzzer;
+import com.github.protocolfuzzing.protocolstatefuzzer.statefuzzer.core.StateFuzzerBuilder;
+import com.github.protocolfuzzing.protocolstatefuzzer.statefuzzer.core.StateFuzzerComposerRA;
+import com.github.protocolfuzzing.protocolstatefuzzer.statefuzzer.core.StateFuzzerRA;
+import com.github.protocolfuzzing.protocolstatefuzzer.statefuzzer.core.config.StateFuzzerClientConfig;
+import com.github.protocolfuzzing.protocolstatefuzzer.statefuzzer.core.config.StateFuzzerConfigBuilder;
+import com.github.protocolfuzzing.protocolstatefuzzer.statefuzzer.core.config.StateFuzzerEnabler;
+import com.github.protocolfuzzing.protocolstatefuzzer.statefuzzer.core.config.StateFuzzerServerConfig;
+import com.github.protocolfuzzing.protocolstatefuzzer.statefuzzer.core.config.StateFuzzerServerConfigStandard;
+import com.github.protocolfuzzing.protocolstatefuzzer.statefuzzer.testrunner.core.TestRunner;
+import com.github.protocolfuzzing.protocolstatefuzzer.statefuzzer.testrunner.core.TestRunnerBuilder;
+import com.github.protocolfuzzing.protocolstatefuzzer.statefuzzer.testrunner.core.config.TestRunnerConfigStandard;
+import com.github.protocolfuzzing.protocolstatefuzzer.statefuzzer.testrunner.core.config.TestRunnerEnabler;
+import com.github.protocolfuzzing.protocolstatefuzzer.statefuzzer.testrunner.timingprobe.TimingProbe;
+import com.github.protocolfuzzing.protocolstatefuzzer.statefuzzer.testrunner.timingprobe.TimingProbeBuilder;
+import com.github.protocolfuzzing.protocolstatefuzzer.statefuzzer.testrunner.timingprobe.config.TimingProbeConfigStandard;
+import com.github.protocolfuzzing.protocolstatefuzzer.statefuzzer.testrunner.timingprobe.config.TimingProbeEnabler;
+import de.learnlib.ralib.data.DataType;
+import de.learnlib.ralib.theory.Theory;
+import de.learnlib.ralib.words.PSymbolInstance;
+import de.learnlib.ralib.words.ParameterizedSymbol;
+
+public class RAMultiBuilder
+ implements StateFuzzerConfigBuilder,
+ StateFuzzerBuilder>, TestRunnerBuilder,
+ TimingProbeBuilder {
+
+ protected AlphabetBuilder alphabetBuilder = new AlphabetBuilderStandard(
+ new RASshSerializer(RASshInput.class,
+ RASshAlphabetPojoXml.class));
+
+ protected SulBuilder sulBuilder;
+
+ SulWrapper sulWrapper = new SulWrapperStandard();
+
+ @Override
+ public StateFuzzerClientConfig buildClientConfig() {
+ return new SshStateFuzzerClientConfig(new SshSulClientConfig());
+ }
+
+ @Override
+ public StateFuzzerServerConfig buildServerConfig() {
+ return new StateFuzzerServerConfigStandard(
+ new LearnerConfigRA(),
+ new RASulServerConfig(),
+ new TestRunnerConfigStandard(),
+ new TimingProbeConfigStandard());
+ }
+
+ @Override
+ public StateFuzzer> build(
+ StateFuzzerEnabler stateFuzzerEnabler) {
+
+ @SuppressWarnings("rawtypes")
+ final Map teachers = new LinkedHashMap<>();
+
+ sulBuilder = new RASulBuilder();
+ StateFuzzerComposerRA stateFuzzerComposer = new StateFuzzerComposerRA(
+ stateFuzzerEnabler, alphabetBuilder,
+ sulBuilder, sulWrapper, teachers);
+
+ return new StateFuzzerRA(stateFuzzerComposer.initialize());
+ }
+
+ @Override
+ public TimingProbe build(TimingProbeEnabler timingProbeEnabler) {
+ // TODO Auto-generated method stub
+ throw new UnsupportedOperationException("Unimplemented method 'build'");
+ }
+
+ @Override
+ public TestRunner build(TestRunnerEnabler testRunnerEnabler) {
+ // TODO Auto-generated method stub
+ throw new UnsupportedOperationException("Unimplemented method 'build'");
+ }
+}
diff --git a/ssh-learner/src/learner/RASocketMapperSul.java b/ssh-learner/src/learner/RASocketMapperSul.java
new file mode 100644
index 0000000..5fe39cb
--- /dev/null
+++ b/ssh-learner/src/learner/RASocketMapperSul.java
@@ -0,0 +1,61 @@
+package learner;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.net.Socket;
+
+import de.learnlib.ralib.words.PSymbolInstance;
+
+/**
+ * Socket interface with the external SSH mapper (test harness).
+ */
+
+public class RASocketMapperSul {
+
+ private PrintWriter sockout;
+ private BufferedReader sockin;
+
+ public RASocketMapperSul(Socket sock) {
+ try {
+ // Create socket out (no buffering) and in
+ sockout = new PrintWriter(sock.getOutputStream(), true);
+ sockin = new BufferedReader(new InputStreamReader(sock.getInputStream()));
+ } catch (IOException e) {
+ throw new MapperException("Failed to create communication streams with mapper", e);
+ }
+ }
+
+ public PSymbolInstance sendInput(PSymbolInstance input) {
+ try {
+ // Create JSON string from input
+ RASshSocketData socketData = new RASshSocketData(input);
+ // Send input to SUL
+ sockout.println(socketData.getMsg());
+ return socketData.getSocketOutput(sockin.readLine());
+ } catch (IOException e) {
+ throw new MapperException("Input could not be sent", e);
+ }
+ }
+
+ public void reset() {
+ // Perform a reset on the SUL: empty input list on wrapper and send reset signal
+ sockout.println("reset");
+
+ // Check if reset succeeded. Note: this is also needed because not receiving
+ // after reset will immediately continue
+ // to sending Input, allowing the possibility for the client to receive "reset
+ // INPUT" in one string. Reading in between
+ // will force a break since reading is blocking.
+ try {
+ String line = sockin.readLine();
+ if (!line.toString().equals("resetok")) {
+ throw new MapperException(String
+ .format("Reset did not succeed. On sending reset expected %s but got %s", "resetok", line));
+ }
+ } catch (IOException e) {
+ throw new MapperException("Reset could not be sent", e);
+ }
+ }
+}
diff --git a/ssh-learner/src/learner/RASshAlphabet.java b/ssh-learner/src/learner/RASshAlphabet.java
new file mode 100644
index 0000000..26c0fe6
--- /dev/null
+++ b/ssh-learner/src/learner/RASshAlphabet.java
@@ -0,0 +1,74 @@
+package learner;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import de.learnlib.ralib.words.InputSymbol;
+import de.learnlib.ralib.words.OutputSymbol;
+import de.learnlib.ralib.words.ParameterizedSymbol;
+import net.automatalib.alphabet.impl.ListAlphabet;
+
+public class RASshAlphabet extends ListAlphabet {
+
+ private final Map symbolMap;
+
+ private RASshAlphabet(Map symbols) {
+ super(new ArrayList(symbols.values()));
+ System.out.println("Alphabet symbols: "+ symbols.toString());
+ this.symbolMap = symbols;
+ }
+
+ public ParameterizedSymbol getPSymbol(String enum_member) {
+
+ ParameterizedSymbol symbol = symbolMap.get(enum_member);
+ if (symbol == null) {
+ throw new RuntimeException("The symbol " + enum_member
+ + " is not present in the alphabet map, the map may have not been initialised properly.");
+ }
+ return symbol;
+ }
+
+ public static class Builder {
+
+ /** Stores the symbols as they are being built, allows overwriting */
+ private HashMap symbolMap = new HashMap<>();
+
+ public Builder() {
+ }
+
+ public Builder withInput(ParameterizedSymbol input) {
+ InputSymbol inputS = new InputSymbol(input.getName(), input.getPtypes());
+ symbolMap.put(input.getName(), inputS);
+ return this;
+ }
+
+ public Builder withOutput(ParameterizedSymbol input) {
+ String name = "O_" + input.getName();
+ OutputSymbol output = new OutputSymbol(name, input.getPtypes());
+ symbolMap.put(name, output);
+ return this;
+ }
+
+ public Builder withInputs(ParameterizedSymbol[] inputs) {
+ for (ParameterizedSymbol i : inputs) {
+ InputSymbol inputS = new InputSymbol(i.getName(), i.getPtypes());
+ symbolMap.put(i.getName(), inputS);
+ }
+ return this;
+ }
+
+ public Builder withOutputs(ParameterizedSymbol[] inputs) {
+ for (ParameterizedSymbol i : inputs) {
+ OutputSymbol outputS = new OutputSymbol(i.getName(), i.getPtypes());
+ symbolMap.put(i.getName(), outputS);
+ }
+ return this;
+ }
+
+ public RASshAlphabet build() {
+ return new RASshAlphabet(symbolMap);
+ }
+
+ }
+}
diff --git a/ssh-learner/src/learner/RASshAlphabetPojoXml.java b/ssh-learner/src/learner/RASshAlphabetPojoXml.java
new file mode 100644
index 0000000..b1a9016
--- /dev/null
+++ b/ssh-learner/src/learner/RASshAlphabetPojoXml.java
@@ -0,0 +1,112 @@
+package learner;
+
+import com.github.protocolfuzzing.protocolstatefuzzer.components.learner.alphabet.xml.AlphabetPojoXml;
+import jakarta.xml.bind.annotation.XmlAccessType;
+import jakarta.xml.bind.annotation.XmlAccessorType;
+import jakarta.xml.bind.annotation.XmlAttribute;
+import jakarta.xml.bind.annotation.XmlElement;
+import jakarta.xml.bind.annotation.XmlElements;
+import jakarta.xml.bind.annotation.XmlRootElement;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@XmlRootElement(name = "alphabet")
+@XmlAccessorType(XmlAccessType.FIELD)
+public class RASshAlphabetPojoXml extends AlphabetPojoXml {
+
+ @XmlElements(value = {
+ @XmlElement(type = RASshInputPojoXml.class, name = "RASshInput")
+ })
+ private List inputs = new ArrayList<>();
+
+ public List getInputs() {
+ System.out.println("getInputs: " + inputs);
+ List allInputs = inputs.stream().map(xmlInput -> {
+ List params = xmlInput.getParams().stream()
+ .map(param -> new RASshParams(param.getName(), param.getType(), param.getValue(),
+ param.getBaseClass()))
+ .collect(Collectors.toList());
+ return new RASshInput(xmlInput.getName(), params);
+ }).collect(Collectors.toList());
+ return allInputs;
+ }
+
+ // maybe not neeeded
+ // public void setInputs(List inputs) {
+ // this.inputs = inputs;
+ // }
+ @XmlAccessorType(XmlAccessType.FIELD)
+ public static class RASshInputPojoXml {
+
+ @XmlAttribute(name = "name", required = true)
+ private String name;
+
+ @XmlElement(name = "param")
+ private List params = new ArrayList<>();
+
+ public String getName() {
+ return name;
+ }
+
+ public List getParams() {
+ return params;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public void setParams(List params) {
+ this.params = params;
+ }
+ }
+
+ @XmlAccessorType(XmlAccessType.FIELD)
+ public static class ParamPojoXml {
+
+ @XmlAttribute(name = "pName", required = true)
+ private String name;
+
+ @XmlAttribute(name = "type", required = true)
+ private String type;
+
+ @XmlAttribute(name = "baseClass", required = true)
+ private String baseClass;
+
+ @XmlAttribute(name = "value", required = true)
+ private String value;
+
+ public String getName() {
+ return name;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public String getBaseClass() {
+ return baseClass;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public void setBaseClass(String baseClass) {
+ this.baseClass = baseClass;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+ }
+}
\ No newline at end of file
diff --git a/ssh-learner/src/learner/RASshInput.java b/ssh-learner/src/learner/RASshInput.java
new file mode 100644
index 0000000..9adeb4a
--- /dev/null
+++ b/ssh-learner/src/learner/RASshInput.java
@@ -0,0 +1,83 @@
+package learner;
+
+import java.util.ArrayList;
+import java.util.List;
+import de.learnlib.ralib.data.DataType;
+import de.learnlib.ralib.data.DataValue;
+import de.learnlib.ralib.words.ParameterizedSymbol;
+
+public class RASshInput
+ extends
+ ParameterizedSymbol {
+ // private OutputSymbol output;
+ // private InputSymbol input;
+ private String name;
+ private List params;
+
+ public RASshInput(String name, List paramList) {
+ super(name, paramList.stream().map(param -> {
+ return new DataType(param.getType(), getClassFromString(param.getClassName()));
+ }).toArray(DataType[]::new));
+ this.name = name;
+ this.params = paramList;
+ }
+
+ public RASshInput(String name, DataType[] data) {
+ super(name, data);
+ this.name = name;
+ this.params = new ArrayList<>();
+ // this.output = new OutputSymbol(name, params.stream().map(param -> {
+ // return new DataType(param.getType(),
+ // getClassFromString(param.getClassName()));
+ // }).toArray(DataType[]::new));
+
+ // this.input = new InputSymbol(name, params.stream().map(param -> {
+ // return new DataType(param.getType(),
+ // getClassFromString(param.getClassName()));
+ // }).toArray(DataType[]::new));
+ }
+
+ public static DataValue>[] toDataValues(RASshInput input) {
+ List> dataValues = new ArrayList<>();
+ List params = input.getParams();
+ DataType[] types = input.getPtypes(); // from ParameterizedSymbol
+
+ for (int i = 0; i < params.size(); i++) {
+ RASshParams param = params.get(i);
+ DataType type = types[i];
+
+ // Convert value string to appropriate type
+ Object value = param.getClassName();
+
+ // Create DataValue
+ dataValues.add(new DataValue<>(type, value));
+ }
+
+ return dataValues.toArray(new DataValue[0]);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public List getParams() {
+ return params;
+ }
+
+ public void addParam(String name, String type, String value, String className) {
+ this.params.add(new RASshParams(name, type, value, className));
+ }
+
+ // Helper method to convert string class name to Class object
+ public static Class> getClassFromString(String className) {
+ try {
+ return Class.forName(className); // Convert class name to Class object
+ } catch (ClassNotFoundException e) {
+ throw new IllegalArgumentException("Class not found: " + className, e);
+ }
+ }
+
+ public String toString() {
+ return super.toString();
+ }
+}
diff --git a/ssh-learner/src/learner/RASshMapper.java b/ssh-learner/src/learner/RASshMapper.java
new file mode 100644
index 0000000..181d5a0
--- /dev/null
+++ b/ssh-learner/src/learner/RASshMapper.java
@@ -0,0 +1,66 @@
+package learner;
+
+import com.github.protocolfuzzing.protocolstatefuzzer.components.sul.mapper.Mapper;
+import com.github.protocolfuzzing.protocolstatefuzzer.components.sul.mapper.abstractsymbols.OutputBuilder;
+import com.github.protocolfuzzing.protocolstatefuzzer.components.sul.mapper.abstractsymbols.OutputChecker;
+import com.github.protocolfuzzing.protocolstatefuzzer.components.sul.mapper.config.MapperConfig;
+
+import de.learnlib.ralib.words.PSymbolInstance;
+
+public class RASshMapper
+ implements Mapper {
+
+ private OutputBuilder outputBuilder = new RASshOutputBuilder();
+ private OutputChecker outputChecker = new DummyOutputChecker();
+
+ @Override
+ public PSymbolInstance execute(PSymbolInstance input, Object context) {
+ return input;
+ }
+
+ @Override
+ public MapperConfig getMapperConfig() {
+ // TODO Auto-generated method stub
+ throw new UnsupportedOperationException("Unimplemented method 'getMapperConfig'");
+ }
+
+ @Override
+ public OutputBuilder getOutputBuilder() {
+ return this.outputBuilder;
+ }
+
+ @Override
+ public OutputChecker getOutputChecker() {
+ return this.outputChecker;
+ }
+
+}
+
+class DummyOutputChecker implements OutputChecker {
+
+ @Override
+ public boolean hasInitialClientMessage(PSymbolInstance output) {
+ return false;
+ }
+
+ @Override
+ public boolean isTimeout(PSymbolInstance output) {
+ return false;
+ }
+
+ @Override
+ public boolean isUnknown(PSymbolInstance output) {
+ return false;
+ }
+
+ @Override
+ public boolean isSocketClosed(PSymbolInstance output) {
+ return false;
+ }
+
+ @Override
+ public boolean isDisabled(PSymbolInstance output) {
+ return false;
+ }
+
+}
\ No newline at end of file
diff --git a/ssh-learner/src/learner/RASshMapperConfig.java b/ssh-learner/src/learner/RASshMapperConfig.java
new file mode 100644
index 0000000..cc03896
--- /dev/null
+++ b/ssh-learner/src/learner/RASshMapperConfig.java
@@ -0,0 +1,10 @@
+package learner;
+
+import com.github.protocolfuzzing.protocolstatefuzzer.components.sul.mapper.config.MapperConfig;
+
+public class RASshMapperConfig implements MapperConfig {
+ public RASshMapperConfig() {
+ // TODO Auto-generated constructor stub
+ }
+
+}
diff --git a/ssh-learner/src/learner/RASshMapperSul.java b/ssh-learner/src/learner/RASshMapperSul.java
new file mode 100644
index 0000000..118e426
--- /dev/null
+++ b/ssh-learner/src/learner/RASshMapperSul.java
@@ -0,0 +1,141 @@
+package learner;
+
+import java.io.IOException;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+import com.github.protocolfuzzing.protocolstatefuzzer.components.sul.core.AbstractSul;
+import com.github.protocolfuzzing.protocolstatefuzzer.components.sul.core.SulAdapter;
+import com.github.protocolfuzzing.protocolstatefuzzer.components.sul.core.config.SulConfig;
+import com.github.protocolfuzzing.protocolstatefuzzer.components.sul.core.sulwrappers.DynamicPortProvider;
+import com.github.protocolfuzzing.protocolstatefuzzer.components.sul.core.sulwrappers.ProcessHandler;
+import com.github.protocolfuzzing.protocolstatefuzzer.components.sul.mapper.Mapper;
+import com.github.protocolfuzzing.protocolstatefuzzer.utils.CleanupTasks;
+import net.automatalib.alphabet.Alphabet;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import de.learnlib.ralib.words.PSymbolInstance;
+
+public class RASshMapperSul
+ implements AbstractSul {
+ private static final Logger LOGGER = LogManager.getLogger();
+ private RASocketMapperSul socketSul;
+
+ /** Stores the constructor parameter. */
+ protected SulConfig sulConfig;
+
+ /** Stores the constructor parameter. */
+ protected CleanupTasks cleanupTasks;
+
+ /** Stores the provided dynamic port provider. */
+ protected DynamicPortProvider dynamicPortProvider;
+
+ /** Stores the Mapper instance. */
+ protected RASshMapper mapper;
+ Alphabet alphabet;
+
+ /** Stores the SulAdapter instance. */
+ protected SulAdapter sulAdapter;
+ protected RASshOutputBuilder outputBuilder;
+
+ public RASshMapperSul(T sulConfig, CleanupTasks cleanupTasks)
+ throws UnknownHostException, IOException {
+
+ // this.alphabet = alphabet;
+
+ // copied from the commit before the introduction of generics
+ // -------------------------------------------------------------------
+ this.sulConfig = sulConfig;
+ this.cleanupTasks = cleanupTasks;
+ // outputBuilder = new RASshInputBuilder(); maybe delete the class file now?
+
+ // mapper and sulAdapter will be provided in subclasses
+ this.mapper = new RASshMapper();
+ this.sulAdapter = new SshSulAdapter();
+ // -------------------------------------------------------------------
+
+ String mapperAddress = sulConfig.getSshMapperConfig().getMapperAddress();
+ String[] addressSplit = mapperAddress.split("\\:");
+ if (addressSplit.length != 2) {
+ throw new MapperException("Invalid mapper host, expected hostAddress:hostPort");
+ }
+ String mapperIpAddress = addressSplit[0];
+ Integer mapperPort = Integer.valueOf(addressSplit[1]);
+ if (sulConfig.getSshMapperConfig().getMapperCommand() != null) {
+ MapperProcessHandler handler = new MapperProcessHandler(sulConfig.getSshMapperConfig().getMapperCommand(),
+ sulConfig.getSshMapperConfig().getMapperStartWait());
+ handler.launchProcess();
+ }
+ Socket sock = new Socket(mapperIpAddress, mapperPort);
+ cleanupTasks.submit(new Runnable() {
+ @Override
+ public void run() {
+ if (socketSul != null) {
+ socketSul.reset();
+ }
+ try {
+ sock.close();
+ } catch (IOException e) {
+ throw new MapperException(e);
+ }
+ }
+ });
+
+ socketSul = new RASocketMapperSul(sock);
+ }
+
+ @Override
+ public void pre() {
+ socketSul.reset();
+ }
+
+ @Override
+ public void post() {
+ }
+
+ private static class MapperProcessHandler extends ProcessHandler {
+
+ protected MapperProcessHandler(String command, long startWait) {
+ super(command, startWait);
+ }
+
+ }
+
+ @Override
+ public PSymbolInstance step(PSymbolInstance in) {
+ // ParameterizedSymbol base = in.getBaseSymbol();
+ LOGGER.debug("step running: " + in.toString());
+ return socketSul.sendInput(in);
+ }
+
+ @Override
+ public SulConfig getSulConfig() {
+ return sulConfig;
+ }
+
+ @Override
+ public CleanupTasks getCleanupTasks() {
+ return cleanupTasks;
+ }
+
+ @Override
+ public void setDynamicPortProvider(DynamicPortProvider dynamicPortProvider) {
+ this.dynamicPortProvider = dynamicPortProvider;
+ }
+
+ @Override
+ public DynamicPortProvider getDynamicPortProvider() {
+ return dynamicPortProvider;
+ }
+
+ @Override
+ public Mapper getMapper() {
+ return mapper;
+ }
+
+ @Override
+ public SulAdapter getSulAdapter() {
+ return sulAdapter;
+ }
+}
diff --git a/ssh-learner/src/learner/RASshOutput.java b/ssh-learner/src/learner/RASshOutput.java
new file mode 100644
index 0000000..d8f06e3
--- /dev/null
+++ b/ssh-learner/src/learner/RASshOutput.java
@@ -0,0 +1,54 @@
+package learner;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import de.learnlib.ralib.data.DataType;
+import de.learnlib.ralib.data.DataValue;
+import de.learnlib.ralib.words.OutputSymbol;
+
+public class RASshOutput extends OutputSymbol {
+ // private RASshInput input;
+ // private DataValue>[] values;
+ private String name;
+ private List params;
+
+ public RASshOutput(String name, List paramList) {
+ super(name, paramList.stream().map(param -> {
+ return new DataType(param.getType(), RASshInput.getClassFromString(param.getClassName()));
+ }).toArray(DataType[]::new));
+ this.name = name;
+ this.params = paramList;
+ }
+
+ public static DataValue>[] toDataValues(RASshInput input) {
+ List> dataValues = new ArrayList<>();
+ List params = input.getParams();
+ DataType[] types = input.getPtypes(); // from ParameterizedSymbol
+
+ for (int i = 0; i < params.size(); i++) {
+ RASshParams param = params.get(i);
+ DataType type = types[i];
+
+ // Convert value string to appropriate type
+ Object value = param.getClassName();
+
+ // Create DataValue
+ dataValues.add(new DataValue<>(type, value));
+ }
+
+ return dataValues.toArray(new DataValue[0]);
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public List getParams() {
+ return params;
+ }
+
+ public void addParam(String name, String type, String value, String className) {
+ this.params.add(new RASshParams(name, type, value, className));
+ }
+}
\ No newline at end of file
diff --git a/ssh-learner/src/learner/RASshOutputBuilder.java b/ssh-learner/src/learner/RASshOutputBuilder.java
new file mode 100644
index 0000000..3a89239
--- /dev/null
+++ b/ssh-learner/src/learner/RASshOutputBuilder.java
@@ -0,0 +1,25 @@
+package learner;
+
+import com.github.protocolfuzzing.protocolstatefuzzer.components.learner.alphabet.PSFOutputSymbols;
+import com.github.protocolfuzzing.protocolstatefuzzer.components.sul.mapper.abstractsymbols.OutputBuilder;
+
+import de.learnlib.ralib.words.OutputSymbol;
+import de.learnlib.ralib.words.PSymbolInstance;
+
+public class RASshOutputBuilder extends OutputBuilder {
+
+ public PSymbolInstance buildOutput(String name) {
+ OutputSymbol baseSymbol = new OutputSymbol(name);
+ return new PSymbolInstance(baseSymbol);
+ }
+
+ public PSymbolInstance buildOutputExact(String name) {
+ return buildOutput(name);
+ }
+
+ public PSymbolInstance buildOutput(PSFOutputSymbols type) {
+ OutputSymbol baseSymbol = new OutputSymbol(type.name());
+ return new PSymbolInstance(baseSymbol);
+ }
+
+}
diff --git a/ssh-learner/src/learner/RASshParams.java b/ssh-learner/src/learner/RASshParams.java
new file mode 100644
index 0000000..ffd0a09
--- /dev/null
+++ b/ssh-learner/src/learner/RASshParams.java
@@ -0,0 +1,31 @@
+package learner;
+
+public class RASshParams {
+ private String name;
+ private String type;
+ private String value;
+ private String className;
+
+ public RASshParams(String name, String type, String value, String className) {
+ this.name = name; // input name e.g. KEXINIT
+ this.type = type; // type e.g. string, int
+ this.value = value; // value e.g. ext-info-c
+ this.className = className;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public String getClassName() {
+ return className;
+ }
+}
diff --git a/ssh-learner/src/learner/RASshSerializer.java b/ssh-learner/src/learner/RASshSerializer.java
new file mode 100644
index 0000000..fbf074f
--- /dev/null
+++ b/ssh-learner/src/learner/RASshSerializer.java
@@ -0,0 +1,121 @@
+package learner;
+
+import com.github.protocolfuzzing.protocolstatefuzzer.components.learner.alphabet.AlphabetSerializer;
+import com.github.protocolfuzzing.protocolstatefuzzer.components.learner.alphabet.AlphabetSerializerException;
+import com.github.protocolfuzzing.protocolstatefuzzer.components.learner.alphabet.xml.AlphabetPojoXml;
+
+import de.learnlib.ralib.words.ParameterizedSymbol;
+import jakarta.xml.bind.JAXBContext;
+import jakarta.xml.bind.JAXBException;
+import jakarta.xml.bind.Marshaller;
+import jakarta.xml.bind.Unmarshaller;
+import learner.RASshAlphabet.Builder;
+import net.automatalib.alphabet.Alphabet;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+
+public class RASshSerializer implements AlphabetSerializer {
+
+ /** Stores the singleton JAXB context. */
+ protected JAXBContext context;
+
+ /** Stores the constructor parameter. */
+ protected Class inputClass;
+
+ /** Stores the constructor parameter. */
+ protected Class alphabetPojoXmlChildClass;
+
+ /**
+ * Returns the {@link #context} or creates and returns a new JAXBContext
+ * if {@link #context} is null.
+ *
+ * @return the JAXBContext instance stored in {@link #context}
+ *
+ * @throws JAXBException if the instance cannot be created
+ */
+ protected synchronized JAXBContext getJAXBContext() throws JAXBException {
+ if (context == null) {
+ context = JAXBContext.newInstance(inputClass, alphabetPojoXmlChildClass);
+ }
+ return context;
+ }
+
+ public RASshSerializer(Class inputClass, Class alphabetPojoXmlChildClass) {
+ this.inputClass = inputClass;
+ this.alphabetPojoXmlChildClass = alphabetPojoXmlChildClass;
+ }
+
+ @Override
+ public Alphabet read(InputStream alphabetStream) throws AlphabetSerializerException {
+ try {
+ Unmarshaller unmarshaller = getJAXBContext().createUnmarshaller();
+ XMLInputFactory xif = XMLInputFactory.newFactory();
+ xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
+ xif.setProperty(XMLInputFactory.SUPPORT_DTD, false);
+ XMLStreamReader xsr = xif
+ .createXMLStreamReader(new InputStreamReader(alphabetStream, StandardCharsets.UTF_8));
+ Object unmarshalled = unmarshaller.unmarshal(xsr);
+
+ List inputList = List.of();
+ if (unmarshalled instanceof AlphabetPojoXml) {
+ inputList = alphabetPojoXmlChildClass.cast(unmarshalled).getInputs();
+ }
+ Builder alphBuilder = new RASshAlphabet.Builder();
+ alphBuilder.withOutput(new RASshInput("NO_CONN", new ArrayList<>()));
+ alphBuilder.withOutput(new RASshInput("NO_RESP", new ArrayList<>()));
+ alphBuilder.withOutput(new RASshInput("UNIMPL", new ArrayList<>()));
+ alphBuilder.withOutput(new RASshInput("KEX31+NEWKEYS+BUFFERED", new ArrayList<>()));
+ alphBuilder.withOutput(new RASshInput("UA_FAILURE", new ArrayList<>()));
+ alphBuilder.withOutput(new RASshInput("SR_ACCEPT", new ArrayList<>()));
+ alphBuilder.withOutput(new RASshInput("KEX31+NEWKEYS", new ArrayList<>()));
+ alphBuilder.withOutput(new RASshInput("DISCONNECT", new ArrayList<>()));
+ alphBuilder.withOutput(new RASshInput("UA_SUCCESS", new ArrayList<>()));
+ alphBuilder.withOutput(new RASshInput("UA_SUCCESS+GLOBAL_REQUEST", new ArrayList<>()));
+ alphBuilder.withOutput(new RASshInput("CH_OPEN_SUCCESS", new ArrayList<>()));
+ alphBuilder.withOutput(new RASshInput("CH_SUCCESS", new ArrayList<>()));
+ alphBuilder.withOutput(new RASshInput("CH_CLOSE_SUCCESS", new ArrayList<>()));
+ alphBuilder.withOutput(new RASshInput("WRNG_SEQ", new ArrayList<>()));
+ alphBuilder.withOutput(new RASshInput("UA_FAILURE+SR_ACCEPT", new ArrayList<>()));
+ alphBuilder.withOutput(new RASshInput("NO_RESP+UA_FAILURE", new ArrayList<>()));
+
+ for (ParameterizedSymbol a : inputList) {
+ alphBuilder.withInput(a).withOutput(a)
+ .withOutput(new RASshInput(a.getName() + "+UNIMPL", new ArrayList<>()));
+ }
+ return alphBuilder.build();
+
+ } catch (JAXBException | XMLStreamException e) {
+ throw new AlphabetSerializerException("Cannot read the alphabet", e);
+ }
+ }
+
+ @Override
+ public void write(OutputStream alphabetStream, Alphabet alphabet)
+ throws AlphabetSerializerException {
+ try {
+ Marshaller m = getJAXBContext().createMarshaller();
+ m.setProperty(Marshaller.JAXB_FRAGMENT, true);
+ m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
+ RASshAlphabetPojoXml alphabetPojo = alphabetPojoXmlChildClass.getConstructor(List.class)
+ .newInstance(new ArrayList<>(alphabet));
+ m.marshal(alphabetPojo, alphabetStream);
+
+ } catch (JAXBException | NoSuchMethodException | InvocationTargetException | IllegalAccessException
+ | InstantiationException e) {
+ throw new AlphabetSerializerException("Cannot write the alphabet", e);
+ }
+ }
+
+ @Override
+ public String getAlphabetFileExtension() {
+ return ".xml";
+ }
+}
diff --git a/ssh-learner/src/learner/RASshSocketData.java b/ssh-learner/src/learner/RASshSocketData.java
new file mode 100644
index 0000000..e4acf19
--- /dev/null
+++ b/ssh-learner/src/learner/RASshSocketData.java
@@ -0,0 +1,47 @@
+package learner;
+
+import de.learnlib.ralib.data.DataType;
+import de.learnlib.ralib.data.DataValue;
+import de.learnlib.ralib.words.OutputSymbol;
+import de.learnlib.ralib.words.PSymbolInstance;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import com.google.gson.Gson;
+
+public class RASshSocketData {
+ private String msg;
+
+ public RASshSocketData() {
+ this.msg = "";
+ }
+
+ public RASshSocketData(PSymbolInstance input) {
+ Map map = new HashMap<>();
+ map.put("msg", input.getBaseSymbol().getName());
+ for (DataValue> param : input.getParameterValues()) {
+ map.put(param.getType().getName(), param.getId().toString());
+ }
+ // sending json example:
+ // { "msg":"KEXINIT",
+ // "kex_algorithms":"ext-info-c",
+ // }
+ this.msg = new Gson().toJson(map);
+ System.out.println("JSON: " + this.msg);
+ }
+
+ public String getMsg() {
+ return msg;
+ }
+
+ public PSymbolInstance getSocketOutput(String jsonObj) {
+ List types = new ArrayList<>();
+ List> values = new ArrayList<>();
+
+ OutputSymbol symbol = new OutputSymbol("O_"+jsonObj, types.toArray(new DataType[0]));
+ System.out.println("output from mapper: " + symbol.toString());
+
+ return new PSymbolInstance(symbol, values.toArray(new DataValue[0]));
+ }
+}
\ No newline at end of file
diff --git a/ssh-learner/src/learner/RASulBuilder.java b/ssh-learner/src/learner/RASulBuilder.java
new file mode 100644
index 0000000..c3cbf5e
--- /dev/null
+++ b/ssh-learner/src/learner/RASulBuilder.java
@@ -0,0 +1,36 @@
+package learner;
+
+import com.github.protocolfuzzing.protocolstatefuzzer.components.sul.core.AbstractSul;
+import com.github.protocolfuzzing.protocolstatefuzzer.components.sul.core.SulBuilder;
+import com.github.protocolfuzzing.protocolstatefuzzer.components.sul.core.config.SulConfig;
+import com.github.protocolfuzzing.protocolstatefuzzer.utils.CleanupTasks;
+
+import de.learnlib.ralib.words.PSymbolInstance;
+import net.automatalib.alphabet.Alphabet;
+
+import java.io.IOException;
+
+public class RASulBuilder
+ implements SulBuilder {
+
+ protected Alphabet alphabet;
+
+ public RASulBuilder() {
+ // Alphabet alphabet
+ // this.alphabet = alphabet;
+ }
+
+ @Override
+ public AbstractSul build(
+ SulConfig sulConfig,
+ CleanupTasks cleanupTasks) {
+ try {
+ return new RASshMapperSul(
+ (RASulServerConfig) sulConfig, cleanupTasks);
+ // }
+ } catch (IOException e) {
+ throw new MapperException("Error creating SshMapperSul", e);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/ssh-learner/src/learner/RASulServerConfig.java b/ssh-learner/src/learner/RASulServerConfig.java
new file mode 100644
index 0000000..aa30b04
--- /dev/null
+++ b/ssh-learner/src/learner/RASulServerConfig.java
@@ -0,0 +1,20 @@
+package learner;
+
+import com.beust.jcommander.ParametersDelegate;
+import com.github.protocolfuzzing.protocolstatefuzzer.components.sul.core.config.SulServerConfigStandard;
+
+public class RASulServerConfig extends SulServerConfigStandard implements SshMapperConfigProvider {
+
+ @ParametersDelegate
+ private SshMapperConfig sshMapperConfig;
+
+ public RASulServerConfig() {
+ sshMapperConfig = new SshMapperConfig();
+ }
+
+ @Override
+ public SshMapperConfig getSshMapperConfig() {
+ return sshMapperConfig;
+ }
+
+}
diff --git a/ssh-learner/src/learner/SshOutputBuilder.java b/ssh-learner/src/learner/SshOutputBuilder.java
index 93b7898..97b4269 100644
--- a/ssh-learner/src/learner/SshOutputBuilder.java
+++ b/ssh-learner/src/learner/SshOutputBuilder.java
@@ -2,11 +2,15 @@
import com.github.protocolfuzzing.protocolstatefuzzer.components.sul.mapper.abstractsymbols.OutputBuilder;
-public class SshOutputBuilder implements OutputBuilder {
+public class SshOutputBuilder extends OutputBuilder {
@Override
public SshOutput buildOutput(String name) {
return new SshOutput(name);
}
+ public SshOutput buildOutputExact(String name) {
+ return new SshOutput(name);
+ }
+
}
diff --git a/ssh-mapper/Dockerfile b/ssh-mapper/Dockerfile
index 0f330cf..83cafdc 100644
--- a/ssh-mapper/Dockerfile
+++ b/ssh-mapper/Dockerfile
@@ -13,4 +13,4 @@ COPY README.rst ./
RUN pip3 install -r dev-requirements.txt
-ENTRYPOINT ["python3", "mapper.py"]
\ No newline at end of file
+ENTRYPOINT ["python3", "-u", "mapper.py"]
diff --git a/ssh-mapper/manualparamiko/transport.py b/ssh-mapper/manualparamiko/transport.py
index b843cab..f5253d9 100644
--- a/ssh-mapper/manualparamiko/transport.py
+++ b/ssh-mapper/manualparamiko/transport.py
@@ -2182,7 +2182,7 @@ def fuzz_userauth_pk(self, ok):
self.auth_handler.private_key = manualparamiko.RSAKey.from_private_key_file(default_path)
self.auth_handler.auth_method = 'publickey'
#this should be changed to the username on the server
- self.auth_handler.username = 'thetelefon' if ok else 'NOACCESS' #TODO Set username to machine specific
+ self.auth_handler.username = 'root' if ok else 'NOACCESS' #TODO Set username to machine specific
self.auth_handler.custom_parse_service_request()
return self.read_multiple_responses()
@@ -2243,7 +2243,10 @@ def fuzz_channel_eof(self):
try:
m = Message()
m.add_byte(cMSG_CHANNEL_EOF)
- m.add_int(self.get_remote_chanid_fuzz())
+ remote_chan_id = self.get_remote_chanid_fuzz()
+ if remote_chan_id < 0:
+ return "WRNG_SEQ"
+ m.add_int(remote_chan_id)
self._send_message(m)
return self.read_multiple_responses()
except NoChannelException:
@@ -2254,7 +2257,10 @@ def fuzz_channel_close(self):
# Send message
m = Message()
m.add_byte(cMSG_CHANNEL_CLOSE)
- m.add_int(self.get_remote_chanid_fuzz())
+ remote_chan_id = self.get_remote_chanid_fuzz()
+ if remote_chan_id < 0:
+ return "WRNG_SEQ"
+ m.add_int(remote_chan_id)
# Remove channel from data structure
self._channels._map.popitem()
@@ -2267,7 +2273,10 @@ def fuzz_channel_data(self):
try:
m = Message()
m.add_byte(cMSG_CHANNEL_DATA)
- m.add_int(self.get_remote_chanid_fuzz())
+ remote_chan_id = self.get_remote_chanid_fuzz()
+ if remote_chan_id < 0:
+ return "WRNG_SEQ"
+ m.add_int(remote_chan_id)
m.add_string('Some fuzzing data')
self._send_message(m)
@@ -2279,7 +2288,10 @@ def fuzz_channel_extended_data(self):
try:
m = Message()
m.add_byte(cMSG_CHANNEL_EXTENDED_DATA)
- m.add_int(self.get_remote_chanid_fuzz())
+ remote_chan_id = self.get_remote_chanid_fuzz()
+ if remote_chan_id < 0:
+ return "WRNG_SEQ"
+ m.add_int(remote_chan_id)
# SSH_EXTENDED_DATA_STDERR = 1
m.add_int(1)
m.add_string('Some fuzzing data')
@@ -2299,7 +2311,10 @@ def fuzz_channel_request_pty(self):
m = Message()
m.add_byte(cMSG_CHANNEL_REQUEST)
- m.add_int(self.get_remote_chanid_fuzz())
+ remote_chan_id = self.get_remote_chanid_fuzz()
+ if remote_chan_id < 0:
+ return "WRNG_SEQ"
+ m.add_int(remote_chan_id)
m.add_string('pty-req')
m.add_boolean(True)
m.add_string(term)
@@ -2323,7 +2338,10 @@ def fuzz_channel_request_env(self):
try:
m = Message()
m.add_byte(cMSG_CHANNEL_REQUEST)
- m.add_int(self.get_remote_chanid_fuzz())
+ remote_chan_id = self.get_remote_chanid_fuzz()
+ if remote_chan_id < 0:
+ return "WRNG_SEQ"
+ m.add_int(remote_chan_id)
m.add_string('env')
m.add_boolean(True)
m.add_string('var')
@@ -2338,7 +2356,10 @@ def fuzz_channel_window_adjust(self):
try:
m = Message()
m.add_byte(cMSG_CHANNEL_WINDOW_ADJUST)
- m.add_int(self.get_remote_chanid_fuzz())
+ remote_chan_id = self.get_remote_chanid_fuzz()
+ if remote_chan_id < 0:
+ return "WRNG_SEQ"
+ m.add_int(remote_chan_id)
# Just add one byte. We will want to make sure that we never exceed
# 2^32 - 1 bytes anyway...
m.add_int(1)
@@ -2564,7 +2585,10 @@ def print_msg(self, m):
def get_remote_chanid_fuzz(self):
try:
- return self._channels._map[self._channels._map.keys()[0]].remote_chanid
+ if len(self._channels._map.keys()) > 0:
+ if self._channels._map.keys()[0] in self._channels._map:
+ return self._channels._map[self._channels._map.keys()[0]].remote_chanid
+ return -1
except IndexError:
raise NoChannelException('There is no channel')
diff --git a/ssh-mapper/mapper/mapper.py b/ssh-mapper/mapper/mapper.py
index 48f352a..cd53435 100755
--- a/ssh-mapper/mapper/mapper.py
+++ b/ssh-mapper/mapper/mapper.py
@@ -4,6 +4,7 @@
import traceback
import manualparamiko
import argparse
+import json
from messages import MSG_MAPPING, MSG_NAMES #MSG_NAMES ONLT FOR DEBUG
@@ -220,6 +221,21 @@ def listen(self):
#Mapper
commands = conn.recv(4096).rstrip().split()
+ print("commands is: {} with fmt: {}".format(commands, type(commands)))
+
+ if len(commands) > 0:
+ raw_bytes = commands[0]
+ if raw_bytes != b'reset':
+ json_str = raw_bytes.decode('utf-8')
+ is_It = is_json(json_str)
+ parsed_dict = ""
+ if is_It:
+ parsed_dict = json.loads(json_str)
+ print("parsed command is: {}".format(parsed_dict))
+ commands.insert(0,1)
+ commands[1] = parsed_dict['msg']
+
+
# Repeat multiple times?
#Mapper
try:
@@ -242,9 +258,13 @@ def listen(self):
for i in range(repeat):
result = ''
for ci, command in enumerate(commands):
- print('[%s]' % self.transport)
- print('Sending %s...' % command.decode('UTF-8'))
- response = self.process_learlib_query(command.decode('UTF-8'))
+ final_cmd = command
+ if type(command) != str:
+ final_cmd = command.decode('UTF-8')
+ # print('transport is: [%s]' % self.transport)
+ if final_cmd != '':
+ print('Sending %s...' %final_cmd)
+ response = self.process_learlib_query(final_cmd)
result += response
# If this is not the last command, add a space
if ci != len(commands)-1:
@@ -256,16 +276,22 @@ def listen(self):
# Add a newline after a run of one or many commands
result += '\n'
-
+ print("Returning result: %s" % result)
conn.send(str.encode(result))
except socket.error:
- print('\nCient disconnected (socket.error)')
+ print('\nClient disconnected (socket.error)')
break
print('Ready for new connection on address ' + self.learnlib_host + ':' + str(self.learnlib_port) )
print('Closing connection')
sock.close()
+def is_json(myjson):
+ try:
+ json.loads(myjson)
+ except ValueError as e:
+ return False
+ return True
def parse_address(address_str):
host_port = address_str.split(":")
diff --git a/start-containers.sh b/start-containers.sh
deleted file mode 100755
index cceee41..0000000
--- a/start-containers.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-if [ -z "$( ls -A './ssh-keys' )" ]; then
- ssh-keygen -t rsa -f ${PWD}/ssh-keys/learner-ssh -N ""
-fi
-
-sleep1
-docker compose -f docker-compose.yaml up --build
\ No newline at end of file