├── .gitmodules ├── digest.py ├── .cirrus.yml ├── config └── pkcs1verify_config.h ├── README.md ├── .github └── workflows │ └── tagtar.yml └── src └── pkcs1verify.zig /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "mbedtls"] 2 | path = mbedtls 3 | url = https://github.com/Mbed-TLS/mbedtls 4 | [submodule "embedtls"] 5 | branch = v3.4.0 6 | -------------------------------------------------------------------------------- /digest.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import os 3 | import sys 4 | import multihash 5 | import multihash.funcs 6 | import multihash.codecs 7 | import multihash.multihash 8 | from multihash import Multihash 9 | 10 | def main(): 11 | tarball = os.environ["ARCHIVE_PATH"] 12 | sha2 = hashlib.sha256() 13 | with open(tarball, "rb") as f: 14 | for chunk in iter(lambda: f.read(4096), b""): 15 | sha2.update(chunk) 16 | 17 | mh = Multihash.from_hash(sha2) 18 | hex_encoded = mh.encode('hex') 19 | print(f"{hex_encoded}") 20 | sys.exit(0) 21 | 22 | 23 | if __name__ == "__main__": 24 | main() 25 | -------------------------------------------------------------------------------- /.cirrus.yml: -------------------------------------------------------------------------------- 1 | freebsd_instance: 2 | image_family: freebsd-14-0-snap 3 | 4 | prereq_task: 5 | name: Setup zig-nightly on FreeBSD 14 6 | install_deps_script: pkg install -y llvm17 cmake git 7 | setup_zig_script: 8 | - curl -L -o zig-012.txz https://ziglang.org/builds/zig-0.12.0-dev.2063+804cee3b9.tar.xz 9 | - tar xzf zig-012.txz 10 | - cd "zig-0.12.0-dev.2063+804cee3b9" 11 | - mkdir build && cd build && cmake -DCMAKE_PREFIX_PATH=/usr/local .. && make install 12 | - tar -cjf ../../nightly.tar.bz2 stage3/lib stage3/bin 13 | - mv stage3/lib/zig /usr/local/lib/ 14 | - mv stage3/bin/zig /usr/local/bin/ 15 | 16 | stage3_nightly_artifacts: 17 | path: nightly.tar.bz2 18 | 19 | ##library_task: 20 | ## name: Config MbedTLS by zig-nightly 21 | ## depends_on: 22 | ## - prereq 23 | recurse_submodules_script: 24 | - git submodule init 25 | - git submodule update 26 | static_clib_script: 27 | - zig build 28 | ##- zig build test 29 | config_mbedtls_artifacts: 30 | path: "zig-out/lib/*" 31 | 32 | 33 | -------------------------------------------------------------------------------- /config/pkcs1verify_config.h: -------------------------------------------------------------------------------- 1 | #define MBEDTLS_ENTROPY_C 2 | #define MBEDTLS_HMAC_DRBG_C 3 | #define MBEDTLS_MD_C 4 | #define MBEDTLS_SHA512_C 5 | 6 | /** 7 | * \file mbedtls_config.h 8 | * 9 | * \brief Configuration options (set of defines) 10 | * 11 | * This set of compile-time options may be used to enable 12 | * or disable features selectively, and reduce the global 13 | * memory footprint. 14 | */ 15 | /* 16 | * Copyright The Mbed TLS Contributors 17 | * SPDX-License-Identifier: Apache-2.0 18 | * 19 | * Licensed under the Apache License, Version 2.0 (the "License"); you may 20 | * not use this file except in compliance with the License. 21 | * You may obtain a copy of the License at 22 | * 23 | * http://www.apache.org/licenses/LICENSE-2.0 24 | * 25 | * Unless required by applicable law or agreed to in writing, software 26 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 27 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 28 | * See the License for the specific language governing permissions and 29 | * limitations under the License. 30 | */ 31 | 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pkcs1-verify 2 | Learn build configuration in MbedTLS to enable single call: `mbedtls_rsa_pkcs1_verify()` 3 | 4 | 5 | The #defines for `mbedtls_config.h` required: 6 | ```c 7 | MBEDTLS_ENTROPY_C 8 | MBEDTLS_HMAC_DRBG_C 9 | MBEDTLS_MD_C 10 | MBEDTLS_SHA512_C 11 | MBEDTLS_SHA256_C 12 | MBEDTLS_RSA_C 13 | MBEDTLS_PKCS1_V15 14 | MBEDTLS_BIGNUM_C 15 | MBEDTLS_OID_C 16 | MBEDTLS_ERROR_C 17 | MBEDTLS_PLATFORM_C 18 | ``` 19 | 20 | ## Quickstart 21 | Run verify on the test signature (bottom of `src/pkcs1verify.zig`): 22 | ```sh 23 | git clone --recurse-submodules https://github.com/patterns/pkcs1-verify 24 | cd pkcs1-verify 25 | zig build test 26 | ``` 27 | 28 | 29 | ## Lessons 30 | - Zig dependency currently lacks root CA certs resolution on FreeBSD. Use a CI pipeline such as GitHub workflow inside a Ubuntu image. 31 | - Zig dependency hash currently uses the `sha2-256` multihash. Using a Python script, but also equivalent to combining `sha256sum` CLI with the example Go encode logic. 32 | - Git submodules are not included in the commit-auto tar archives created by GitHub. In order to create a Zig dependency URL, you must generate your own archive file set. 33 | - Zig dependency rejects `.tar.gz` archive. Compared tarballs released on GitHub and GitLab, both are rejected by zig-build. 34 | - Cirrus CI pipeline task names are hidden when the `only_if: $CIRRUS_BRANCH="main"` rule is applied which prevents dependent tasks during pull requests. 35 | - GitLab CI pipeline uses `$CI_JOB_TOKEN` for the generic package registry; personal access token and project access token are not for the CI x generic package case. 36 | 37 | At this time, I think it's necessary to avoid using Git submodules in favor of including the submodule source files directly, and then letting GitHub generate the automatic tar archives. Testing this as next approach for depedency in pink-elephants. 38 | 39 | ## Credits 40 | 41 | Wrap C lib 42 | by [Nathan Michaels](https://nmichaels.org/zig/wrap-sodium.html) 43 | 44 | Minimal configuration 45 | by [MbedTLS](https://mbed-tls.readthedocs.io/en/latest/kb/how-to/using-loose-modules-without-the-full-library/) 46 | 47 | Example verify 48 | by [MbedTLS](https://github.com/Mbed-TLS/mbedtls/blob/development/programs/pkey/rsa_verify.c) ([LICENSE](https://github.com/Mbed-TLS/mbedtls/blob/development/LICENSE)) 49 | 50 | Python workflow 51 | by [Jacob Tomlinson](https://jacobtomlinson.dev/posts/2019/creating-github-actions-in-python/) 52 | 53 | MD5 in [Python](https://stackoverflow.com/a/3431838) 54 | 55 | -------------------------------------------------------------------------------- /.github/workflows/tagtar.yml: -------------------------------------------------------------------------------- 1 | name: tagtar 2 | 3 | env: 4 | LIB_VERSION: https://github.com/Mbed-TLS/mbedtls/archive/refs/tags/v3.4.0.tar.gz 5 | 6 | on: 7 | push: 8 | tags: 9 | - 'v*' 10 | permissions: 11 | contents: write 12 | 13 | jobs: 14 | tag-tar: 15 | name: Tag archives fileset for linkable 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - uses: actions/checkout@v3 20 | 21 | 22 | - name: Fetch MbedTLS v3.4.0 23 | run: | 24 | curl ${{ env.LIB_VERSION }} -L -o lib.tgz 25 | echo "1b899f355022e8d02c4d313196a0a16af86c5a692456fa99d302915b8cf0320a lib.tgz" | sha256sum --check 26 | mkdir -p dist/mbedtls/library 27 | tar -xzf lib.tgz 28 | mv mbedtls-3.4.0/*.md ./dist/mbedtls/ 29 | mv mbedtls-3.4.0/LICENSE ./dist/mbedtls/ 30 | mv mbedtls-3.4.0/include ./dist/mbedtls/ 31 | mv mbedtls-3.4.0/library/entropy.c ./dist/mbedtls/library/ 32 | mv mbedtls-3.4.0/library/hmac_drbg.c ./dist/mbedtls/library/ 33 | mv mbedtls-3.4.0/library/md.c ./dist/mbedtls/library/ 34 | mv mbedtls-3.4.0/library/sha512.c ./dist/mbedtls/library/ 35 | mv mbedtls-3.4.0/library/sha256.c ./dist/mbedtls/library/ 36 | mv mbedtls-3.4.0/library/rsa.c ./dist/mbedtls/library/ 37 | mv mbedtls-3.4.0/library/rsa_alt_helpers.c ./dist/mbedtls/library/ 38 | mv mbedtls-3.4.0/library/bignum.c ./dist/mbedtls/library/ 39 | mv mbedtls-3.4.0/library/bignum_core.c ./dist/mbedtls/library/ 40 | mv mbedtls-3.4.0/library/bignum_mod.c ./dist/mbedtls/library/ 41 | mv mbedtls-3.4.0/library/bignum_mod_raw.c ./dist/mbedtls/library/ 42 | mv mbedtls-3.4.0/library/oid.c ./dist/mbedtls/library/ 43 | mv mbedtls-3.4.0/library/constant_time.c ./dist/mbedtls/library/ 44 | mv mbedtls-3.4.0/library/platform_util.c ./dist/mbedtls/library/ 45 | mv mbedtls-3.4.0/library/error.c ./dist/mbedtls/library/ 46 | mv mbedtls-3.4.0/library/hash_info.c ./dist/mbedtls/library/ 47 | mv mbedtls-3.4.0/library/platform.c ./dist/mbedtls/library/ 48 | # Bundle fileset into archive 49 | cp build.* ./dist/ 50 | cp -R src ./dist/ 51 | cp -R config ./dist/ 52 | rm lib.tgz 53 | tar -czf ${{github.sha}}.tar.gz ./dist/* 54 | 55 | - name: Setup Python 56 | uses: actions/setup-python@v4 57 | with: 58 | python-version: "3.9" 59 | - name: Install dependencies 60 | run: | 61 | python -m pip install --upgrade pip 62 | pip install pymultihash 63 | - name: Print archive digest 64 | id: archive-digest 65 | run: | 66 | output=$(python digest.py) 67 | echo "SHA256_DIGEST=${output}" >> "$GITHUB_OUTPUT" 68 | env: 69 | ARCHIVE_PATH: "${{github.sha}}.tar.gz" 70 | - name: Summarize archive digest 71 | run: | 72 | echo ":paperclip: **sha2-256** ``${{ steps.archive-digest.outputs.SHA256_DIGEST }}``" >> "$GITHUB_STEP_SUMMARY" 73 | 74 | - name: Make tag into release 75 | run: | 76 | gh release create ${{github.ref_name}} ${{github.sha}}.tar.gz --notes "SHA256 ${{steps.archive-digest.outputs.SHA256_DIGEST}}" 77 | env: 78 | GITHUB_TOKEN: ${{ github.TOKEN }} 79 | shell: bash 80 | 81 | -------------------------------------------------------------------------------- /src/pkcs1verify.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const c = @cImport({ 3 | 4 | @cInclude("mbedtls/bignum.h"); 5 | @cInclude("mbedtls/md.h"); 6 | @cInclude("mbedtls/rsa.h"); 7 | }); 8 | 9 | // Call into MbedTLS Crypto (C) library 10 | pub fn pkcs1Verify( 11 | hashed: [*]u8, 12 | sig: [*]const u8, 13 | N: [:0]const u8, 14 | E: [:0]const u8, 15 | ) !void { 16 | //hashed is the sha256 sum of message (32 bytes assumed here) 17 | //sig is the signature supplied to us in the header 18 | //todo N,E sanity checks 19 | //todo better errors for debug 20 | 21 | var rsa_context: c.mbedtls_rsa_context = undefined; 22 | var mpi_n: c.mbedtls_mpi = undefined; 23 | var mpi_e: c.mbedtls_mpi = undefined; 24 | c.mbedtls_rsa_init(&rsa_context); 25 | c.mbedtls_mpi_init(&mpi_n); 26 | c.mbedtls_mpi_init(&mpi_e); 27 | defer c.mbedtls_rsa_free(&rsa_context); 28 | defer c.mbedtls_mpi_free(&mpi_n); 29 | defer c.mbedtls_mpi_free(&mpi_e); 30 | 31 | // read hexadecimal form of N and E 32 | const read_n = c.mbedtls_mpi_read_string(&mpi_n, 16, N); 33 | const read_e = c.mbedtls_mpi_read_string(&mpi_e, 16, E); 34 | if (read_n != 0 or read_e != 0) return error.PublicKeyRead; 35 | 36 | // assign N and E public key fields 37 | _ = c.mbedtls_rsa_import(&rsa_context, &mpi_n, null, null, null, &mpi_e); 38 | const ready = c.mbedtls_rsa_complete(&rsa_context); 39 | if (ready != 0) return error.PublicKeyInit; 40 | 41 | // invoke MbedTLS (C) lib's verify 42 | const ret = c.mbedtls_rsa_pkcs1_verify( 43 | &rsa_context, 44 | c.MBEDTLS_MD_SHA256, 45 | 32, 46 | hashed, 47 | sig, 48 | ); 49 | 50 | if (ret != 0) return error.VerifyBad; 51 | } 52 | 53 | const cert = std.crypto.Certificate; 54 | test "mbedcrypto pkcs1 verify" { 55 | var hashed_msg: [32]u8 = undefined; 56 | const sha = cert.Algorithm.sha256WithRSAEncryption.Hash(); 57 | sha.hash(base_peop_TXT, &hashed_msg, .{}); 58 | // coerce to many-pointer (for C interop) 59 | const c_hashed: [*]u8 = &hashed_msg; 60 | 61 | var b64 = std.base64.standard.Decoder; 62 | var decoded: [256]u8 = undefined; 63 | try b64.decode(&decoded, signature_peop); 64 | // coerce to many-pointer (for C interop) 65 | const c_decoded: [*]u8 = &decoded; 66 | 67 | try pkcs1Verify(c_hashed, c_decoded, modulus_peop, "010001"); 68 | } 69 | 70 | 71 | const base_peop_TXT = 72 | \\(request-target): post /users/oatmeal/inbox 73 | \\host: mastodon.social 74 | \\date: Sun, 30 Apr 2023 04:55:37 GMT 75 | \\digest: SHA-256=a9IYUmhfuVYZQnUuiqFWHhLnxk67FUjWF4W7vewjGKA= 76 | ; 77 | const sum256_peop = "9F17D1A9F11C6AFD8AC047A4929E4A6D61CA9E9773E4A9A0FA4B6F33C6FED548"; 78 | 79 | const modulus_peop = "B2EE5CE4E8E52D18EF25F1712C6229601DCEE9E076408BC21A51F0303CB0CFD91B063412FC3E75836192499DE3BC7CC1E4C4CDFA2A7ED8C08BB77D6D54A5FD0F478C3CD0471564BE6D4BD0EE3783EE40EF7D378BABB5A9778CB8B36464D2851798017AEBAF1EACE47A78B618ECF4F1774DB701272F693AD3D938E8D5CC90EB66D6A247E5612CEDCC49EB3EE9F482377EF87B295187463182B48F7FA17863EC6543743A7F193C5248FF8011EE53AC558FBA6024951FBDA236D0504313C29DDAD6D890299D6383BE23DCF209AC8B8B9DDAF916244A3868DEFB4B4498F8ED40E7506AAE76ACFB3427CD3EE718406E34F0B9EBF5AEB1D39BAC67FCF2BB368A4CD47D"; 80 | 81 | const signature_peop = "ZooM2n+l3bYVe0lCU0V9kfBz6kLZ+LjjLPeiAoPbYT2FUQflA2ke7tZVmNGzbMKu+ILNrO9JpGlI+ai9fLKvDXbuPjurlZ6Sq9O8xgXJfuLjYY8n7qEil90dhhFa99cTDNR3RV3wk/i5cVLozoNJTJzQnGcCI5Z8MtMy7hi/W/1AR42CwCiP3CalnB0dS8S4cYdKUQnVPYX6cuCkQH7UdzcEUVQovZGZtRZ9dv3uBXlCKY+3k//haezLKtdyVYfkrGDngtS6MBz4Lp0M4LCa5XSwyUcVZ94+hx2ghoXaCiBjWtow02mrAqH9Ud8i/gnyQ9Bl18AmvmMcStcSBHrSQg=="; 82 | --------------------------------------------------------------------------------