diff --git a/.github/python_matcher.json b/.github/python_matcher.json
new file mode 100644
index 0000000000000000000000000000000000000000..ac38ab7eca88eec6bca07f0cf5bd7b72337807ee
--- /dev/null
+++ b/.github/python_matcher.json
@@ -0,0 +1,42 @@
+{
+    "problemMatcher": [
+        {
+            "owner": "yapf-diff",
+            "pattern": [
+                {
+                    "regexp": "^[+-]{3}\\s*([^\\s]*)\\s*\\((original|reformatted)\\)$",
+                    "file": 1
+                },
+                {
+                    "regexp": "^@@\\s*-(\\d+),(\\d+)\\s*\\+(\\d+),(\\d+)\\s*@@$",
+                    "line": 1,
+                    "column": 2
+                },
+                {
+                    "regexp": "^(\\s|\\+[^+]|\\-[^-]).*$",
+                    "loop": true,
+                    "message": 1
+                }
+            ]
+        },
+        {
+            "owner": "pylint",
+            "pattern": [
+                {
+                    "regexp": "^PYLINT:(.*)/.*$",
+                    "fromPath": 1
+                },
+                {
+                    "regexp": "^\\*{13}\\s*Module\\s+(.*)$",
+                    "file": 1
+                },
+                {
+                    "regexp": "^([CEFIRW]\\d{4}):\\s*(\\d+)\\s*:\\s*(.*)$",
+                    "code": 1,
+                    "line": 2,
+                    "message": 3
+                }
+            ]
+        }
+    ]
+}
diff --git a/.github/workflows/cargo_audit.yml b/.github/workflows/cargo_audit.yml
index 94b00f6ea242df015e54d4cb33abfb4be1041183..584dc2068ec1dd0d7c65a47481442e459feb7e30 100644
--- a/.github/workflows/cargo_audit.yml
+++ b/.github/workflows/cargo_audit.yml
@@ -2,16 +2,16 @@ name: Security audit
 on:
   schedule:
     - cron: '0 0 * * *'
+
 jobs:
   audit:
     runs-on: ubuntu-18.04
+    if: env.GITHUB_HEAD_REF == 0
     steps:
       - uses: actions/checkout@v2
       - uses: actions-rs/toolchain@v1
         with:
-          toolchain: nightly
           target: thumbv7em-none-eabi
-          override: true
       - uses: actions/setup-python@v1
         with:
           python-version: 3.7
diff --git a/.github/workflows/cargo_check.yml b/.github/workflows/cargo_check.yml
index 9697b373bb24d8e85e1a3ea745d5baa3fa6cce32..36110a9217bc95aa107ccd7d78c10e4c49ef7cd9 100644
--- a/.github/workflows/cargo_check.yml
+++ b/.github/workflows/cargo_check.yml
@@ -19,9 +19,7 @@ jobs:
       - uses: actions/checkout@v2
       - uses: actions-rs/toolchain@v1
         with:
-          toolchain: nightly
           target: thumbv7em-none-eabi
-          override: true
       - uses: actions/setup-python@v1
         with:
           python-version: 3.7
diff --git a/.github/workflows/cargo_fmt.yml b/.github/workflows/cargo_fmt.yml
index 324b4755c7fbd5975bb7e8eea2684fe3c75eea5d..00589ca0dcc175c0533eb8d6a7d1dd5538838b05 100644
--- a/.github/workflows/cargo_fmt.yml
+++ b/.github/workflows/cargo_fmt.yml
@@ -19,9 +19,7 @@ jobs:
       - uses: actions/checkout@v2
       - uses: actions-rs/toolchain@v1
         with:
-          toolchain: nightly
           target: thumbv7em-none-eabi
-          override: true
       - uses: actions/setup-python@v1
         with:
           python-version: 3.7
diff --git a/.github/workflows/cbor_test.yml b/.github/workflows/cbor_test.yml
new file mode 100644
index 0000000000000000000000000000000000000000..a00775242c46b673c1ba84218177c333c0a11125
--- /dev/null
+++ b/.github/workflows/cbor_test.yml
@@ -0,0 +1,36 @@
+---
+name: CBOR tests
+on:
+  push:
+    paths:
+      - 'libraries/cbor/**/*'
+  pull_request:
+    types: [opened, synchronize, reopened]
+
+jobs:
+  cbor_test:
+    runs-on: ubuntu-18.04
+    steps:
+      - uses: actions/checkout@v2
+      - uses: actions-rs/toolchain@v1
+        with:
+          target: thumbv7em-none-eabi
+      - uses: actions/setup-python@v1
+        with:
+          python-version: 3.7
+      - name: Install Python dependencies
+        run: python -m pip install --upgrade pip setuptools wheel
+      - name: Set up OpenSK
+        run: ./setup.sh
+
+      - name: Unit testing of CBOR library (release mode)
+        uses: actions-rs/cargo@v1
+        with:
+          command: test
+          args: --manifest-path libraries/cbor/Cargo.toml --release --features std
+
+      - name: Unit testing of CBOR library (debug mode)
+        uses: actions-rs/cargo@v1
+        with:
+          command: test
+          args: --manifest-path libraries/cbor/Cargo.toml --features std
diff --git a/.github/workflows/crypto_test.yml b/.github/workflows/crypto_test.yml
new file mode 100644
index 0000000000000000000000000000000000000000..790bee24a1099bb1cd61f80298971582219a0c40
--- /dev/null
+++ b/.github/workflows/crypto_test.yml
@@ -0,0 +1,40 @@
+---
+name: Crypto library tests
+on:
+  push:
+    paths:
+      - 'libraries/crypto/**/*'
+  pull_request:
+    types: [opened, synchronize, reopened]
+    paths:
+      - 'libraries/crypto/**/*'
+
+jobs:
+  crypto_test:
+    runs-on: ubuntu-18.04
+    steps:
+      - uses: actions/checkout@v2
+      - uses: actions-rs/toolchain@v1
+        with:
+          target: thumbv7em-none-eabi
+      - uses: actions/setup-python@v1
+        with:
+          python-version: 3.7
+      - name: Install Python dependencies
+        run: python -m pip install --upgrade pip setuptools wheel
+      - name: Set up OpenSK
+        run: ./setup.sh
+
+      - run: echo "::set-env name=RUSTFLAGS::-C target-feature=+aes"
+
+      - name: Unit testing of crypto library (release mode)
+        uses: actions-rs/cargo@v1
+        with:
+          command: test
+          args: --manifest-path libraries/crypto/Cargo.toml --release --features std,derive_debug
+
+      - name: Unit testing of crypto library (debug mode)
+        uses: actions-rs/cargo@v1
+        with:
+          command: test
+          args: --manifest-path libraries/crypto/Cargo.toml --features std,derive_debug
diff --git a/.github/workflows/opensk_build.yml b/.github/workflows/opensk_build.yml
new file mode 100644
index 0000000000000000000000000000000000000000..a99438aca47b37a95669dd144c4e9af290758319
--- /dev/null
+++ b/.github/workflows/opensk_build.yml
@@ -0,0 +1,31 @@
+---
+name: OpenSK build
+on:
+  push:
+  pull_request:
+    types: [opened, synchronize, reopened]
+
+jobs:
+  build_ctap2:
+    strategy:
+      matrix:
+        os: [ubuntu-18.04, macos-10.15]
+    runs-on: ${{ matrix.os }}
+    steps:
+      - uses: actions/checkout@v2
+      - uses: actions-rs/toolchain@v1
+        with:
+          target: thumbv7em-none-eabi
+      - uses: actions/setup-python@v1
+        with:
+          python-version: 3.7
+      - name: Install Python dependencies
+        run: python -m pip install --upgrade pip setuptools wheel
+      - name: Set up OpenSK
+        run: ./setup.sh
+
+      - name: Building OpenSK
+        uses: actions-rs/cargo@v1
+        with:
+          command: build
+          args: --release --target=thumbv7em-none-eabi --features with_ctap1
diff --git a/.github/workflows/opensk_test.yml b/.github/workflows/opensk_test.yml
new file mode 100644
index 0000000000000000000000000000000000000000..6b177638145cc60ab477b5a80bb044a64d9ae806
--- /dev/null
+++ b/.github/workflows/opensk_test.yml
@@ -0,0 +1,51 @@
+---
+name: OpenSK tests
+on:
+  push:
+    paths:
+      - 'src/**/*.rs'
+  pull_request:
+    types: [opened, synchronize, reopened]
+
+jobs:
+  ctap2_test:
+    name: CTAP2 unit tests
+    runs-on: ubuntu-18.04
+
+    steps:
+      - uses: actions/checkout@v2
+      - uses: actions-rs/toolchain@v1
+        with:
+          target: thumbv7em-none-eabi
+      - uses: actions/setup-python@v1
+        with:
+          python-version: 3.7
+      - name: Install Python dependencies
+        run: python -m pip install --upgrade pip setuptools wheel
+      - name: Set up OpenSK
+        run: ./setup.sh
+
+      - name: Unit testing of CTAP2 (release mode)
+        uses: actions-rs/cargo@v1
+        with:
+          command: test
+          args: --release --features std
+
+      - name: Unit testing of CTAP2 (debug mode)
+        uses: actions-rs/cargo@v1
+        with:
+          command: test
+          args: --features std
+
+      - name: Unit testing of CTAP2 (release mode + CTAP1)
+        uses: actions-rs/cargo@v1
+        with:
+          command: test
+          args: --release --features std,with_ctap1
+
+      - name: Unit testing of CTAP2 (debug mode + CTAP1)
+        uses: actions-rs/cargo@v1
+        with:
+          command: test
+          args: --features std,with_ctap1
+
diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml
index dc739b8b71234fd0a7e507e3e8ae4a774eaaf218..f6051a3e9fda0f7ddfa9208c89d51fa5b9309886 100644
--- a/.github/workflows/python.yml
+++ b/.github/workflows/python.yml
@@ -22,11 +22,11 @@ jobs:
       - name: Install dependencies
         run: |
           python -m pip install --upgrade pip setuptools wheel
-          pip install tockloader
+          pip install tockloader pylint
+      - name: Register matcher
+        run: echo ::add-matcher::./.github/python_matcher.json
       - name: Test code with pylint
-        run: |
-          pip install pylint
-          pylint --rcfile=.pylintrc --score=n `find . ! -path "./third_party/*" -type f -name '*.py'`
+        run: ./tools/run_pylint.sh
 
   yapf:
     runs-on: ubuntu-18.04
@@ -39,6 +39,8 @@ jobs:
       - name: Install dependencies
         run: |
           python -m pip install --upgrade pip setuptools wheel
-          pip install yapf
+          pip install yapf tockloader
       - name: Test code formatting with yapf
-        run: yapf --style=chromium --recursive --exclude third_party --diff .
+        run: |
+          echo ::add-matcher::./.github/python_matcher.json
+          yapf --style=chromium --recursive --exclude third_party --diff .
diff --git a/build.rs b/build.rs
index 70844b045766775dfa9843e0e2aa63773dad33c1..c8c90078047ae33c1441e20972ad7b2e05c12937 100644
--- a/build.rs
+++ b/build.rs
@@ -35,9 +35,8 @@ fn main() {
 
     // Load the OpenSSL PEM ECC key
     let ecc_data = include_bytes!("crypto_data/opensk.key");
-    let pkey = ec::EcKey::private_key_from_pem(ecc_data)
-        .ok()
-        .expect("Failed to load OpenSK private key file");
+    let pkey =
+        ec::EcKey::private_key_from_pem(ecc_data).expect("Failed to load OpenSK private key file");
 
     // Check key validity
     pkey.check_key().unwrap();
@@ -70,9 +69,7 @@ fn main() {
 
     // Convert the PEM certificate to DER and extract the serial for AAGUID
     let input_pem_cert = include_bytes!("crypto_data/opensk_cert.pem");
-    let cert = x509::X509::from_pem(input_pem_cert)
-        .ok()
-        .expect("Failed to load OpenSK certificate");
+    let cert = x509::X509::from_pem(input_pem_cert).expect("Failed to load OpenSK certificate");
 
     // Do some sanity check on the certificate
     assert!(cert
diff --git a/tools/run_pylint.sh b/tools/run_pylint.sh
new file mode 100755
index 0000000000000000000000000000000000000000..2953e4fbf7c5fa53eb29ab7f397e0f4fe35dc1a3
--- /dev/null
+++ b/tools/run_pylint.sh
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+# Copyright 2019 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SUCCESS=0
+
+# Ensure we are at the project root directory
+cd $(readlink -f $(dirname $0))/..
+
+for file in `find . ! -path "./third_party/*" -type f -name '*.py'`
+do
+  # Output header for our custom matcher on Github workflow
+  echo "PYLINT:${file}"
+  if ! pylint --rcfile=.pylintrc --score=n "$file"
+  then
+    SUCCESS=1
+  fi
+done
+
+exit $SUCCESS