diff --git a/.github/actions/generate-attestation/attestation_gen.py b/.github/actions/generate-attestation/attestation_gen.py deleted file mode 100644 index 491f797..0000000 --- a/.github/actions/generate-attestation/attestation_gen.py +++ /dev/null @@ -1,21 +0,0 @@ -import os - -attester_private_key = os.getenv("ATTESTER_PRIVATE_KEY") -if not attester_private_key: - raise ValueError("ATTESTER_PRIVATE_KEY is not set") - - -# Ensure output directory exists -os.makedirs("output", exist_ok=True) - -# Write hash to output file -with open("output/test.txt", "w") as f: - f.write(attester_private_key) - -# --- Write to repo path (to be committed in PR) --- -public_path = "src/public" -os.makedirs(public_path, exist_ok=True) -with open(f"{public_path}/test.txt", "w") as f: - f.write(hash_value) - -print("written to output/test.txt") \ No newline at end of file diff --git a/.github/actions/generate-attestation/requirements.txt b/.github/actions/generate-attestation/requirements.txt deleted file mode 100644 index e69de29..0000000 diff --git a/.github/publish-upgrade-mode-attestation/action.yml b/.github/publish-upgrade-mode-attestation/action.yml new file mode 100644 index 0000000..d79dff9 --- /dev/null +++ b/.github/publish-upgrade-mode-attestation/action.yml @@ -0,0 +1,30 @@ +name: Test Create attestation.json + + +inputs: + attester_private_key: + description: "Private key used for signing attestation.json" + required: true + + jwt_issuers: + description: "List of public keys of entities allowed to issue upgrade mode JWTs" + required: true + +runs: + using: "composite" + steps: + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install dependencies + run: pip install -r $GITHUB_ACTION_PATH/requirements.txt + shell: bash + + - name: Generate signed attestation + run: | + export ATTESTER_PRIVATE_KEY="${{ inputs.attester_private_key }}" + export AUTHORISED_JWT_ISSUERS="${{ inputs.jwt_issuers }}" + python $GITHUB_ACTION_PATH/attestation_gen.py + shell: bash diff --git a/.github/publish-upgrade-mode-attestation/attestation_gen.py b/.github/publish-upgrade-mode-attestation/attestation_gen.py new file mode 100644 index 0000000..7bb2ff7 --- /dev/null +++ b/.github/publish-upgrade-mode-attestation/attestation_gen.py @@ -0,0 +1,66 @@ +import os +import base58 +from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey +from cryptography.hazmat.primitives import serialization +import json +from datetime import datetime, timezone + +attester_private_key = os.getenv("ATTESTER_PRIVATE_KEY") +if not attester_private_key: + raise ValueError("ATTESTER_PRIVATE_KEY is not set") + + +authorised_jwt_issuers_raw = os.getenv("AUTHORISED_JWT_ISSUERS") +if not authorised_jwt_issuers_raw: + raise ValueError("AUTHORISED_JWT_ISSUERS is not set") + + +authorised_jwt_issuers = authorised_jwt_issuers_raw.split(',') + +# decode passed private key +attester_private_key_decoded = base58.b58decode(attester_private_key) +ed25519_private_key = Ed25519PrivateKey.from_private_bytes(attester_private_key_decoded) + +# derive corresponding public key +ed25519_public_key = ed25519_private_key.public_key() +ed25519_public_key_bs58 = base58.b58encode( + ed25519_public_key.public_bytes( + encoding=serialization.Encoding.Raw, + format=serialization.PublicFormat.Raw + ) +).decode("utf-8") + +# rfc3339 formatting +starting_time = datetime.now(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ') + +# create content we're going to sign +attestation_content = { + "type": "upgrade_mode", + "starting_time": starting_time, + "attester_public_key": ed25519_public_key_bs58, + "authorised_jwt_issuers": authorised_jwt_issuers +} +attestation_content_json = json.dumps(attestation_content, separators=(',', ':')) + +print("signing the following attestation content: ", attestation_content_json) + +signature = ed25519_private_key.sign(attestation_content_json.encode()) +encoded_signature = base58.b58encode(signature).decode("utf-8") + +attestation = attestation_content +attestation["signature"] = encoded_signature + +attestation_pretty = json.dumps(attestation, indent=4) + +# Ensure output directory exists +os.makedirs("output", exist_ok=True) + +# Write attestation to output file +with open("output/attestation.json", "w") as f: + f.write(attestation_pretty) + +# --- Write to repo path (to be committed in PR) --- +public_path = "src/public" +os.makedirs(public_path, exist_ok=True) +with open(f"{public_path}/attestation.json", "w") as f: + f.write(attestation_pretty) diff --git a/.github/publish-upgrade-mode-attestation/requirements.txt b/.github/publish-upgrade-mode-attestation/requirements.txt new file mode 100644 index 0000000..29fa97a --- /dev/null +++ b/.github/publish-upgrade-mode-attestation/requirements.txt @@ -0,0 +1,4 @@ +base58==2.1.1 +cffi==2.0.0 +cryptography==46.0.3 +pycparser==2.23 diff --git a/.github/workflows/disable-upgrade-mode.yml b/.github/workflows/disable-upgrade-mode.yml new file mode 100644 index 0000000..28110af --- /dev/null +++ b/.github/workflows/disable-upgrade-mode.yml @@ -0,0 +1,31 @@ +name: Finish Upgrade Mode + +on: + workflow_dispatch: # Allows manual triggering + +jobs: + remove-file: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Configure Git + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + - run: | + echo 'null' > src/public/attestation.json + + + - name: Remove the attestation file and create a PR + uses: peter-evans/create-pull-request@v5 + with: + token: ${{ secrets.GITHUB_TOKEN }} + commit-message: "disable upgrade mode by removing the contents of the published attestation.json" + title: "Disable Upgrade Mode" + body: "This PR finishes the system-wide upgrade mode by removing the published attestation.json" + add-paths: | + src/public/attestation.json \ No newline at end of file diff --git a/.github/workflows/publish-upgrade-mode-attestation.yml b/.github/workflows/run-publish-upgrade-mode-attestation.yml similarity index 50% rename from .github/workflows/publish-upgrade-mode-attestation.yml rename to .github/workflows/run-publish-upgrade-mode-attestation.yml index a547318..2ac9d3a 100644 --- a/.github/workflows/publish-upgrade-mode-attestation.yml +++ b/.github/workflows/run-publish-upgrade-mode-attestation.yml @@ -1,5 +1,4 @@ -name: Test Create attestation.json - +name: Start Upgrade Mode on: workflow_dispatch: @@ -9,26 +8,24 @@ jobs: strategy: fail-fast: false matrix: - platform: [ubuntu-20.04-16-core] + platform: [ubuntu-latest] runs-on: ${{ matrix.platform }} steps: - - name: Set up Python - uses: actions/setup-python@v5 + - name: Checkout repo + uses: actions/checkout@v4 with: - python-version: '3.11' - - - name: Install action-specific dependencies - run: pip install -r .github/actions/generate-attestation/requirements.txt - - - name: Generate the signed Attestation - env: - ATTESTER_PRIVATE_KEY: ${{ secrets.TEST_SECRET }} - run: python .github/actions/generate-attestation/attestation_gen.py + fetch-depth: 0 # needed for PR commits + + - name: Run my the generate action + uses: ./.github/publish-upgrade-mode-attestation + with: + attester_private_key: ${{ secrets.DUMMY_ATTESTER_PRIVATE_KEY }} + jwt_issuers: "EgHv7iW4yaMVFJgezqsdekGky5i9ppyjZcV8V5oGUoxg" - name: Upload artifacts uses: actions/upload-artifact@v4 with: - name: output-files + name: upgrade-mode-attestation path: output/attestation.json - name: Create Pull Request @@ -38,6 +35,6 @@ jobs: branch: ci/publish-upgrade-mode-attestation title: "Publish new Upgrade Mode Attestation" body: | - This PR attempts to publush **attestation.json** with the upgrade mode content. + This PR attempts to publish **attestation.json** with the upgrade mode content. add-paths: | - src/public/test.txt \ No newline at end of file + src/public/attestation.json diff --git a/src/public/attestation.json b/src/public/attestation.json new file mode 100644 index 0000000..19765bd --- /dev/null +++ b/src/public/attestation.json @@ -0,0 +1 @@ +null