├── .clang-format ├── .editorconfig ├── .github └── workflows │ ├── ci.yaml │ └── golangci-lint.yml ├── .gitignore ├── .golangci.yml ├── .goreleaser.yaml ├── LICENSE ├── LICENSES ├── BSD-2-Clause.txt └── GPL-2.0-only.txt ├── Makefile ├── README.md ├── RELEASE.md ├── REUSE.toml ├── certs ├── client.crt ├── client.csr ├── client.key ├── server.crt ├── server.csr ├── server.key ├── tillitis.crl ├── tillitis.crt └── tillitis.key ├── cmd └── tkey-verification │ ├── api.go │ ├── appbins.go │ ├── bins │ ├── .gitkeep │ ├── README.md │ ├── signer-v1.0.1.bin │ ├── signer-v1.0.1.bin.sha512 │ ├── verisigner-v0.0.3.bin │ ├── verisigner-v0.0.3.bin.deps │ └── verisigner-v0.0.3.bin.sha512 │ ├── cert.go │ ├── common.go │ ├── config.go │ ├── error.go │ ├── firmwares.go │ ├── main.go │ ├── remotesign.go │ ├── servesigner.go │ ├── showpubkey.go │ ├── util.go │ ├── vendor-signing-pubkeys.txt │ ├── vendorpubkeys.go │ └── verify.go ├── doc ├── implementation-notes.md ├── tkey-verification.1 └── tkey-verification.scd ├── go.mod ├── go.sum ├── gon.hcl ├── internal └── tkey │ ├── errors.go │ ├── tkey.go │ └── udi.go ├── release-builds ├── tkey-verification_0.0.2_linux-amd64.sha512 ├── tkey-verification_0.0.2_macos-amd64.sha512 ├── tkey-verification_0.0.2_macos-arm64.sha512 ├── tkey-verification_0.0.2_macos-universal.sha512 ├── tkey-verification_0.0.2_windows-amd64.exe.sha512 ├── tkey-verification_0.0.3_linux-amd64.sha512 ├── tkey-verification_0.0.3_macos-amd64.sha512 ├── tkey-verification_0.0.3_macos-arm64.sha512 ├── tkey-verification_0.0.3_macos-universal.sha512 └── tkey-verification_0.0.3_windows-amd64.exe.sha512 ├── test-vendor-signing-pubkeys.txt ├── tkey-verification.yaml.example-remote-sign ├── tkey-verification.yaml.example-serve-signer └── tools └── spdx-ensure /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | UseTab: Always 3 | IndentWidth: 8 4 | TabWidth: 8 5 | 6 | IncludeBlocks: Preserve 7 | 8 | BreakBeforeBraces: Custom 9 | BraceWrapping: 10 | AfterFunction: true 11 | 12 | AllowShortFunctionsOnASingleLine: false 13 | AllowShortIfStatementsOnASingleLine: Never 14 | AllowShortEnumsOnASingleLine: false 15 | 16 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{md}] 2 | max_line_length = 70 3 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | 2 | name: ci 3 | 4 | on: 5 | push: 6 | branches: 7 | - 'main' 8 | pull_request: {} 9 | # allow manual runs: 10 | workflow_dispatch: {} 11 | 12 | jobs: 13 | ci: 14 | runs-on: ubuntu-latest 15 | container: 16 | image: ghcr.io/tillitis/tkey-builder:4 17 | steps: 18 | - name: checkout 19 | uses: actions/checkout@v4 20 | with: 21 | # fetch-depth: 0 22 | persist-credentials: false 23 | 24 | - name: fix 25 | # https://github.com/actions/runner-images/issues/6775 26 | run: | 27 | git config --global --add safe.directory "$GITHUB_WORKSPACE" 28 | 29 | - name: check for SPDX tags 30 | run: ./tools/spdx-ensure 31 | 32 | - name: make 33 | run: make 34 | 35 | reuse-compliance-check: 36 | runs-on: ubuntu-latest 37 | steps: 38 | - name: Checkout 39 | uses: actions/checkout@v4 40 | 41 | - name: REUSE Compliance Check 42 | uses: fsfe/reuse-action@v4 43 | with: 44 | args: lint 45 | -------------------------------------------------------------------------------- /.github/workflows/golangci-lint.yml: -------------------------------------------------------------------------------- 1 | name: golangci-lint 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | 8 | permissions: 9 | contents: read 10 | # Optional: allow read access to pull request. Use with `only-new-issues` option. 11 | # pull-requests: read 12 | 13 | jobs: 14 | golangci: 15 | name: lint 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v4 19 | - uses: actions/setup-go@v4 20 | with: 21 | go-version: '1.21' 22 | cache: false 23 | - name: golangci-lint 24 | uses: golangci/golangci-lint-action@v3 25 | with: 26 | # Require: The version of golangci-lint to use. 27 | # When `install-mode` is `binary` (default) the value can be v1.2 or v1.2.3 or `latest` to use the latest version. 28 | # When `install-mode` is `goinstall` the value can be v1.2.3, `latest`, or the hash of a commit. 29 | version: v1.55.2 30 | 31 | # Optional: working directory, useful for monorepos 32 | # working-directory: somedir 33 | 34 | # Optional: golangci-lint command line arguments. 35 | # 36 | # Note: By default, the `.golangci.yml` file should be at the root of the repository. 37 | # The location of the configuration file can be changed by using `--config=` 38 | # args: --timeout=30m --config=/my/path/.golangci.yml --issues-exit-code=0 39 | 40 | # Optional: show only new issues if it's a pull request. The default value is `false`. 41 | # only-new-issues: true 42 | 43 | # Optional: if set to true, then all caching functionality will be completely disabled, 44 | # takes precedence over all other caching options. 45 | # skip-cache: true 46 | 47 | # Optional: if set to true, then the action won't cache or restore ~/go/pkg. 48 | # skip-pkg-cache: true 49 | 50 | # Optional: if set to true, then the action won't cache or restore ~/.cache/go-build. 51 | # skip-build-cache: true 52 | 53 | # Optional: The mode to install golangci-lint. It can be 'binary' or 'goinstall'. 54 | # install-mode: "goinstall" 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /dist 2 | /show-pubkey 3 | /tkey-verification 4 | /certs 5 | 6 | 7 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | linters: 2 | presets: 3 | # found in: golangci-lint help linters 4 | - bugs 5 | - comment 6 | - complexity 7 | - error 8 | - format 9 | - import 10 | - metalinter 11 | - module 12 | - performance 13 | - sql 14 | # - style # turned off, can be too much 15 | - test 16 | - unused 17 | disable: 18 | - cyclop 19 | - funlen 20 | - gci 21 | - gocognit 22 | - gofumpt 23 | - nestif 24 | - exhaustruct # TODO? annoying for now 25 | - godot 26 | - depguard 27 | - noctx 28 | - unparam 29 | 30 | issues: 31 | max-issues-per-linter: 0 32 | max-same-issues: 0 33 | 34 | linters-settings: 35 | govet: 36 | enable: 37 | - shadow 38 | -------------------------------------------------------------------------------- /.goreleaser.yaml: -------------------------------------------------------------------------------- 1 | # Make sure to check the documentation at https://goreleaser.com 2 | version: 2 # Goreleaser v2 3 | 4 | release: 5 | draft: true 6 | replace_existing_draft: true 7 | 8 | before: 9 | hooks: 10 | # You may remove this if you don't use go modules. 11 | - go mod tidy 12 | # you may remove this if you don't need go generate 13 | - go generate ./... 14 | builds: 15 | - id: linux 16 | main: ./cmd/tkey-verification 17 | binary: tkey-verification 18 | env: 19 | - GOPROXY=https://proxy.golang.org,direct 20 | - GOSUMDB=sum.golang.org 21 | - CGO_ENABLED=0 22 | 23 | goos: 24 | - linux 25 | goarch: 26 | - amd64 27 | - arm64 28 | flags: 29 | - -trimpath 30 | - -buildvcs=false 31 | 32 | # Custom ldflags mostly to avoid setting main.date which for some 33 | # reason is default 34 | ldflags: 35 | -w -X main.version={{ .Version }} -buildid= 36 | 37 | - id: darwin 38 | main: ./cmd/tkey-verification 39 | binary: tkey-verification 40 | env: 41 | - GOPROXY=https://proxy.golang.org,direct 42 | - GOSUMDB=sum.golang.org 43 | - CGO_ENABLED=1 44 | 45 | goos: 46 | - darwin 47 | goarch: 48 | - amd64 49 | - arm64 50 | flags: 51 | - -trimpath 52 | - -buildvcs=false 53 | 54 | # Custom ldflags mostly to avoid setting main.date which for some 55 | # reason is default 56 | ldflags: 57 | -w -X main.version={{ .Version }} -buildid= 58 | 59 | - id: windows 60 | main: ./cmd/tkey-verification 61 | binary: tkey-verification 62 | env: 63 | - GOPROXY=https://proxy.golang.org,direct 64 | - GOSUMDB=sum.golang.org 65 | - CGO_ENABLED=0 66 | 67 | goos: 68 | - windows 69 | goarch: 70 | - amd64 71 | - arm64 72 | flags: 73 | - -trimpath 74 | - -buildvcs=false 75 | 76 | # Custom ldflags mostly to avoid setting main.date which for some 77 | # reason is default 78 | ldflags: 79 | -w -X main.version={{ .Version }} -buildid= 80 | 81 | universal_binaries: 82 | - ids: 83 | - darwin 84 | replace: true 85 | name_template: "tkey-verification" 86 | hooks: 87 | post: gon gon.hcl 88 | 89 | archives: 90 | - format: tar.gz 91 | allow_different_binary_count: true 92 | # this name template makes the OS and Arch compatible with the results of uname. 93 | name_template: >- 94 | {{ .ProjectName }}_ 95 | {{- .Version }}_ 96 | {{- title .Os }}_ 97 | {{- if eq .Arch "all" }}universal 98 | {{- else }}{{ .Arch }}{{ end }} 99 | {{- if .Arm }}v{{ .Arm }}{{ end }} 100 | # use zip for windows archives 101 | format_overrides: 102 | - goos: windows 103 | format: zip 104 | files: 105 | - src: doc/tkey-verification.1 106 | dst: man 107 | strip_parent: true 108 | 109 | nfpms: 110 | - package_name: tkey-verification 111 | vendor: Tillitis AB 112 | homepage: https://tillitis.se/ 113 | maintainer: Tillitis 114 | description: |- 115 | A program to sign or verify the identity of a Tillitis TKey. 116 | license: "GPL-2.0-only" 117 | formats: 118 | - apk 119 | - deb 120 | - rpm 121 | - archlinux # Since: v1.13 122 | bindir: /usr/bin 123 | release: 1 124 | section: misc 125 | contents: 126 | - src: doc/tkey-verification.1 127 | dst: /usr/share/man/man1/tkey-verification.1 128 | file_info: 129 | mode: 0644 130 | deb: 131 | lintian_overrides: 132 | - statically-linked-binary 133 | - changelog-file-missing-in-native-package 134 | 135 | winget: 136 | - name: TKeyVerification 137 | publisher: Tillitis 138 | short_description: "A program to sign or verify the identity of a Tillitis TKey." 139 | license: "GPL-2.0-only" 140 | publisher_url: https://tillitis.se/ 141 | publisher_support_url: "https://github.com/tillitis/tkey-verification/issues/new" 142 | package_identifier: Tillitis.TKeyVerification 143 | homepage: "https://tillitis.se/" 144 | description: "A program to sign or verify the identity of a Tillitis TKey." 145 | license_url: "https://github.com/tillitis/tkey-verification/blob/main/LICENSE" 146 | copyright: "Tillitis AB" 147 | skip_upload: true 148 | release_notes: "{{.Changelog}}" 149 | 150 | repository: 151 | owner: tillitis 152 | name: winget-pkgs 153 | 154 | brews: 155 | - name: tkey-verification 156 | commit_author: 157 | name: goreleaserbot 158 | email: bot@goreleaser.com 159 | commit_msg_template: "Brew formula update for {{ .ProjectName }} version {{ .Tag }}" 160 | directory: Formula 161 | # Caveats for the user of your binary. 162 | #caveats: "How to use this binary" 163 | homepage: "https://tillitis.se/" 164 | description: "A program to sign or verify the identity of a Tillitis TKey." 165 | license: "GPL-2.0-only" 166 | skip_upload: true 167 | install: | 168 | bin.install "tkey-verification" 169 | man1.install "man/tkey-verification.1" 170 | # Repository to push the generated files to. 171 | repository: 172 | owner: tillitis 173 | name: homebrew-tkey 174 | branch: main 175 | pull_request: 176 | enabled: true 177 | draft: true 178 | 179 | checksum: 180 | name_template: 'checksums.txt' 181 | snapshot: 182 | name_template: "{{ incpatch .Version }}-next" 183 | changelog: 184 | sort: 185 | filters: 186 | exclude: 187 | - '^docs:' 188 | - '^test:' 189 | 190 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright 2022 Tillitis AB 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | 1. Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in the 14 | documentation and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /LICENSES/BSD-2-Clause.txt: -------------------------------------------------------------------------------- 1 | Copyright 2022 Tillitis AB 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 15 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 16 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 17 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 18 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 20 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /LICENSES/GPL-2.0-only.txt: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | 3 | Version 2, June 1991 4 | 5 | 6 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 7 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 8 | 9 | Everyone is permitted to copy and distribute verbatim copies 10 | of this license document, but changing it is not allowed. 11 | 12 | 13 | Preamble 14 | 15 | The licenses for most software are designed to take away your freedom to share and 16 | change it. By contrast, the GNU General Public License is intended to guarantee your 17 | freedom to share and change free software--to make sure the software is free for all 18 | its users. This General Public License applies to most of the Free Software 19 | Foundation's software and to any other program whose authors commit to using it. 20 | (Some other Free Software Foundation software is covered by the GNU Lesser General 21 | Public License instead.) You can apply it to your programs, too. 22 | 23 | When we speak of free software, we are referring to freedom, not price. Our General 24 | Public Licenses are designed to make sure that you have the freedom to distribute 25 | copies of free software (and charge for this service if you wish), that you receive 26 | source code or can get it if you want it, that you can change the software or use 27 | pieces of it in new free programs; and that you know you can do these things. 28 | 29 | To protect your rights, we need to make restrictions that forbid anyone to deny you 30 | these rights or to ask you to surrender the rights. These restrictions translate to 31 | certain responsibilities for you if you distribute copies of the software, or if you 32 | modify it. 33 | 34 | For example, if you distribute copies of such a program, whether gratis or for a 35 | fee, you must give the recipients all the rights that you have. You must make sure 36 | that they, too, receive or can get the source code. And you must show them these 37 | terms so they know their rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and (2) offer you 40 | this license which gives you legal permission to copy, distribute and/or modify the 41 | software. 42 | 43 | Also, for each author's protection and ours, we want to make certain that everyone 44 | understands that there is no warranty for this free software. If the software is 45 | modified by someone else and passed on, we want its recipients to know that what 46 | they have is not the original, so that any problems introduced by others will not 47 | reflect on the original authors' reputations. 48 | 49 | Finally, any free program is threatened constantly by software patents. We wish to 50 | avoid the danger that redistributors of a free program will individually obtain 51 | patent licenses, in effect making the program proprietary. To prevent this, we have 52 | made it clear that any patent must be licensed for everyone's free use or not 53 | licensed at all. 54 | 55 | The precise terms and conditions for copying, distribution and modification follow. 56 | 57 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 58 | 59 | 0. This License applies to any program or other work which contains a notice placed 60 | by the copyright holder saying it may be distributed under the terms of this General 61 | Public License. The "Program", below, refers to any such program or work, and a 62 | "work based on the Program" means either the Program or any derivative work under 63 | copyright law: that is to say, a work containing the Program or a portion of it, 64 | either verbatim or with modifications and/or translated into another language. 65 | (Hereinafter, translation is included without limitation in the term 66 | "modification".) Each licensee is addressed as "you". 67 | 68 | Activities other than copying, distribution and modification are not covered by this 69 | License; they are outside its scope. The act of running the Program is not 70 | restricted, and the output from the Program is covered only if its contents 71 | constitute a work based on the Program (independent of having been made by running 72 | the Program). Whether that is true depends on what the Program does. 73 | 74 | 1. You may copy and distribute verbatim copies of the Program's source code as you 75 | receive it, in any medium, provided that you conspicuously and appropriately publish 76 | on each copy an appropriate copyright notice and disclaimer of warranty; keep intact 77 | all the notices that refer to this License and to the absence of any warranty; and 78 | give any other recipients of the Program a copy of this License along with the 79 | Program. 80 | 81 | You may charge a fee for the physical act of transferring a copy, and you may at 82 | your option offer warranty protection in exchange for a fee. 83 | 84 | 2. You may modify your copy or copies of the Program or any portion of it, thus 85 | forming a work based on the Program, and copy and distribute such modifications or 86 | work under the terms of Section 1 above, provided that you also meet all of these 87 | conditions: 88 | 89 | a) You must cause the modified files to carry prominent notices stating that you 90 | changed the files and the date of any change. 91 | b) You must cause any work that you distribute or publish, that in whole or in 92 | part contains or is derived from the Program or any part thereof, to be licensed 93 | as a whole at no charge to all third parties under the terms of this License. 94 | c) If the modified program normally reads commands interactively when run, you 95 | must cause it, when started running for such interactive use in the most 96 | ordinary way, to print or display an announcement including an appropriate 97 | copyright notice and a notice that there is no warranty (or else, saying that 98 | you provide a warranty) and that users may redistribute the program under these 99 | conditions, and telling the user how to view a copy of this License. (Exception: 100 | if the Program itself is interactive but does not normally print such an 101 | announcement, your work based on the Program is not required to print an 102 | announcement.) 103 | 104 | These requirements apply to the modified work as a whole. If identifiable sections 105 | of that work are not derived from the Program, and can be reasonably considered 106 | independent and separate works in themselves, then this License, and its terms, do 107 | not apply to those sections when you distribute them as separate works. But when you 108 | distribute the same sections as part of a whole which is a work based on the 109 | Program, the distribution of the whole must be on the terms of this License, whose 110 | permissions for other licensees extend to the entire whole, and thus to each and 111 | every part regardless of who wrote it. 112 | 113 | Thus, it is not the intent of this section to claim rights or contest your rights to 114 | work written entirely by you; rather, the intent is to exercise the right to control 115 | the distribution of derivative or collective works based on the Program. 116 | 117 | In addition, mere aggregation of another work not based on the Program with the 118 | Program (or with a work based on the Program) on a volume of a storage or 119 | distribution medium does not bring the other work under the scope of this License. 120 | 121 | 3. You may copy and distribute the Program (or a work based on it, under Section 2) 122 | in object code or executable form under the terms of Sections 1 and 2 above provided 123 | that you also do one of the following: 124 | 125 | a) Accompany it with the complete corresponding machine-readable source code, 126 | which must be distributed under the terms of Sections 1 and 2 above on a medium 127 | customarily used for software interchange; or, 128 | b) Accompany it with a written offer, valid for at least three years, to give 129 | any third party, for a charge no more than your cost of physically performing 130 | source distribution, a complete machine-readable copy of the corresponding 131 | source code, to be distributed under the terms of Sections 1 and 2 above on a 132 | medium customarily used for software interchange; or, 133 | c) Accompany it with the information you received as to the offer to distribute 134 | corresponding source code. (This alternative is allowed only for noncommercial 135 | distribution and only if you received the program in object code or executable 136 | form with such an offer, in accord with Subsection b above.) 137 | 138 | The source code for a work means the preferred form of the work for making 139 | modifications to it. For an executable work, complete source code means all the 140 | source code for all modules it contains, plus any associated interface definition 141 | files, plus the scripts used to control compilation and installation of the 142 | executable. However, as a special exception, the source code distributed need not 143 | include anything that is normally distributed (in either source or binary form) with 144 | the major components (compiler, kernel, and so on) of the operating system on which 145 | the executable runs, unless that component itself accompanies the executable. 146 | 147 | If distribution of executable or object code is made by offering access to copy from 148 | a designated place, then offering equivalent access to copy the source code from the 149 | same place counts as distribution of the source code, even though third parties are 150 | not compelled to copy the source along with the object code. 151 | 152 | 4. You may not copy, modify, sublicense, or distribute the Program except as 153 | expressly provided under this License. Any attempt otherwise to copy, modify, 154 | sublicense or distribute the Program is void, and will automatically terminate your 155 | rights under this License. However, parties who have received copies, or rights, 156 | from you under this License will not have their licenses terminated so long as such 157 | parties remain in full compliance. 158 | 159 | 5. You are not required to accept this License, since you have not signed it. 160 | However, nothing else grants you permission to modify or distribute the Program or 161 | its derivative works. These actions are prohibited by law if you do not accept this 162 | License. Therefore, by modifying or distributing the Program (or any work based on 163 | the Program), you indicate your acceptance of this License to do so, and all its 164 | terms and conditions for copying, distributing or modifying the Program or works 165 | based on it. 166 | 167 | 6. Each time you redistribute the Program (or any work based on the Program), the 168 | recipient automatically receives a license from the original licensor to copy, 169 | distribute or modify the Program subject to these terms and conditions. You may not 170 | impose any further restrictions on the recipients' exercise of the rights granted 171 | herein. You are not responsible for enforcing compliance by third parties to this 172 | License. 173 | 174 | 7. If, as a consequence of a court judgment or allegation of patent infringement or 175 | for any other reason (not limited to patent issues), conditions are imposed on you 176 | (whether by court order, agreement or otherwise) that contradict the conditions of 177 | this License, they do not excuse you from the conditions of this License. If you 178 | cannot distribute so as to satisfy simultaneously your obligations under this 179 | License and any other pertinent obligations, then as a consequence you may not 180 | distribute the Program at all. For example, if a patent license would not permit 181 | royalty-free redistribution of the Program by all those who receive copies directly 182 | or indirectly through you, then the only way you could satisfy both it and this 183 | License would be to refrain entirely from distribution of the Program. 184 | 185 | If any portion of this section is held invalid or unenforceable under any particular 186 | circumstance, the balance of the section is intended to apply and the section as a 187 | whole is intended to apply in other circumstances. 188 | 189 | It is not the purpose of this section to induce you to infringe any patents or other 190 | property right claims or to contest validity of any such claims; this section has 191 | the sole purpose of protecting the integrity of the free software distribution 192 | system, which is implemented by public license practices. Many people have made 193 | generous contributions to the wide range of software distributed through that system 194 | in reliance on consistent application of that system; it is up to the author/donor 195 | to decide if he or she is willing to distribute software through any other system 196 | and a licensee cannot impose that choice. 197 | 198 | This section is intended to make thoroughly clear what is believed to be a 199 | consequence of the rest of this License. 200 | 201 | 8. If the distribution and/or use of the Program is restricted in certain countries 202 | either by patents or by copyrighted interfaces, the original copyright holder who 203 | places the Program under this License may add an explicit geographical distribution 204 | limitation excluding those countries, so that distribution is permitted only in or 205 | among countries not thus excluded. In such case, this License incorporates the 206 | limitation as if written in the body of this License. 207 | 208 | 9. The Free Software Foundation may publish revised and/or new versions of the 209 | General Public License from time to time. Such new versions will be similar in 210 | spirit to the present version, but may differ in detail to address new problems or 211 | concerns. 212 | 213 | Each version is given a distinguishing version number. If the Program specifies a 214 | version number of this License which applies to it and "any later version", you have 215 | the option of following the terms and conditions either of that version or of any 216 | later version published by the Free Software Foundation. If the Program does not 217 | specify a version number of this License, you may choose any version ever published 218 | by the Free Software Foundation. 219 | 220 | 10. If you wish to incorporate parts of the Program into other free programs whose 221 | distribution conditions are different, write to the author to ask for permission. 222 | For software which is copyrighted by the Free Software Foundation, write to the Free 223 | Software Foundation; we sometimes make exceptions for this. Our decision will be 224 | guided by the two goals of preserving the free status of all derivatives of our free 225 | software and of promoting the sharing and reuse of software generally. 226 | 227 | NO WARRANTY 228 | 229 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE 230 | PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN 231 | WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" 232 | WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT 233 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 234 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH 235 | YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY 236 | SERVICING, REPAIR OR CORRECTION. 237 | 238 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY 239 | COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM 240 | AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, 241 | INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 242 | PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE 243 | OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE 244 | WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 245 | POSSIBILITY OF SUCH DAMAGES. 246 | 247 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | shasum = sha512sum 2 | CGO = 0 3 | 4 | .PHONY: all 5 | all: tkey-verification 6 | 7 | # APP_VERSION ?= $(shell git describe --dirty --always | sed -n "s/^v\(.*\)/\1/p") 8 | APP_VERSION ?= $(shell git describe --dirty --always | sed -n "s/^v\(.*\)/\1/p") 9 | # .PHONY to let go-build handle deps and rebuilds 10 | .PHONY: tkey-verification 11 | tkey-verification: 12 | CGO_ENABLED=$(CGO) go build -ldflags "-w -X main.version=$(APP_VERSION) -buildid=" -trimpath -buildvcs=false ./cmd/tkey-verification 13 | ./tkey-verification --version 14 | 15 | .PHONY: podman 16 | podman: 17 | podman run --arch=amd64 --rm --mount type=bind,source=$(CURDIR),target=/src -w /src -it ghcr.io/tillitis/tkey-builder:4 make -j 18 | 19 | .PHONY: check-digests 20 | check-digests: 21 | cd cmd/tkey-verification/bins && \ 22 | $(shasum) -c signer-v1.0.1.bin.sha512 && \ 23 | $(shasum) -c verisigner-v0.0.3.bin.sha512 24 | 25 | .PHONY: man 26 | man: doc/tkey-verification.1 27 | 28 | doc/tkey-verification.1: doc/tkey-verification.scd 29 | scdoc < $^ > $@ 30 | 31 | .PHONY: clean 32 | clean: 33 | rm -f tkey-verification 34 | 35 | .PHONY: lint 36 | lint: 37 | golangci-lint run 38 | 39 | .PHONY: certs 40 | certs: 41 | certstrap --depot-path certs init --expires="10 years" --passphrase="" --common-name=tillitis 42 | 43 | # To deploy the tkey-verification server part (that runs the 44 | # serve-signer command) on a different machine, you need to add the 45 | # machine's IP address (or domain) to the following certificate 46 | # request. Adding for example the following at the end of the command 47 | # line: --ip=192.168.122.20 48 | certstrap --depot-path certs request-cert --passphrase="" --common-name=server --domain=localhost 49 | certstrap --depot-path certs sign --CA=tillitis --expires="10 years" server 50 | certstrap --depot-path certs request-cert --passphrase="" --common-name=client 51 | certstrap --depot-path certs sign --CA=tillitis --expires="10 years" client 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tillitis TKey Verification 2 | 3 | `tkey-verification` is a tool used for signing a TKey identity and 4 | verifying that it still has the same identity as it did when it was 5 | provisioned, typically by [Tillitis](https://tillitis.se/) the 6 | original vendor. 7 | 8 | *Note well*: If your TKey has been provisioned by you or someone else, 9 | like your IT department, you will need to run their version of the 10 | `tkey-verification` program instead of this one. 11 | 12 | ## Installation 13 | 14 | [Official 15 | instructions](https://tillitis.se/app/tkey-device-verification/) on 16 | Tillitis' web. 17 | 18 | You can download a release of the tool at: 19 | 20 | https://github.com/tillitis/tkey-verification/releases 21 | 22 | Or do: 23 | 24 | ``` 25 | $ go install github.com/tillitis/tkey-verification/cmd/tkey-verification@latest 26 | ``` 27 | 28 | if you have a Go compiler. Please note that if you install like this 29 | you won't get the tag in `--version`. 30 | 31 | ## Terminology 32 | 33 | - "device under verification": The device the vendor is provisioning 34 | or the user is verifying. 35 | - "device signature": A signature made on the device under 36 | verification with the signer device app. 37 | - Unique Device Identifier (UDI): A unique identifier present in all 38 | TKeys. The 1st half identifies the revision of the hardware, the 2nd 39 | half is a serial number. 40 | - "signing server": An HSM-like machine providing signatures over 41 | messages and producing files to be uploaded to some database. 42 | - "signer": A device app used for confirming the TKey's identity, 43 | right now either verisigner (source in older versions in this 44 | repository, look for verisigner tags) or 45 | [tkey-device-signer](https://github.com/tillitis/tkey-device-signer). 46 | - "signer public key": The public key of signer running on the device 47 | under verification. 48 | - "vendor signature": A signature made by the signing server. 49 | 50 | ## Security Protocol 51 | 52 | ### During provisioning 53 | 54 | 1. Retrieve the UDI from the device under verification. 55 | 2. Run the signer with a specific tag on the device under 56 | verification. This creates a unique key pair. 57 | 3. Retrieve signer public key from the device under verification. 58 | 4. Ask signer to sign a random challenge. 59 | 5. Verify the signature of the random challenge with the retrieved 60 | public key to let the device prove that it has the corresponding 61 | private key. 62 | 6. Ask signer for a digest of the firmware binary (in ROM). Consult 63 | the internal firmware database to verify that the TKey, according 64 | to its hardware revision, is running the expected firmware. 65 | 7. Sign a message consisting of the UDI, firmware digest, and signer 66 | public key, with a vendor signature. 67 | 8. Publish the [Verification file](#verification-file), which includes 68 | the UDI, the tag and digest of the signer program used, the vendor 69 | signature, and the timestamp when signature was made. 70 | 71 | ### Verifying 72 | 73 | 1. Retrieve the UDI from the device under verification. 74 | 2. Get the [Verification file](#verification-file) with the vendor 75 | signature, signer tag and digest for this UDI. 76 | 3. Run the signer with the same tag and digest on the device under 77 | verification. 78 | 4. Retrieve the signer public key. 79 | 5. Ask signer to sign a random challenge. 80 | 6. Verify the signature of the random challenge with the signer public 81 | key thus proving that the device under verification has access to the 82 | corresponding private key. 83 | 7. Ask signer for a digest of the firmware binary (in ROM). Consult 84 | the internal firmware database to verify that the TKey, according 85 | to its hardware revision, is running the expected firmware. 86 | 8. Recreate the message of UDI, firmware digest and signer public key. 87 | Verify the vendor signature of the message, thus proving that the 88 | UDI, the firmware, and this private/public key pair was the same 89 | during vendor signing. 90 | 91 | Note that the exact same signer binary that was used for producing the 92 | signer signature during provisioning *must* be used when verifying it. 93 | If a different signer is used then the device private/public key will 94 | not match, even if the TKey is the same. A verifier must check the 95 | "hash" field and complain if it does not have a signer binary with the 96 | same digest. 97 | 98 | ## Building tkey-verification 99 | 100 | Build the `tkey-verification` tool with the test file containing 101 | public key(s) for vendor signing/verify. 102 | 103 | ``` 104 | $ cp test-vendor-signing-pubkeys.txt cmd/tkey-verification/vendor-signing-pubkeys.txt 105 | $ make 106 | ``` 107 | 108 | See below if you need to get hold of a different public key. 109 | 110 | The device apps used for signing is included in binary form under 111 | `cmd/tkey-verification/bins/`. See more info under [Building included 112 | device apps](#building-included-device-apps) if you want to build them 113 | yourself. 114 | 115 | ## Maintenance 116 | 117 | See [Implementation notes](doc/implementation-notes.md) for more 118 | in-depth notes on the program. 119 | 120 | - `tkey-verification` contains an in-code database mapping known 121 | hardware revisions (first half of the Unique Device Identifier) to 122 | their expected firmware size and hash. This needs to be maintained 123 | in 124 | [cmd/tkey-verification/firmwares.go](cmd/tkey-verification/firmwares.go) 125 | in `NewFirmwares()`. 126 | 127 | ## Building included device apps 128 | 129 | The device apps used for signing is included in binary form under 130 | `cmd/tkey-verification/bins/`. 131 | 132 | Reproducible versions of the device app `verisigner` binary included 133 | in this repo can be built from earlier verisigner tags. Checkout the 134 | wanted tag and follow the instructions there. 135 | 136 | The [signer](https://github.com/tillitis/tkey-device-signer) binary 137 | can be built reproducible from the tags mentioned in the `bins` 138 | directory. Please note that you have to build without touch 139 | requirement. 140 | 141 | ## Certificates 142 | 143 | For your convenience we include a test CA and some test certificates 144 | in `certs`. They expire 10 years after being generated. 145 | 146 | If you need to rebuild CA, server, and client certs you can use any 147 | ordinary X.509 certificate tools like GnuTLS's certtool or OpenSSL to 148 | generate your certificates. 149 | 150 | You can also install the small [certstrap 151 | tool](https://github.com/square/certstrap) and run: 152 | 153 | ``` 154 | $ make certs 155 | ``` 156 | 157 | ## Testing 158 | 159 | - You need 1 TKey and 1 QEMU machine running to try this out (or 2 160 | TKeys, or 2 QEMU machines, if you manage to get that working). One 161 | is Tillitis' (vendor's) signing TKey, and the other is a TKey that 162 | you want to sign and then verify as genuine. You need to know the 163 | serial port device paths for these. 164 | 165 | - Run the signing server on QEMU (see [the Tillitis Developer 166 | Handbook](https://dev.tillitis.se/tools/#qemu-emulator) for more 167 | information on how to run QEMU). Notice the port QEMU provides when 168 | starting. 169 | 170 | ``` 171 | ./tkey-verification serve-signer --config tkey-verification.yaml.example-serve-signer --port /dev/pts/12 172 | ``` 173 | 174 | - Insert the device under verification, the TKey to be signed and verified. 175 | 176 | - Get the signing server to sign for a device under verification (here 177 | a hardware TKey) 178 | 179 | ``` 180 | $ ./tkey-verification remote-sign --config tkey-verification.yaml.example-remote-sign 181 | Auto-detected serial port /dev/ttyACM0 182 | Connecting to device on serial port /dev/ttyACM0 ... 183 | Firmware name0:'tk1 ' name1:'mkdf' version:4 184 | Loading verisigner-app built from tag:verisigner-v0.0.1 hash:9598910ec9ebe2504a5f894de6f8e067… ... 185 | App loaded. 186 | App name0:'veri' name1:'sign' version:1 187 | TKey UDI: 0x0001020304050607(BE) VendorID: 0x0010 ProductID: 8 ProductRev: 3 188 | TKey firmware with size:3204 and verified hash:31accb1c40febc2bf02f48656a943336… 189 | Remote Sign was successful 190 | ``` 191 | 192 | - The signing server should now have signed and saved a verification 193 | file under `signatures` with a filename generated from the Unique 194 | Device Identifier, typically something like `0133704100000015` if 195 | from Tillitis, but `0001020304050607` if the bitstream has been 196 | built directly from 197 | [tillitis-key1](https://github.com/tillitis/tillitis-key1). 198 | 199 | - Before trying to verify you need to remove and re-insert the device 200 | under verification to get it back to firmware mode. 201 | `tkey-verification` always requires to load the signer itself. Then 202 | try to verify against local files in a directory using `verify -d 203 | signatures` (the default is to query a web server): 204 | 205 | ``` 206 | $ ./tkey-verification verify -d signatures 207 | TKey UDI: 0x0001020304050607(BE) VendorID: 0x0010 ProductID: 8 ProductRev: 3 208 | Reading verification data from file signatures/0001020304050607 ... 209 | TKey is genuine! 210 | ``` 211 | 212 | For the complete set of commands, see the manual page 213 | [tkey-verification(1)](doc/tkey-verification.1). 214 | 215 | ## Creating the vendor public keys file 216 | 217 | The vendor's public key is built into the tkey-verification binary 218 | from a text file. 219 | 220 | For each public key, the tag and hash digest of the device app used 221 | when extracting the public key is also provided. The signing server 222 | needs this so that its TKey can have the correct private key when 223 | signing. Note that these tags per public key are independent from and 224 | can be different from the tag used for device signing. 225 | 226 | A test file is provided in `test-vendor-signing-pubkeys.txt`. It 227 | contains the default public key of our QEMU machine, which is 228 | generated when running verisigner v0.0.3. 229 | 230 | If you want to use some other key(s) this is how: 231 | 232 | If you're just testing start a QEMU as a signing endpoint. See above. 233 | 234 | Get the public key from the TKey in the signing server. We provide a 235 | command in `tkey-verification`, `show-pubkey`, for that. The path to 236 | the app binary to use must be given as an argument. 237 | 238 | Example: 239 | 240 | ``` 241 | ./tkey-verification show-pubkey --port /dev/pts/10 --app cmd/tkey-verification/bins/signer-v1.0.1.bin 242 | Public Key, app tag, and app hash for vendor-signing-pubkeys.txt follows on stdout: 243 | 03a7bd3be67cb466869904ec14b9974ebcc6e593abdc4151315ace2511b9c94d signer-v1.0.1 cd3c4f433f84648428113bd0a0cc407b2150e925a51b478006321e5a903c1638ce807138d1cc1f8f03cfb6236a87de0febde3ce0ddf177208e5483d1c169bac4 244 | ``` 245 | 246 | Enter that line into a file, for instance `other-pubkey.txt`. Then build everything with this file: 247 | 248 | ``` 249 | $ cat other-pubkey.txt >> cmd/tkey-verification/vendor-signing-pubkeys.txt 250 | $ make 251 | ``` 252 | 253 | ## Verification file 254 | 255 | The computer running `tkey-verification serve-sign` generates files 256 | in a directory `signatures/` which is named after the Unique Device 257 | Identifier (in hex), for example `signatures/0133704100000015`. This 258 | file is needed in order to be able to verify a TKey. 259 | 260 | The file contains: 261 | 262 | - timestamp: RFC3339 UTC timestamp when the signature was done. 263 | - apptag: The Git tag of the signer app used on the device under 264 | verification, 265 | - apphash: The hash of the signer app binary used on the device under 266 | verification. Stored in hexadecimal. 267 | - signature: Vendor signature of the message (described above). Stored 268 | in hexadecimal. 269 | 270 | Example file content: 271 | 272 | ``` 273 | { 274 | "timestamp": "2023-03-03T09:31:51Z", 275 | "apptag": "verisigner-v0.0.1", 276 | "apphash": "9598910ec9ebe2504a5f894de6f8e0677dc94c156c7bd6f7e805a35354b3c85daa4ca66ab93f4d75221b501def457b4cafc933c6cdcf16d1eb8ccba6cccf6630", 277 | "signature": "db4e7a72b720b33f6d4887df0f9dcdd6988ca8adb6b0042d8e8c92b5be3e4e39d908f166d093f3ab20880102d43a2b0c8e31178ab7cdb59977dcf7204116cc0c" 278 | } 279 | ``` 280 | 281 | ## Releases of tkey-verification and reproducible builds 282 | 283 | `tkey-verification` is released with the help of GoReleaser, see 284 | `.goreleaser.yaml` in the root of the repo. 285 | 286 | Currently this has to be done on a computer running Darwin, at least 287 | for Tillitis' official releases. The reason is that `tkeyclient` needs 288 | CGO enabled for Darwin to enumerate USB-devices, and that the Darwin 289 | binary is signed with Tillitis' Apple Developer Certificate, which 290 | at the moment also needs to be done using Darwin. We are looking into 291 | solutions for both those points. 292 | 293 | You should be able to build a binary that is a exact copy of our 294 | release binaries if you use the same Go compiler, at least for the 295 | statically linked Linux and Windows binaries. 296 | 297 | Please see [the official 298 | releases](https://github.com/tillitis/tkey-verification/releases) for 299 | digests and details about the build environment. 300 | 301 | Note that `tar.gz` and such files are not reproducible since they contain 302 | a timestamp. 303 | 304 | For the universal Darwin file, the signature produced by Tillitis 305 | needs to be removed before it can be compared to one not built and 306 | released by Tillitis. This can be done using ` codesign 307 | --remove-signature path/to/binary`, at least on a Darwin machine. 308 | 309 | The releases are published at 310 | https://github.com/tillitis/tkey-verification/releases with the 311 | binaries and checksum files. 312 | 313 | Release v0.0.2-v0.0.3 were built with `make podman` and the 314 | [release-builds](release-builds) directory contains digests of 315 | released versions. 316 | 317 | ## Licenses and SPDX tags 318 | 319 | Unless otherwise noted, the project sources are copyright Tillitis AB, 320 | licensed under the terms and conditions of the "BSD-2-Clause" license. 321 | See [LICENSE](LICENSE) for the full license text. 322 | 323 | Until Nov 6, 2024, the license was GPL-2.0 Only. 324 | 325 | Please note that this project embeds binaries that are released under 326 | GPL-2.0 Only, see 327 | [cmd/tkey-verification/bins/README.md](cmd/tkey-verification/bins/README.md) 328 | for more details. 329 | 330 | External source code we have imported are isolated in their own 331 | directories. They may be released under other licenses. This is noted 332 | with a similar `LICENSE` file in every directory containing imported 333 | sources. 334 | 335 | The project uses single-line references to Unique License Identifiers 336 | as defined by the Linux Foundation's [SPDX project](https://spdx.org/) 337 | on its own source files, but not necessarily imported files. The line 338 | in each individual source file identifies the license applicable to 339 | that file. 340 | 341 | The current set of valid, predefined SPDX identifiers can be found on 342 | the SPDX License List at: 343 | 344 | https://spdx.org/licenses/ 345 | 346 | We attempt to follow the [REUSE 347 | specification](https://reuse.software/). 348 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | # Release notes 2 | 3 | ## v1.0.0 4 | 5 | This version brings a lot of refactoring, aiming at simplifying 6 | building and program structure. Only the major changes 7 | will be mentioned here, see [complete 8 | changelog](https://github.com/tillitis/tkey-verification/compare/v0.0.3...v1.0.0) 9 | for more details. 10 | 11 | Changes: 12 | - Refactoring, giving a new way of accessing internal assets, making 13 | it more idiomatic 14 | - Signer binaries are now checked in to the repo under 15 | cmd/tkey-verification/bins 16 | - Verisigner-0.0.3 is deprecated, but buildable from older tags 17 | - New signer app included, signer-v1.0.1, built from 18 | tkey-device-signer without touch 19 | - Support multiple vendor signing keys 20 | - Earlier `show-pubkey` program is now a command in tkey-verification 21 | - Use tkeyclient instead of internal pkg 22 | - Use tkeysign instead of internal pkg 23 | - Refine errors to present more sensible errors to users of the verify 24 | command 25 | - Pointing to tillitis.se/verify if an error occur when verifying a 26 | TKey, with explanations to common errors 27 | - Use GoReleaser for release building 28 | - Enable CGO for Darwin, to find the port automatically 29 | - Add `--speed` flag to support multiple baudrates 30 | 31 | ## v0.0.3 32 | 33 | Update to include new TKey product revision from Tillitis which is 34 | based on the FPGA design and bitstream in release: 35 | 36 | https://github.com/tillitis/tillitis-key1/releases/tag/TK1-24.03 37 | 38 | ## v0.0.2 39 | 40 | In this second release we ensure that the published executable 41 | binaries can be reproduced, see 42 | [README.md](https://github.com/tillitis/tkey-verification#readme) in 43 | the repository. 44 | 45 | Verifying your TKey in a few simple steps: 46 | 47 | - download the suitable `tkey-verification` binary for your platform 48 | - rename the file to `tkey-verification` (add `.exe` on Windows, on 49 | other platforms run: `chmod +x ./tkey-verification`) 50 | - plug in your TKey 51 | - In a terminal on Linux, or in PowerShell on Windows, you can run the 52 | verification with: 53 | 54 | ``` 55 | ./tkey-verification verify 56 | ``` 57 | 58 | - On MacOS, automatic detection of the serial port is currently not 59 | available. You have to first list the serial port devices with: 60 | 61 | ``` 62 | ls -l /dev/cu.* 63 | ``` 64 | 65 | The TKey device name looks like “/dev/cu.usbmodemN” where N is a 66 | number. Now you can run the verification like: 67 | 68 | ``` 69 | ./tkey-verification verify --port /dev/cu.usbmodemN 70 | ``` 71 | 72 | The default operation of `tkey-verification` requires Internet 73 | connectivity to download the verification data on the machine where 74 | you plug in your TKey. But it is also possible to run the verification 75 | on a machine that does not have connectivity, by first downloading the 76 | verification data on a machine which does. See `tkey-verification 77 | verify --help` for more information. 78 | 79 | After processing the data and talking to your TKey, expect a final 80 | message saying `TKey is genuine!`. 81 | 82 | 83 | ## v0.0.1 84 | 85 | This is the first release of the tool for Tillitis signing and you 86 | verifying that your TKey is genuine. 87 | 88 | Verifying your TKey in a few simple steps: 89 | - download the suitable `tkey-verification` binary for your platform 90 | - rename the file to `tkey-verification` (add `.exe` on Windows; do `chmod 91 | +x ./tkey-verification` on other platforms) 92 | - plug in your TKey 93 | - execute this command in your terminal: `./tkey-verification verify` 94 | (without ./ on Windows) 95 | 96 | The default operation of `tkey-verification` requires Internet 97 | connectivity to download the verification data on the machine where 98 | you plug in your TKey. But it is also possible to run the verification 99 | on a machine that does not have connectivity, by first downloading the 100 | verification data on machine which does. See `tkey-verification verify 101 | --help` for more information. 102 | 103 | After processing the data and talking to your TKey, expect a final 104 | message saying `TKey is genuine!`. 105 | -------------------------------------------------------------------------------- /REUSE.toml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2024 Tillitis AB 2 | # SPDX-License-Identifier: BSD-2-Clause 3 | version = 1 4 | 5 | [[annotations]] 6 | path = ".github/workflows/*" 7 | SPDX-FileCopyrightText = "2022 Tillitis AB " 8 | SPDX-License-Identifier = "BSD-2-Clause" 9 | 10 | [[annotations]] 11 | path = [ 12 | ".clang-format", 13 | ".editorconfig", 14 | ".gitignore", 15 | ".golangci.yml", 16 | ".goreleaser.yaml", 17 | "Makefile", 18 | "README.md", 19 | "RELEASE.md", 20 | "certs/client.crt", 21 | "certs/client.csr", 22 | "certs/client.key", 23 | "certs/server.crt", 24 | "certs/server.csr", 25 | "certs/server.key", 26 | "certs/tillitis.crl", 27 | "certs/tillitis.crt", 28 | "certs/tillitis.key", 29 | "cmd/tkey-verification/bins/README.md", 30 | "cmd/tkey-verification/bins/signer-v1.0.1.bin", 31 | "cmd/tkey-verification/bins/signer-v1.0.1.bin.sha512", 32 | "cmd/tkey-verification/bins/verisigner-v0.0.3.bin", 33 | "cmd/tkey-verification/bins/verisigner-v0.0.3.bin.deps", 34 | "cmd/tkey-verification/bins/verisigner-v0.0.3.bin.sha512", 35 | "cmd/tkey-verification/vendor-signing-pubkeys.txt", 36 | "doc/implementation-notes.md", 37 | "doc/tkey-verification.1", 38 | "doc/tkey-verification.scd", 39 | "go.mod", 40 | "go.sum", 41 | "release-builds/tkey-verification_0.0.2_linux-amd64.sha512", 42 | "release-builds/tkey-verification_0.0.2_macos-amd64.sha512", 43 | "release-builds/tkey-verification_0.0.2_macos-arm64.sha512", 44 | "release-builds/tkey-verification_0.0.2_macos-universal.sha512", 45 | "release-builds/tkey-verification_0.0.2_windows-amd64.exe.sha512", 46 | "release-builds/tkey-verification_0.0.3_linux-amd64.sha512", 47 | "release-builds/tkey-verification_0.0.3_macos-amd64.sha512", 48 | "release-builds/tkey-verification_0.0.3_macos-arm64.sha512", 49 | "release-builds/tkey-verification_0.0.3_macos-universal.sha512", 50 | "release-builds/tkey-verification_0.0.3_windows-amd64.exe.sha512", 51 | "test-vendor-signing-pubkeys.txt", 52 | "tkey-verification.yaml.example-remote-sign", 53 | "tkey-verification.yaml.example-serve-signer" 54 | ] 55 | SPDX-FileCopyrightText = "2022 Tillitis AB " 56 | SPDX-License-Identifier = "BSD-2-Clause" 57 | 58 | 59 | [[annotations]] 60 | path = [ 61 | "cmd/tkey-verification/bins/signer-v1.0.1.bin", 62 | "cmd/tkey-verification/bins/verisigner-v0.0.3.bin", 63 | ] 64 | SPDX-FileCopyrightText = "2022 Tillitis AB " 65 | SPDX-License-Identifier = "GPL-2.0-only" 66 | -------------------------------------------------------------------------------- /certs/client.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEHzCCAgegAwIBAgIQPfWOuGYux4o4NUbJAtLb7DANBgkqhkiG9w0BAQsFADAT 3 | MREwDwYDVQQDEwh0aWxsaXRpczAeFw0yNDA2MTExNDEyMzJaFw0zNDA2MTExNDIy 4 | MjhaMBExDzANBgNVBAMTBmNsaWVudDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC 5 | AQoCggEBAL80lSHNMZKr3eZJN2LoIy8sbt4npWA/QTtPeAy2vK5/ErAAl2E/Thtq 6 | Ngpdqx7N+ue3+N6/EzoYYz7ziyFNVZ8eZbz62h9+1Os5sk/udk5oqBJmCvJnc47J 7 | pk0Zn/aMAAQbws8Z38pnCxnJQ6EQVvSnQ5/gKP/BBwM8jNtFGHcHxwC/10mwxy2t 8 | Aj8BmmrqSj0X6Kwiil2KNhGkyiOi/iP/FZlKk/jlzbBelnMk4dw1PyjBBvXpRY/f 9 | OoFfiXpdqcyqFBRluVhH5fTXUJbEVV/DLou2N5idOCP6GxCF8bp7bsdIsI280bha 10 | lrvGPqBNU0hvmy69C5Dl8nivSHGYM6kCAwEAAaNxMG8wDgYDVR0PAQH/BAQDAgO4 11 | MB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAdBgNVHQ4EFgQUrNPc13D1 12 | LCI8mITP0HWgcvEUcrgwHwYDVR0jBBgwFoAUawc/QlR5JqKP4FK/jVzHIyzL7YEw 13 | DQYJKoZIhvcNAQELBQADggIBAEw/Hv0waxSlzrH7b+v3sUquCYd9hAyUq5AvQHfB 14 | W0sgV2iQ/HdRbEPSgOQ5j7KfmYUXGbANKXEEmQLtzjdhPBUwFgKxpmmjspLxPrfp 15 | clZOyM1a4rwRA3+SNo87lV5hkJa4UYyxwEuuXddRqaoiSjCdi36xquOSJKSwIhIk 16 | fiWW7WQQ87KF2t1dBw9DPvS/cAwvQvLmipddHKhZdK9aRtCd6GQXa1utp8FftrfV 17 | F7jq+5AA7gdzOCn4D/VTdf8oB8fAVwui/kvItf3s444qMGaazw7rAHQLCaXtBC1J 18 | Kmx+7celXT4dwFDJCJKTAqp0Hd64iyJVdXaIZef384Vct6KmqVNht938BBTKgxOB 19 | V5sN5nLRxjuPfjkBNMQ2eHI4KG7svZhAP/ZIAQ7UMDoEFgIpVptVMvq+p9OxGyLz 20 | t8ifr3PNuianuLP0fo9MAwI9UDoCM6skbG7OVlpE02ku+GqaH4/A3Vr+KBQ1hg4x 21 | fTA/I9n2iGWhKB7u+zf6YZ/hAl8L/DQGl51rohckO5uZGSsfXo4R0DpDwZ26RYBA 22 | PWdoKthC9z/V957kEURGqBeyMaU5AB4T4sYpru8sVMx4KPwYdHQZ4TzbEwZ1xuEc 23 | 6UOEu+xZOgciYa4GYANM/F4AKgrHu6df3al9rRxdeFBKFIL/D9nWJ+GDoaeuoI0C 24 | ZJ9f 25 | -----END CERTIFICATE----- 26 | -------------------------------------------------------------------------------- /certs/client.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIICVjCCAT4CAQAwETEPMA0GA1UEAxMGY2xpZW50MIIBIjANBgkqhkiG9w0BAQEF 3 | AAOCAQ8AMIIBCgKCAQEAvzSVIc0xkqvd5kk3YugjLyxu3ielYD9BO094DLa8rn8S 4 | sACXYT9OG2o2Cl2rHs3657f43r8TOhhjPvOLIU1Vnx5lvPraH37U6zmyT+52Tmio 5 | EmYK8mdzjsmmTRmf9owABBvCzxnfymcLGclDoRBW9KdDn+Ao/8EHAzyM20UYdwfH 6 | AL/XSbDHLa0CPwGaaupKPRforCKKXYo2EaTKI6L+I/8VmUqT+OXNsF6WcyTh3DU/ 7 | KMEG9elFj986gV+Jel2pzKoUFGW5WEfl9NdQlsRVX8Mui7Y3mJ04I/obEIXxuntu 8 | x0iwjbzRuFqWu8Y+oE1TSG+bLr0LkOXyeK9IcZgzqQIDAQABoAAwDQYJKoZIhvcN 9 | AQELBQADggEBABN3sMtRKvFkJXjeHiwgqEnqUEVF+IrjJ8cHxtiRHaHjhSxepTX6 10 | MHWFpGZ6b9ogeUpJZbuZtgm0c0nVgjC/1gSmwKEvlm8/IXp3jNiTvRqp/QxyRDCK 11 | 7EypkTPbd0DXwd9+5RvcFOnCq0LlmZGZ5bYJMZW5Gky619//2jky2hqhmYCT99Sx 12 | bb/4bRmsbanCae0tFGhQxbw3228rtmY58he2FUVIgEIlZxrz9zTTKn/JtmRyp76D 13 | Wg1OvtxBCYEvfh8BVHQv40bittXcQ4OKI81Gi65gVZOmGPFLa3n7zakR0m4oTjAK 14 | eB4KnTdYdRd/SGGVb386TIyYoI1V96DNzWw= 15 | -----END CERTIFICATE REQUEST----- 16 | -------------------------------------------------------------------------------- /certs/client.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEAvzSVIc0xkqvd5kk3YugjLyxu3ielYD9BO094DLa8rn8SsACX 3 | YT9OG2o2Cl2rHs3657f43r8TOhhjPvOLIU1Vnx5lvPraH37U6zmyT+52TmioEmYK 4 | 8mdzjsmmTRmf9owABBvCzxnfymcLGclDoRBW9KdDn+Ao/8EHAzyM20UYdwfHAL/X 5 | SbDHLa0CPwGaaupKPRforCKKXYo2EaTKI6L+I/8VmUqT+OXNsF6WcyTh3DU/KMEG 6 | 9elFj986gV+Jel2pzKoUFGW5WEfl9NdQlsRVX8Mui7Y3mJ04I/obEIXxuntux0iw 7 | jbzRuFqWu8Y+oE1TSG+bLr0LkOXyeK9IcZgzqQIDAQABAoIBAGW+22TjRe/gmPIE 8 | H/73vFFL+aspuazfvByNizpouzq3z//sMKHFCBE6kYsukPKRdWR812fgkaCmfZlw 9 | C6cq+db0W/sWGaAM48xQJXy3TiknM9imiEi5mBpxX5dDfYW6tf95rk56XEXeU1/j 10 | Y6N/iGYj465doDOISe0E+Yds2znq816ge4EGrJo6QUCdOgRkFKmDyRogDAh7OCXX 11 | 9Npqbi5jobIjtm80d2juop3I/g8sXmBhdcgGvYaoaML3M42EGB56Y+ZhjdXoPgXQ 12 | hEtYdPMYg4jcIh9SlA/Fxxdmj7//aJjJENvh3Z3rMEr/oJ6yI+RNhv6RihuxNzRx 13 | DPAcYOkCgYEAzHfQycwCpJ1L29O+O2Hd0BjlCAiBABHvU+UK0YvX1el2G5xgL+vt 14 | 8qOX/GXy6yuRkIOMoRbm9NWWQycTZd7Yn1rQRqujz6pnp4uKrtpIOf/IHPFWn0A4 15 | BnpqGF12VGV0aSXt76pLo+hHXm36gdUKD1AYZFCJKrue0O7zgPHdtMsCgYEA72UR 16 | c0fmnf+wEQlVpRzS6w8oL1UP8YB6cpZspPb+QkC3oipUXy8y2xVsPaxFcnBCGRwJ 17 | jmzMZM01uR9s2HKxeC8mOylwxFkp3ySlIW+yB5ZwJgPJ2h9AAq3hS5WGRq2i0SDK 18 | r/BqSEj0vV/8mZ1r+QhoC13APg0WP/1kU5wkXtsCgYBwKb34JRjCEqOdeH9fdeVH 19 | FZD2IiwZzLzb6cZQgwUojERFCzhcsXC7bPCf/ZEPXrwgaXmImNajMlOWjOYAOQne 20 | COQ+EEPxVEo+R7cRCh8a6extrBpXi9PwAdEXgpW54FnGoI7wrRuHU5FrBKup8yLs 21 | mYR36xIDxh5uANbFQmjblwKBgQC8uu5F9zJd750L4VMO56l0vS9OGX6HQ2XFfsfK 22 | ShrreoALLdn0bI5oX/xEptLdRJknmI4EJcwB0DKBbr/t6IX3HRzD2YjYkv7Kt+Yj 23 | dR5ikvx5S9VPzAE324NZhlkk0XEh8/s3N49iy3l+7vSC2rOr0i/Mfi23/bND4W29 24 | 6MRXrwKBgQCeQdfgNG1alad6NNFXRyN+3z6+PGQKlM+Mj27Dzf15+IAnoQs4EHSL 25 | qXMKllzvnKOHvMto46wBUOBBCPPUL5hcwSrUalfDMH/245E/VQOCC2jxPU+E90xD 26 | dOLOb9GG8sQDclEPpVvw0D1txprC4GKSEEIlg2XRK0jvyQ/vXJ69AQ== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /certs/server.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIENzCCAh+gAwIBAgIQV7dYtpTcq13DoPbACp1pjzANBgkqhkiG9w0BAQsFADAT 3 | MREwDwYDVQQDEwh0aWxsaXRpczAeFw0yNDA2MTExNDEyMzJaFw0zNDA2MTExNDIy 4 | MjhaMBExDzANBgNVBAMTBnNlcnZlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC 5 | AQoCggEBAMOsh3YneAwgQW65pe3uXXKKM2l0UGbV3Bu3eP7RIB7NKTXOL4IK4k1m 6 | 4EywggzL2oku8C+LUvISfZeDOcyAWRVgM4HFEz6qCXVmhQ2C9BnVqQJ0N6gtMBRc 7 | v3Yf57LGT8OhH4GR9NDB6tZpm/hvLOQK94MZpCq2pJux0ETBdGcxnQjbggPG2C/p 8 | /rwvaL9WlBuZYcKthMWWfF/Dn36+DBOXmveUMPvvrhBjO3YUpFsrBnv/aQM3YAmc 9 | PKqRmcW0Hs9C3Pczu4HVFOHTyyKmBPHH+iYsNxDCibjclX9zTzOgSzlgGiXa45xH 10 | eO9MrEvOYFLs/136agzY9/YUKqumRBcCAwEAAaOBiDCBhTAOBgNVHQ8BAf8EBAMC 11 | A7gwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQWBBTQHQZt 12 | CxFC3uL16DwD5wWi/69KWjAfBgNVHSMEGDAWgBRrBz9CVHkmoo/gUr+NXMcjLMvt 13 | gTAUBgNVHREEDTALgglsb2NhbGhvc3QwDQYJKoZIhvcNAQELBQADggIBAFWRxNIp 14 | pYBa6QViLzGiNYTJ757+p20WPLv0RAOnbSEQISsC9HE0WzkldcJ3Mduns1QURcqR 15 | cZ8nIkK6tsqD1T9P5po7RsGolj7SxLpzYuyoKQqL8yM3NYZqdKJ9KOMzSHDDRn/p 16 | I4VvMldfUFXmB4SNShsd1J0mu/pH0nhq/aJv8Ll/ruBmKnz/iZ+cafz+J/RyZx8s 17 | /figE8IVMIcZzmBK0ytUvllPAv/4TB7dlVjgn89mwJ7f7r83rESDqT+CRMlb3hcT 18 | OhiX7tTlHvGzbRjGgIOl4DWBcYnlrevkrTXBSFdc6cCWT4zqXyJFtCOsB83bxbCf 19 | 1VNvBfirKtQaS2fkjd2BkMZ/fbGwkMC0G7+6gYBm+gGiAFcj6IiYJHmjOZV2//CR 20 | svziJhZEvHmvVHwcFOXNfsKuS6cUlhtGP7k5OUP+N/K+WTlvRaU/2Ssq2Q7aALla 21 | MzHePpaU+tihRJ3Js/p6/lU0NpFrKnZD8zXZcr19d1f74yfReO83Y2agnLRImXV+ 22 | wgKY7tr0mDsJmB2UIVBAOG+uA0bWTTDzv3op1lodqnYGr/0xBCNCq4Xk4kEIDoZ5 23 | vVfpl4iIPscdlAkQLKs2E/8/yXn4QjJBFrM7s0dHmOQb4shuFpXDFlIDwOryV/O+ 24 | 72obqYOGkqAaQaoxCWGlz67yPDk0MXD12Pqf 25 | -----END CERTIFICATE----- 26 | -------------------------------------------------------------------------------- /certs/server.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIICfTCCAWUCAQAwETEPMA0GA1UEAxMGc2VydmVyMIIBIjANBgkqhkiG9w0BAQEF 3 | AAOCAQ8AMIIBCgKCAQEAw6yHdid4DCBBbrml7e5dcoozaXRQZtXcG7d4/tEgHs0p 4 | Nc4vggriTWbgTLCCDMvaiS7wL4tS8hJ9l4M5zIBZFWAzgcUTPqoJdWaFDYL0GdWp 5 | AnQ3qC0wFFy/dh/nssZPw6EfgZH00MHq1mmb+G8s5Ar3gxmkKrakm7HQRMF0ZzGd 6 | CNuCA8bYL+n+vC9ov1aUG5lhwq2ExZZ8X8Offr4ME5ea95Qw+++uEGM7dhSkWysG 7 | e/9pAzdgCZw8qpGZxbQez0Lc9zO7gdUU4dPLIqYE8cf6Jiw3EMKJuNyVf3NPM6BL 8 | OWAaJdrjnEd470ysS85gUuz/XfpqDNj39hQqq6ZEFwIDAQABoCcwJQYJKoZIhvcN 9 | AQkOMRgwFjAUBgNVHREEDTALgglsb2NhbGhvc3QwDQYJKoZIhvcNAQELBQADggEB 10 | AEeoYs6pXmHF/3GRnXoljShCKmA8QFuea2M9tPp9IJmBkUxGOFX6sohp/tCv/fuD 11 | MH3zg4kk5vWA/wodcLk/zba1gH7BitENm3d5Gnk81+4Eq7stZQjnZnMqfX+JSPG2 12 | cRreVWfEGePRjiL/Wfp9ajhb6xgC4Q1tpZVT86rb3m0GndMB59Z0J3s7zHhOOfGf 13 | TXvaHEBMeXedWA9CISTmI70U5CkMgatHp7di69beefCco/W5b1rO/u5BkgUqJ+mV 14 | K0OhX0UV/oH0I2fx8gO1YiqFSKzfrPJBKXfVs9kpratbbWQ6anRE3aFcMGCCWd1r 15 | Z4gzDvW0gG60i4Eddy+EpVE= 16 | -----END CERTIFICATE REQUEST----- 17 | -------------------------------------------------------------------------------- /certs/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAw6yHdid4DCBBbrml7e5dcoozaXRQZtXcG7d4/tEgHs0pNc4v 3 | ggriTWbgTLCCDMvaiS7wL4tS8hJ9l4M5zIBZFWAzgcUTPqoJdWaFDYL0GdWpAnQ3 4 | qC0wFFy/dh/nssZPw6EfgZH00MHq1mmb+G8s5Ar3gxmkKrakm7HQRMF0ZzGdCNuC 5 | A8bYL+n+vC9ov1aUG5lhwq2ExZZ8X8Offr4ME5ea95Qw+++uEGM7dhSkWysGe/9p 6 | AzdgCZw8qpGZxbQez0Lc9zO7gdUU4dPLIqYE8cf6Jiw3EMKJuNyVf3NPM6BLOWAa 7 | JdrjnEd470ysS85gUuz/XfpqDNj39hQqq6ZEFwIDAQABAoIBACfb70QsShH+SBXs 8 | D1bmZXa9nDdxECfCjLHAsA8IPojHtgtV0uukl/C4chxtIDDHHd8wBpFIyj6xcY/5 9 | lT6JLpxiGlvm70KmlIKrvZLBCERy95ieX/AMNG0Rb5b6bNJH/1uUaLyWJM6ZkeLX 10 | ZIQ48WYKrdywznY8+k8U65314mZao3dUxXsFzzfA4LSD3fqXDVzlYMp2CGkFNrNs 11 | QXT4V6ksdiU5LpoMEDTi6u8TW8ohnbbEHMf+W/3NRp5ok25Iu5QVi9z5hqxPHbrf 12 | WC90ds5NiZrL542HbBQR2mQur5Ghhmzr/lgYr+RCp3MpYrIn3v/vxMK8lxUTty7+ 13 | lE+HFTECgYEA8Uy3wRSwauVEXbAOZY2uOvTfZJch0EF6Qx9AjoN4IQIoUiSOoFiF 14 | obH7o6gV8StM7KpYIOAAoECiVodCnCFvzqHET0DEFfQXR9VbgkJXNhxBk03cHJB4 15 | Nis/N7xflRlG/ceSaK1ovDOMLp2abgOOE4zJ3pvq+SOMaWT7Zo0HXjsCgYEAz5g8 16 | 0gEqQBU4gjlVxoMizqm5ExJ8bJ/E8JAJKqdmqKbQSjYFdArgF1G6F3qS1l2YOfNe 17 | RKU+a3vw4NAjbQm0dQ6FzZjg3mUD/Ae7YWf8J31FNHt406MsCSjxHknm+ZHLZhiq 18 | TvE8MVzLLHJHjPN7dHjX1FnexpihHkIiZ+F/x9UCgYEAx+0kotmdgjQLfQSOKR5G 19 | qZqSLSn7Xi2iO1L9jhbnxZGgDs8zTS+LKPnq4SI3a+bQiZZqBnMSkbR7zO2tBue9 20 | V5s4p+Gllfrz/xSMCVUmJTMsMfxkz1X0BA25B1kQaNzUsRhV+l0TVjvvfgAfH9/a 21 | K+COsd7FxT7q67g7hDB9VlkCgYByHnNY0rEiN0gwNkqT7STmTODjvibOI+pEgDm5 22 | y/qgPcuE8DuBNjVIPHI7U/OCikMToFizDc/JkIGzZFgFKe+c5dXiVudO+mL9WlbE 23 | zkAMiU3Q8wtR9IJRqsMZ7nBdw6r3vhlpei2nySYbkdsJfhPgzEmBf8H6lI8Frj9x 24 | scHn3QKBgCT/2xVuBwbVkCxwJO2CgRFB1CMGE5ntZgqUPWe2wFhPt0D8jTDF8be+ 25 | fm9lLzaSWBzsC8vmF2sow7bEQ2GmF5dZCy2ulBw9EOvyAjvjCKtAy+QC6tfoUVSS 26 | lj6UHIDQ96LrVsN6pyvWYQR0S68GIfz03EVkr3vA31UWWEjWLyjN 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /certs/tillitis.crl: -------------------------------------------------------------------------------- 1 | -----BEGIN X509 CRL----- 2 | MIICgjBsAgEBMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNVBAMTCHRpbGxpdGlzFw0y 3 | NDA2MTExNDIyMzJaFw0zNDA2MTExNDIyMjlaMACgIzAhMB8GA1UdIwQYMBaAFGsH 4 | P0JUeSaij+BSv41cxyMsy+2BMA0GCSqGSIb3DQEBCwUAA4ICAQCThZ5IA71CIpnt 5 | +nMy9OBrkPBuP28sHqOwF+sYoUwi6M+5bF/fboLdgfXJoXkcQZD1DWRwZZaP/G/c 6 | +RRiUsYx2UVgyxlBLLs3wSwlMU2f5tN0lOnwEhoJhxu7AnAxa4xM9VSzBu5t3uj6 7 | DWlQ9kJvD2jcdlkUBaSEcnymDKWck7zQOrSackCdnrpeyWO8f/VcLX/he3uivynq 8 | tvbyq7pk2dpr+I9t8aALHM/QqNBpeF1w99Rtrw8UuG8srk9oI4/sgmK8mek/Vjlq 9 | uTng/LXnmf6LsjXI5vl85dlvEbfklMQ7CC+/loc8hpSNoVOLwKjsjSDku4daD/ML 10 | ZD3PwKQ5GXkowwWJgDavS5df49Sti7qHcuYAKgVtYszPtj2zSq/gw4y3hkmHpcOK 11 | 4hTFdijB0Ewtn8e62AZW+I/xd8SyyYbpsYP9jI0gUHVMMGE4EKWqD5n9i+4VUFRc 12 | q/mZM3ztjkZ5ezvcY1NKivmFcp+8/64VypUFnAR1p2p5ltrZmh5ZBaOycw7Q0ieD 13 | LuSJwAORwfGSgBWvrWnbFWEisNajIlFye9k9KxhC0E26JKSWD5/OrYJr60VfQvDU 14 | yc0gExgQ2MyZTkek7hDMyPG2GUpfoLvpKryHFYXBnRr20K8U8c5dxEYfkapDi75t 15 | NNAlUCmpTmxDN58CRAcvz78ICvBANw== 16 | -----END X509 CRL----- 17 | -------------------------------------------------------------------------------- /certs/tillitis.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIE5jCCAs6gAwIBAgIBATANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDEwh0aWxs 3 | aXRpczAeFw0yNDA2MTExNDEyMzJaFw0zNDA2MTExNDIyMjlaMBMxETAPBgNVBAMT 4 | CHRpbGxpdGlzMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEArBIpIwrk 5 | qoc36D/N3FQut8owjTtrJKek/igEnagHR/XL9oG7lwWW+UqSVauXFtv5hHrauxL5 6 | EXMZy6XcewYx3aPf8FmbY0G+LLCoO4AsxppluK/DtbvA2aZNCJa6YNHSiMbYVLEl 7 | +W2U2aY+ZtTaC1I5z6zWhBoZpIWMDJ3vBsXuu2900xGSBzph+Epk+cgnqv+C5zFA 8 | cfSYKJyZEDvEffnMEXQBljUj/mEwSkjLDIs6axwH6GKzvp++26a2V9F2LjT6VZNT 9 | 5Ql79n2I45MCW0UgfAjcWlyHI+rmGJGtfOT5Hx1USZaGgPMiFxaPqeeElYzXt84U 10 | STi2MAbGJ7VBhezr4FV7W1qjVs7NJI4xVmYZUkNjfj4rXPDg2v+5wOv7aMN/vvvN 11 | cqZPtnFRRsobibHt11TrnOQsdZFNkRVpgTzNWCv+vNe3/wEtAHTfsID/4lIyICV1 12 | hFc+spN2djbol6rekEJKYinJ43wdyhgnkF3A8QuScFO26nOLMpIger/RPlUWCvrQ 13 | TvUtGXGNAlqfUEHuQRhciH4+TWftFrHfr8WN5NA2ps51q6N9BMrjN6Zx53FDo3X8 14 | cGwatd3jQEzl7pOMx8zcwmCi5GZs44ZyKqmD64vUdnMz2fVmJz+X4eTT+VdDSKdB 15 | +iZZ8vXLPAFBMoFMoAxM/XI5lm/31YF1PU8CAwEAAaNFMEMwDgYDVR0PAQH/BAQD 16 | AgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFGsHP0JUeSaij+BSv41c 17 | xyMsy+2BMA0GCSqGSIb3DQEBCwUAA4ICAQBMdK+TJW3evSY+ee6x8eotZ4/12PhK 18 | OjKx3bPRwaqNKlLRxKdFdg1VfMq0EyT5tug76Tu9TfbiH22KML8u3pD6f1Ck63Rw 19 | KwY8rbv78IIPCQ+gbXjKCW25DjMfMMCM0GM1ywCDeEE06xbC6hKCayIFsOl8n9CI 20 | ixiBopl4Jy0R2tZYzWgVQ8blqoEiaBaLlQYA2vBKaD+rv3G8GqSlB9IWo/QJsJCt 21 | /8B6JX1dc476sUAdYWsP+B2m9+xUVb6h7uaJMbujcnNpnXLkhEN1u2FQNXCPUjyG 22 | UJJzwpRAQaczdJ+jwh89WVmP6CJ9h6zpeEduXz9dKYZ719prDNfTKXdB3FrJaODP 23 | 2fTPmNaEKt9e7O7DQPIJAs4HlZ8aBW3r8wHtLJnDNSuw6yxwj5ZV2LlJIcs2rm0I 24 | jzrl14djCV6L5t+clhrzeWbdRMM3YeCLkJ+ExGLyd2NA+1FEM46YVSQu14VkK+20 25 | GBwBvYQG1mSrC7E76rTt9QbxpjEXq/5hnqmJ5ob8ehZ1c4ixk7C3ChEgr2C3/EXA 26 | cppe9HlIdxfhBe4Y+c9BsOiJuTMmS4qMTQfuZgjEksAMoBTSHGauaBRSdVQ+hC6S 27 | D4GBs3Fe7RDQfmKUJSW6zBa8OQd1jOx10iT5Cr3zGodd6elb07JqaCKystswAT6C 28 | jldSdc9XrBcVvg== 29 | -----END CERTIFICATE----- 30 | -------------------------------------------------------------------------------- /certs/tillitis.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIJJwIBAAKCAgEArBIpIwrkqoc36D/N3FQut8owjTtrJKek/igEnagHR/XL9oG7 3 | lwWW+UqSVauXFtv5hHrauxL5EXMZy6XcewYx3aPf8FmbY0G+LLCoO4AsxppluK/D 4 | tbvA2aZNCJa6YNHSiMbYVLEl+W2U2aY+ZtTaC1I5z6zWhBoZpIWMDJ3vBsXuu290 5 | 0xGSBzph+Epk+cgnqv+C5zFAcfSYKJyZEDvEffnMEXQBljUj/mEwSkjLDIs6axwH 6 | 6GKzvp++26a2V9F2LjT6VZNT5Ql79n2I45MCW0UgfAjcWlyHI+rmGJGtfOT5Hx1U 7 | SZaGgPMiFxaPqeeElYzXt84USTi2MAbGJ7VBhezr4FV7W1qjVs7NJI4xVmYZUkNj 8 | fj4rXPDg2v+5wOv7aMN/vvvNcqZPtnFRRsobibHt11TrnOQsdZFNkRVpgTzNWCv+ 9 | vNe3/wEtAHTfsID/4lIyICV1hFc+spN2djbol6rekEJKYinJ43wdyhgnkF3A8QuS 10 | cFO26nOLMpIger/RPlUWCvrQTvUtGXGNAlqfUEHuQRhciH4+TWftFrHfr8WN5NA2 11 | ps51q6N9BMrjN6Zx53FDo3X8cGwatd3jQEzl7pOMx8zcwmCi5GZs44ZyKqmD64vU 12 | dnMz2fVmJz+X4eTT+VdDSKdB+iZZ8vXLPAFBMoFMoAxM/XI5lm/31YF1PU8CAwEA 13 | AQKCAgAaqO2qukNXkH+/AjO5EWnzm+YN28jr8vWDU1vzuVb3dUPWu57+9STBFya5 14 | E07jgc2VExvUpEnmxDiY7ufMP9d2Ca/mo1uJ82OttOk73s4RL1bXWJQwEhj9H+EL 15 | lZ7m5VuDSZu284N4s9u+JR3B/PSTkHfYj2TlLs4OSYq7YbTpDb3BWLF3Cl/fstH/ 16 | EDe446QRwGjdvSnI/iadFDY656e4BP3zLlAYn78l6JT8zW54URIKClvHS5Oh00pU 17 | 6e2SiaeP3TF0FNGIyQFl3WlaKSrSTVGNJipN7cwaaoigAeIpWaing3nNtP699+2u 18 | OhakMnS+XwYFOm62MY04v7Pv7wtpnXGK6E3uWQyBKq2Bwxxl/q3UDPNdmXpo5V4u 19 | g9shBd67Jf8NH+XSGYchHA70/Ugsywidfd25ejj43Bf7t/rmnAlhsSOM5UnHiBpI 20 | bZHjgJqUDkCXpR5YMNxmBJ21zEtVd/tUEhvStq4/al1C+vv6wupljLXAHy5jVEaq 21 | gAQSMLc7GLJSkUZTG63qc6+10GZlp1XGOttM2M6+Xjpq6EKXQ4lPdsLIzf3v0AbT 22 | KPO4KAXpZTtRIp7MDBiBUKFXrXsjHeGzAFJx6pYnAEkGBeJ81SOIqiXPBaZSBPjl 23 | lQB4rNNeXn3yFR9au0T/0QNFjsG0ZBeOPtNwL7kiEVWGM1sZ4QKCAQEAxMbCcqfU 24 | QsRLjujwhEMEH0QXnNA+yXLCfpgbiaHLFS7PG+S4GuMMtbPbBZ7t9tQ2XKjnuFQh 25 | oQoSNd1UCSy8oto4JGM9ekKJBwLTzECibPKKGorz/QiH4e5PDKygrIn6MkQ+urAV 26 | YPco0gj64lhQ01jpENlLg7/mhelyYQuxRwlR3+cUZMzQOxLSlveLv4Ek64P60b/s 27 | dmms0vaQYBqHLbhZ1Ucv+WN615/vWM6qC0wW8nmX9Lejx7LJYI8F/ZRb6BzB1gDJ 28 | +rhHoaXSUUtyZKgPUZ2LOw+FLjiucAfakYXFm37X3db66hLtWAKlsYxszOyGUgOP 29 | 8qC7XKlwtidVyQKCAQEA39vkm9v7fxEPAGQqvsvA4O55DijkETw/5DbM0pRIToLB 30 | KxrfX4FQ1DB9TO/JajeXbLsqcZmqj5CjEMpZlqJbPjrIofkIPmGF3RM36CWbtJkz 31 | 5Dx6n8dC+GZzvL+WdetnSHqM5G9ea4P3G1C6oZc78CG6pvzKh+WbO3xeDOnFRE4B 32 | IhmnFAzDZ1T3ZX8Wfvnp2B5l7QVlnvXGult+iYAw9c8dJ/USeFLWo9yZnHbZ2lOd 33 | Fb06Zn7e0pR2a2l/q/n45x5QpBPvMe2ZBrsb7Mfa61kRFEi9PyrYGT5zXhvCna3T 34 | RuC2sn1N7kdyyGoURLuABkO0HWB/BDpeEFduZyFmVwKCAQBgZJ1MXTp1moMI9sMb 35 | h1i1mD2Y68pWFImCSjDzMvaw4cVC/L/TK/ZF7F0WW8xmZX5b1cHtYgk3Qh+5kzpf 36 | ZDLj89IcwSCUi5Nkg9YM8Hygsg9FAVyUT2GBI6RFqSrsWordpOC1wxw1691rRkrg 37 | JjMx1RQW1NtRJuheIQWO1JN7IO2iqSgsWMiIp6fRnGIw5aqYVIbBySqktw+aKH6B 38 | FX2yv+QBvmWZ3UmH5yjwcAyMjDGRhk8R6gOpB8PuMSHWHp8HDU9G7gwDbdJpNTnZ 39 | 6QOYm1slt+9Ed03eXpXU+4PuVYF/sps8aNtxXuQ1gNZ6KzgBCryh4GzbhjFCKx6z 40 | nIFBAoIBAEd4sViBt54uGBgL4wnfi8lE2imHLKnEDrISPPrOlPdcD8hvX/niE6hr 41 | gtXZiPj8nr/8UeTLJwWHLEjkEg5h77fClOQB2nvwLwkSZbscpsXWVBb+qDfzKpKc 42 | Qz9z+9nSOruNRQZ5+C9jhrWV5MBKxLw6SJmgBBm3WQR5BJ4i1PSl4v68Kffgy1Jm 43 | ckZZeRk5OUvjCK/QFU3XMoe1RoUoBHm2kpx7RlZcPGM8v16RI6TtJS4XqFmpEAMz 44 | m0tX94c03B8OMrjwFGJgdYcMnjHO7TpOyYEsaB+O0kfdBIB7ZHtrUBVFEY+TKhp1 45 | fVQ3ahk3s/xMCWmHsYXsDLQy9fC/XSkCggEAFQbYlE4ExCg88oTxd/Taw7+JZF0f 46 | o6W3LAyX7S5/2VQixpfXTksae8p85dW3WeequYKa92n5DZCIQGiW5JIcnYuzbvo1 47 | gHi+9S8nFq8cRGas+W2sc4cmPFpRQ1zekryULmqL7NBPQ27m2euKI7lsqvP9xCX8 48 | eQAF5m3EV2zrYxIIJk0cdINbBCfYU6449cORtU7Dmg3queMxEu2DzBCVF8Sc/2CU 49 | zaaEyPxcq36EUcikdyFE8UNf3EYILt5jL49PFOdfY9fDtwLINNLOtX0spqieWKYa 50 | Cr18vrC52x1hZqKm6NrZbvt/YCZnxFQaVpvIo65gqBH6ccuSM4uaZaeTdQ== 51 | -----END RSA PRIVATE KEY----- 52 | -------------------------------------------------------------------------------- /cmd/tkey-verification/api.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Tillitis AB 2 | // SPDX-License-Identifier: BSD-2-Clause 3 | 4 | package main 5 | 6 | import ( 7 | "crypto/ed25519" 8 | "crypto/sha512" 9 | "encoding/hex" 10 | "encoding/json" 11 | "errors" 12 | "fmt" 13 | "os" 14 | "sync" 15 | "time" 16 | 17 | "github.com/tillitis/tkey-verification/internal/tkey" 18 | ) 19 | 20 | const MessageLen = tkey.UDISize + sha512.Size + ed25519.PublicKeySize 21 | 22 | type API struct { 23 | mu sync.Mutex 24 | vendorPubKey []byte 25 | tk tkey.TKey 26 | } 27 | 28 | func NewAPI(vendorPubKey []byte, tk tkey.TKey) *API { 29 | return &API{ 30 | mu: sync.Mutex{}, 31 | vendorPubKey: vendorPubKey, 32 | tk: tk, 33 | } 34 | } 35 | 36 | func (*API) Ping(_ *struct{}, _ *struct{}) error { 37 | le.Printf("Got Ping\n") 38 | 39 | return nil 40 | } 41 | 42 | type Args struct { 43 | UDIBE []byte 44 | AppTag string 45 | AppHash []byte 46 | Message []byte 47 | } 48 | 49 | func (api *API) Sign(args *Args, _ *struct{}) error { 50 | api.mu.Lock() 51 | defer api.mu.Unlock() 52 | 53 | le.Printf("Going to sign for TKey with UDI:%s(BE) apptag:%s apphash:%0x…\n", hex.EncodeToString(args.UDIBE), args.AppTag, args.AppHash[:16]) 54 | 55 | if l := len(args.UDIBE); l != tkey.UDISize { 56 | le.Printf("Expected %d bytes UDIBE, got %d", tkey.UDISize, l) 57 | 58 | return ErrUDI 59 | } 60 | 61 | if args.AppTag == "" { 62 | le.Printf("No tag provided\n") 63 | 64 | return ErrNoTag 65 | } 66 | 67 | // Not encoded as hex, so this is the best we can do, instead 68 | // of trying to parse. 69 | if l := len(args.AppHash); l != sha512.Size { 70 | le.Printf("Expected %d bytes app digest, got %d\n", sha512.Size, l) 71 | 72 | return ErrWrongDigest 73 | } 74 | 75 | if l := len(args.Message); l != MessageLen { 76 | le.Printf("Expected %d bytes message to sign, got %d\n", MessageLen, l) 77 | 78 | return ErrWrongLen 79 | } 80 | 81 | signature, err := api.tk.Sign(args.Message) 82 | if err != nil { 83 | le.Printf("tkey.Sign failed: %v", err) 84 | 85 | return ErrSignFailed 86 | } 87 | 88 | if !ed25519.Verify(api.vendorPubKey, args.Message, signature) { 89 | le.Printf("Vendor signature failed verification\n") 90 | 91 | return ErrVerificationFailed 92 | } 93 | 94 | // File named after the UDIBE in hex 95 | fn := fmt.Sprintf("%s/%s", signaturesDir, hex.EncodeToString(args.UDIBE)) 96 | if _, err = os.Stat(fn); err == nil || !errors.Is(err, os.ErrNotExist) { 97 | le.Printf("Signature file %s already exists\n", fn) 98 | 99 | return ErrSigExist 100 | } 101 | 102 | json, err := json.Marshal(Verification{ 103 | Timestamp: time.Now().UTC().Format(time.RFC3339), 104 | AppTag: args.AppTag, 105 | AppHash: hex.EncodeToString(args.AppHash), 106 | Signature: hex.EncodeToString(signature), 107 | }) 108 | if err != nil { 109 | le.Printf("JSON Marshal failed: %v", err) 110 | 111 | return ErrInternal 112 | } 113 | 114 | //nolint:gosec 115 | if err = os.WriteFile(fn, append(json, '\n'), 0o644); err != nil { 116 | le.Printf("WriteFile %s failed: %v", fn, err) 117 | 118 | return ErrInternal 119 | } 120 | 121 | le.Printf("Wrote %s\n", fn) 122 | 123 | return nil 124 | } 125 | -------------------------------------------------------------------------------- /cmd/tkey-verification/appbins.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Tillitis AB 2 | // SPDX-License-Identifier: BSD-2-Clause 3 | 4 | package main 5 | 6 | import ( 7 | "bytes" 8 | "crypto/sha512" 9 | "embed" 10 | "encoding/hex" 11 | "fmt" 12 | "io/fs" 13 | "path" 14 | "sort" 15 | "strings" 16 | ) 17 | 18 | type AppBin struct { 19 | Tag string // Name and tag of device app 20 | Bin []byte // Actual binary of device app 21 | } 22 | 23 | func (a *AppBin) String() string { 24 | return fmt.Sprintf("tag:%s hash:%0x…", a.Tag, a.Hash()[:16]) 25 | } 26 | 27 | func (a *AppBin) Hash() []byte { 28 | hash := sha512.Sum512(a.Bin) 29 | return hash[:] 30 | } 31 | 32 | type AppBins struct { 33 | Bins map[string]AppBin 34 | } 35 | 36 | // Get returns an AppBin indexed by the app hash digest. 37 | func (a AppBins) Get(hash string) (AppBin, error) { 38 | if val, ok := a.Bins[hash]; ok { 39 | return val, nil 40 | } 41 | 42 | return AppBin{}, ErrNotFound 43 | } 44 | 45 | //go:embed bins/*.bin bins/*.bin.sha512 46 | var binsFS embed.FS 47 | 48 | const binsDir = "bins" 49 | 50 | func (a AppBins) Tags() []string { 51 | tags := []string{} 52 | 53 | for _, appBin := range a.Bins { 54 | tags = append(tags, appBin.Tag) 55 | } 56 | 57 | sort.Strings(tags) 58 | 59 | return tags 60 | } 61 | 62 | // NewAppBins initializes the embedded device apps. 63 | func NewAppBins() (AppBins, error) { 64 | var appBins = AppBins{ 65 | Bins: map[string]AppBin{}, 66 | } 67 | 68 | entries, err := binsFS.ReadDir(binsDir) 69 | if err != nil { 70 | return AppBins{}, IOError{path: binsDir, err: err} 71 | } 72 | 73 | for _, entry := range entries { 74 | binFn := entry.Name() 75 | if !entry.Type().IsRegular() || !strings.HasSuffix(binFn, ".bin") { 76 | continue 77 | } 78 | 79 | tag := strings.TrimSuffix(binFn, ".bin") 80 | 81 | if tag == "" { 82 | continue 83 | } 84 | 85 | var info fs.FileInfo 86 | 87 | if info, err = entry.Info(); err != nil { 88 | return AppBins{}, IOError{path: binFn, err: err} 89 | } else if info.Size() == 0 { 90 | return AppBins{}, MissingError{what: binFn} 91 | } 92 | 93 | var bin []byte 94 | if bin, err = binsFS.ReadFile(path.Join(binsDir, binFn)); err != nil { 95 | return AppBins{}, IOError{path: binFn, err: err} 96 | } 97 | 98 | // Require accompanying sha512 file with matching hash 99 | hashFn := binFn + ".sha512" 100 | var hash []byte 101 | if hash, err = binsFS.ReadFile(path.Join(binsDir, hashFn)); err != nil { 102 | return AppBins{}, IOError{path: binFn, err: err} 103 | } 104 | if hash, err = hex.DecodeString(string(hash[:sha512.Size*2])); err != nil { 105 | return AppBins{}, IOError{path: hashFn, err: err} 106 | } 107 | 108 | appBin := AppBin{ 109 | Tag: tag, 110 | Bin: bin, 111 | } 112 | 113 | if !bytes.Equal(appBin.Hash(), hash) { 114 | return AppBins{}, EqualError{one: binFn, two: hashFn} 115 | } 116 | 117 | appBins.Bins[hex.EncodeToString(hash)] = appBin 118 | } 119 | 120 | return appBins, nil 121 | } 122 | -------------------------------------------------------------------------------- /cmd/tkey-verification/bins/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tillitis/tkey-verification/066f40bf090f83c2c9d860481cdf81006c8d43a0/cmd/tkey-verification/bins/.gitkeep -------------------------------------------------------------------------------- /cmd/tkey-verification/bins/README.md: -------------------------------------------------------------------------------- 1 | # Embedded binaries 2 | 3 | This file consists of a list of embedded binaries, the location of the 4 | source and the license. 5 | 6 | File: signer-v1.0.1.bin 7 | Source: https://github.com/tillitis/tkey-device-signer 8 | License: GPL-2.0 Only 9 | 10 | File: verisigner-v0.0.3.bin 11 | Source: https://github.com/tillitis/tkey-verification 12 | License: GPL-2.0 Only 13 | 14 | -------------------------------------------------------------------------------- /cmd/tkey-verification/bins/signer-v1.0.1.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tillitis/tkey-verification/066f40bf090f83c2c9d860481cdf81006c8d43a0/cmd/tkey-verification/bins/signer-v1.0.1.bin -------------------------------------------------------------------------------- /cmd/tkey-verification/bins/signer-v1.0.1.bin.sha512: -------------------------------------------------------------------------------- 1 | cd3c4f433f84648428113bd0a0cc407b2150e925a51b478006321e5a903c1638ce807138d1cc1f8f03cfb6236a87de0febde3ce0ddf177208e5483d1c169bac4 signer-v1.0.1.bin 2 | -------------------------------------------------------------------------------- /cmd/tkey-verification/bins/verisigner-v0.0.3.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tillitis/tkey-verification/066f40bf090f83c2c9d860481cdf81006c8d43a0/cmd/tkey-verification/bins/verisigner-v0.0.3.bin -------------------------------------------------------------------------------- /cmd/tkey-verification/bins/verisigner-v0.0.3.bin.deps: -------------------------------------------------------------------------------- 1 | appsrepotag=v0.0.6 2 | -------------------------------------------------------------------------------- /cmd/tkey-verification/bins/verisigner-v0.0.3.bin.sha512: -------------------------------------------------------------------------------- 1 | f8ecdcda53a296636a0297c250b27fb649860645626cc8ad935eabb4c43ea3e1841c40300544fade4189aa4143c1ca8fe82361e3d874b42b0e2404793a170142 verisigner-v0.0.3.bin 2 | -------------------------------------------------------------------------------- /cmd/tkey-verification/cert.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Tillitis AB 2 | // SPDX-License-Identifier: BSD-2-Clause 3 | 4 | package main 5 | 6 | import ( 7 | "crypto/tls" 8 | "crypto/x509" 9 | "os" 10 | ) 11 | 12 | func loadCA(certFile string) *x509.CertPool { 13 | caPEM, err := os.ReadFile(certFile) 14 | if err != nil { 15 | le.Printf("ReadFile failed: %s", err) 16 | os.Exit(1) 17 | } 18 | certPool := x509.NewCertPool() 19 | if !certPool.AppendCertsFromPEM(caPEM) { 20 | le.Printf("Append CA failed: %s", err) 21 | os.Exit(1) 22 | } 23 | return certPool 24 | } 25 | 26 | func loadCert(certFile string, keyFile string) tls.Certificate { 27 | serverCert, err := tls.LoadX509KeyPair(certFile, keyFile) 28 | if err != nil { 29 | le.Printf("Load cert failed: %s", err) 30 | os.Exit(1) 31 | } 32 | return serverCert 33 | } 34 | -------------------------------------------------------------------------------- /cmd/tkey-verification/common.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Tillitis AB 2 | // SPDX-License-Identifier: BSD-2-Clause 3 | 4 | package main 5 | 6 | import ( 7 | "bytes" 8 | "crypto/ed25519" 9 | "crypto/sha512" 10 | 11 | "github.com/tillitis/tkey-verification/internal/tkey" 12 | ) 13 | 14 | func buildMessage(udiBE, fwHash, pubKey []byte) ([]byte, error) { 15 | var buf bytes.Buffer 16 | 17 | if l := len(udiBE); l != tkey.UDISize { 18 | return nil, ErrWrongLen 19 | } 20 | buf.Write(udiBE) 21 | 22 | if l := len(fwHash); l != sha512.Size { 23 | return nil, ErrWrongLen 24 | } 25 | buf.Write(fwHash) 26 | 27 | if l := len(pubKey); l != ed25519.PublicKeySize { 28 | return nil, ErrWrongLen 29 | } 30 | buf.Write(pubKey) 31 | 32 | return buf.Bytes(), nil 33 | } 34 | 35 | func verifyFirmwareHash(expectedFW Firmware, tk tkey.TKey) (Firmware, error) { 36 | fwHash, err := tk.GetFirmwareHash(expectedFW.Size) 37 | if err != nil { 38 | return Firmware{}, IOError{path: "TKey", err: err} 39 | } 40 | if !bytes.Equal(expectedFW.Hash[:], fwHash) { 41 | le.Printf("TKey does not have expected firmware hash %0x…, but instead %0x…", expectedFW.Hash[:16], fwHash[:16]) 42 | return Firmware{}, ErrWrongFirmware 43 | } 44 | 45 | return expectedFW, nil 46 | } 47 | -------------------------------------------------------------------------------- /cmd/tkey-verification/config.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 Tillitis AB 2 | // SPDX-License-Identifier: BSD-2-Clause 3 | 4 | package main 5 | 6 | import ( 7 | "crypto/tls" 8 | "os" 9 | 10 | "gopkg.in/yaml.v2" 11 | ) 12 | 13 | type Server struct { 14 | Addr string 15 | TLSConfig tls.Config 16 | } 17 | 18 | type ServerConfig struct { 19 | CACert string `yaml:"cacert"` 20 | ServerCert string `yaml:"servercert"` 21 | ServerKey string `yaml:"serverkey"` 22 | ListenAddr string `yaml:"listen"` 23 | VendorSigningAppHash string `yaml:"vendorapphash"` 24 | } 25 | 26 | type ProvConfig struct { 27 | CACert string `yaml:"cacert"` 28 | ClientCert string `yaml:"clientcert"` 29 | ClientKey string `yaml:"clientkey"` 30 | ServerAddr string `yaml:"server"` 31 | SigningAppHash string `yaml:"signingapphash"` 32 | } 33 | 34 | func loadServeSignerConfig(fn string) (ServerConfig, error) { 35 | var conf ServerConfig 36 | 37 | rawConfig, err := os.ReadFile(fn) 38 | if err != nil { 39 | return conf, IOError{path: fn, err: err} 40 | } 41 | 42 | err = yaml.Unmarshal(rawConfig, &conf) 43 | if err != nil { 44 | return conf, ParseError{what: "config", err: err} 45 | } 46 | 47 | return conf, nil 48 | } 49 | 50 | func loadRemoteSignConfig(fn string) (ProvConfig, error) { 51 | var conf ProvConfig 52 | 53 | rawConfig, err := os.ReadFile(fn) 54 | if err != nil { 55 | return conf, IOError{path: fn, err: err} 56 | } 57 | 58 | err = yaml.Unmarshal(rawConfig, &conf) 59 | if err != nil { 60 | return conf, ParseError{what: "config", err: err} 61 | } 62 | 63 | return conf, nil 64 | } 65 | -------------------------------------------------------------------------------- /cmd/tkey-verification/error.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Tillitis AB 2 | // SPDX-License-Identifier: BSD-2-Clause 3 | 4 | package main 5 | 6 | import "fmt" 7 | 8 | type constError string 9 | 10 | func (err constError) Error() string { 11 | return string(err) 12 | } 13 | 14 | const ( 15 | ErrNotFound = constError("not found") 16 | ErrUDI = constError("erroneous UDI") 17 | ErrNoTag = constError("empty tag") 18 | ErrWrongDigest = constError("erroneous app digest") 19 | ErrWrongLen = constError("wrong message length") 20 | ErrSignFailed = constError("signing failed") 21 | ErrVerificationFailed = constError("signature failed verification") 22 | ErrSigExist = constError("vendor signature already exist") 23 | ErrInternal = constError("internal error") 24 | ErrIO = constError("I/O error") 25 | ErrWrongFirmware = constError("not expected firmware") 26 | ) 27 | 28 | // More complex errors get their own type below 29 | 30 | type MissingError struct { 31 | what string 32 | } 33 | 34 | func (e MissingError) Error() string { 35 | return fmt.Sprintf("missing: %v", e.what) 36 | } 37 | 38 | type EqualError struct { 39 | one interface{} 40 | two interface{} 41 | } 42 | 43 | func (e EqualError) Error() string { 44 | return fmt.Sprintf("not equal: %v != %v", e.one, e.two) 45 | } 46 | 47 | type IOError struct { 48 | path string 49 | err error 50 | } 51 | 52 | func (e IOError) Error() string { 53 | return fmt.Sprintf("I/O error on %v: %v", e.path, e.err) 54 | } 55 | 56 | func (e IOError) Unwrap() error { 57 | return e.err 58 | } 59 | 60 | type ParseError struct { 61 | what string 62 | err error 63 | } 64 | 65 | func (e ParseError) Error() string { 66 | return fmt.Sprintf("couldn't parse %v: %v", e.what, e.err) 67 | } 68 | 69 | func (e ParseError) Unwrap() error { 70 | return e.err 71 | } 72 | 73 | type SimpleParseError struct { 74 | msg string 75 | } 76 | 77 | func (e SimpleParseError) Error() string { 78 | return e.msg 79 | } 80 | 81 | type ExistError struct { 82 | what string 83 | } 84 | 85 | func (e ExistError) Error() string { 86 | return fmt.Sprintf("already exists: %v", e.what) 87 | } 88 | 89 | type RangeError struct { 90 | what string 91 | } 92 | 93 | func (e RangeError) Error() string { 94 | return fmt.Sprintf("out of range: %v", e.what) 95 | } 96 | -------------------------------------------------------------------------------- /cmd/tkey-verification/firmwares.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Tillitis AB 2 | // SPDX-License-Identifier: BSD-2-Clause 3 | 4 | package main 5 | 6 | import ( 7 | "crypto/sha512" 8 | "encoding/binary" 9 | "encoding/hex" 10 | "fmt" 11 | 12 | "github.com/tillitis/tkey-verification/internal/tkey" 13 | ) 14 | 15 | const ( 16 | fwSizeMin int = 2000 17 | fwSizeMax int = 8192 18 | ) 19 | 20 | type Firmware struct { 21 | Hash [sha512.Size]byte 22 | Size int 23 | } 24 | 25 | // Firmwares is a dictionary of all known firmwares, index by the 26 | // first part of the UDI, UDI0. 27 | type Firmwares struct { 28 | firmwares map[hardware]Firmware 29 | } 30 | 31 | // GetFirmware returns what we know about a firmware indexed by a UDI, 32 | // or an error if no firmare is found. 33 | func (f Firmwares) GetFirmware(udi tkey.UDI) (*Firmware, error) { 34 | hw, err := newHardware(udi.VendorID, udi.ProductID, udi.ProductRev) 35 | if err != nil { 36 | return nil, err 37 | } 38 | fw, ok := f.firmwares[*hw] 39 | if !ok { 40 | return nil, ErrUDI 41 | } 42 | 43 | return &fw, nil 44 | } 45 | 46 | func (f Firmwares) List() []string { 47 | list := []string{} 48 | for hw, fw := range f.firmwares { 49 | list = append(list, fmt.Sprintf("VendorID:0x%04x ProductID:%d ProductRev:%d [0x%s] with size:%d hash:%0x…", 50 | hw.VendorID, hw.ProductID, hw.ProductRev, hw.toUDI0BEhex(), fw.Size, fw.Hash[:16])) 51 | } 52 | 53 | return list 54 | } 55 | 56 | // NewFirmwares initialises all known firmwares. It returns a map of 57 | // all firmwares or an error. 58 | // 59 | // To add a new firmware to the database, use addFirmware() in this 60 | // function. 61 | func NewFirmwares() (Firmwares, error) { 62 | fws := Firmwares{ 63 | firmwares: make(map[hardware]Firmware), 64 | } 65 | 66 | var err error 67 | 68 | // This is the default/qemu UDI0, with firmware from main at 69 | // c126199a41149f6284aa9533e72395c978733b44 70 | err = fws.addFirmware("00010203", 0x0010, 8, 3, 4192, "3769540390ee3d990ea3f9e4cc9a0d1af5bcaebb82218185a78c39c6bf01d9cdc305ba253a1fb9f3f9fcc63d97c8e5f34bbb1f7bec56a8f246f1d2239867b623") 71 | if err != nil { 72 | return fws, err 73 | } 74 | 75 | err = fws.addFirmware("01337080", 0x1337, 2, 0, 4192, "3769540390ee3d990ea3f9e4cc9a0d1af5bcaebb82218185a78c39c6bf01d9cdc305ba253a1fb9f3f9fcc63d97c8e5f34bbb1f7bec56a8f246f1d2239867b623") 76 | if err != nil { 77 | return fws, err 78 | } 79 | 80 | err = fws.addFirmware("01337081", 0x1337, 2, 1, 4192, "3769540390ee3d990ea3f9e4cc9a0d1af5bcaebb82218185a78c39c6bf01d9cdc305ba253a1fb9f3f9fcc63d97c8e5f34bbb1f7bec56a8f246f1d2239867b623") 81 | if err != nil { 82 | return fws, err 83 | } 84 | 85 | err = fws.addFirmware("01337082", 0x1337, 2, 2, 4160, "06d0aafcc763307420380a8c5a324f3fccfbba6af7ff6fe0facad684ebd69dd43234c8531a096c77c2dc3543f8b8b629c94136ca7e257ca560da882e4dbbb025") 86 | if err != nil { 87 | return fws, err 88 | } 89 | 90 | if len(fws.firmwares) == 0 { 91 | return fws, MissingError{what: "no firmware"} 92 | } 93 | 94 | return fws, nil 95 | } 96 | 97 | // addFirmware adds a new known hardware identified by the triple 98 | // (vendorID, productID, productRev) with a known firmware size and 99 | // hash. To avoid mistakes, the hardware triple is used to recreate 100 | // the first UDI word (UDI0) which must then match the argument 101 | // udi0BEhex. For example, given the hardware triple argument (0x10, 102 | // 8, 3) the udi0BEhex argument must be "00010203" (this is the 103 | // default UDI0 in FPGA bitstream and QEMU machine). 104 | func (f *Firmwares) addFirmware(udi0BEhex string, vendorID uint16, productID uint8, productRev uint8, fwSize int, fwHashHex string) error { 105 | udi0BE, err := hex.DecodeString(udi0BEhex) 106 | if err != nil { 107 | return ParseError{what: "UDI", err: err} 108 | } 109 | if l := len(udi0BE); l != tkey.UDISize/2 { 110 | return ErrWrongLen 111 | } 112 | 113 | hw, err := newHardware(vendorID, productID, productRev) 114 | if err != nil { 115 | return err 116 | } 117 | 118 | if fwSize < fwSizeMin { 119 | return ErrWrongLen 120 | } 121 | if fwSize > fwSizeMax { 122 | return ErrWrongLen 123 | } 124 | 125 | fwHash, err := hex.DecodeString(fwHashHex) 126 | if err != nil { 127 | return ParseError{what: "firmware hash", err: err} 128 | } 129 | if l := len(fwHash); l != sha512.Size { 130 | return ErrWrongLen 131 | } 132 | 133 | // Safety check. We compare the passed UDI0 argument to what 134 | // we computed from vendor ID, product ID, and product 135 | // revision. If it's not the same, we bail. 136 | if udi0BEhex != hw.toUDI0BEhex() { 137 | return EqualError{one: "udi0BEhex arg", two: "calculated"} 138 | } 139 | 140 | if _, ok := f.firmwares[*hw]; ok { 141 | return ExistError{what: "hardware with same UDI0"} 142 | } 143 | 144 | f.firmwares[*hw] = Firmware{ 145 | Hash: *(*[64]byte)(fwHash), 146 | Size: fwSize, 147 | } 148 | 149 | return nil 150 | } 151 | 152 | type hardware struct { 153 | VendorID uint16 154 | ProductID uint8 // 6 bits 155 | ProductRev uint8 // 6 bits 156 | } 157 | 158 | // newHardware is a utility function to generate a hardware struct 159 | // from a UDI we probably got from talking to a TKey. 160 | func newHardware(vendorID uint16, productID uint8, productRev uint8) (*hardware, error) { 161 | const sixBitsMax = 2*2*2*2*2*2 - 1 162 | 163 | if productID > sixBitsMax { 164 | return nil, RangeError{what: "product ID"} 165 | } 166 | if productRev > sixBitsMax { 167 | return nil, RangeError{what: "product revision"} 168 | } 169 | 170 | return &hardware{ 171 | VendorID: vendorID, 172 | ProductID: productID, 173 | ProductRev: productRev, 174 | }, nil 175 | } 176 | 177 | func (h hardware) toUDI0BEhex() string { 178 | var udi0BE [4]byte 179 | var vidBE [2]byte 180 | 181 | binary.BigEndian.PutUint16(vidBE[:], h.VendorID) 182 | // 4 reserved bits | first 4 bits of 1st vendorID byte 183 | udi0BE[0] = 0x0 | ((vidBE[0] & 0b11110000) >> 4) 184 | // last 4 bits of 1st vendorID byte | first 4 bits of 2nd vendorID byte 185 | udi0BE[1] = ((vidBE[0] & 0b00001111) << 4) | ((vidBE[1] & 0b11110000) >> 4) 186 | // last 4 bits of 2nd vendorID byte | first 4 bits of productID 187 | udi0BE[2] = ((vidBE[1] & 0b00001111) << 4) | ((h.ProductID & 0b111100) >> 2) 188 | // last 2 bits of productID | 6 bits productRev 189 | udi0BE[3] = ((h.ProductID & 0b000011) << 6) | h.ProductRev 190 | 191 | return hex.EncodeToString(udi0BE[:]) 192 | } 193 | -------------------------------------------------------------------------------- /cmd/tkey-verification/main.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 Tillitis AB 2 | // SPDX-License-Identifier: BSD-2-Clause 3 | 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | "log" 9 | "os" 10 | "strings" 11 | 12 | "github.com/spf13/pflag" 13 | "github.com/tillitis/tkeyclient" 14 | ) 15 | 16 | const progname = "tkey-verification" 17 | 18 | const signaturesDir = "signatures" 19 | 20 | const ( 21 | defaultBaseURL = "https://tkey.tillitis.se/verify" 22 | defaultConfigFile = "./tkey-verification.yaml" 23 | ) 24 | 25 | var version string 26 | 27 | // Use when printing err/diag msgs 28 | var le = log.New(os.Stderr, "", 0) 29 | 30 | type Device struct { 31 | Path string 32 | Speed int 33 | } 34 | 35 | func main() { 36 | var dev Device 37 | var baseURL, baseDir, configFile, binPath string 38 | var checkConfigOnly, verbose, showURLOnly, versionOnly, build, helpOnly bool 39 | 40 | if version == "" { 41 | version = readBuildInfo() 42 | } 43 | 44 | pflag.CommandLine.SetOutput(os.Stderr) 45 | pflag.CommandLine.SortFlags = false 46 | pflag.StringVar(&dev.Path, "port", "", 47 | "Set serial port device `PATH`. If this is not passed, auto-detection will be attempted.") 48 | pflag.IntVarP(&dev.Speed, "speed", "s", tkeyclient.SerialSpeed, 49 | "Set serial port `speed` in bits per second.") 50 | pflag.BoolVar(&verbose, "verbose", false, 51 | "Enable verbose output.") 52 | pflag.StringVar(&configFile, "config", defaultConfigFile, 53 | "`PATH` to configuration file (commands: serve-signer, remote-sign).") 54 | pflag.BoolVar(&checkConfigOnly, "check-config", false, 55 | "Only check that the configuration is usable, then exit (commands: serve-signer, remote-sign).") 56 | pflag.BoolVarP(&showURLOnly, "show-url", "u", false, 57 | "Only output the URL to the verification data that should be downloaded (command: verify).") 58 | pflag.StringVarP(&baseDir, "base-dir", "d", "", 59 | "Read verification data from a file located in `DIRECTORY` and named after the TKey UDI in hex, instead of from a URL. You can for example first use \"verify --show-url\" and download the verification file manually on some other computer, then transfer the file back and use \"verify --base-dir .\" (command: verify).") 60 | pflag.StringVar(&baseURL, "base-url", defaultBaseURL, 61 | "Set the base `URL` of verification server for fetching verification data (command: verify).") 62 | pflag.StringVarP(&binPath, "app", "a", "", 63 | "`PATH` to the device app to show vendor signing pubkey (command: show-pubkey).") 64 | pflag.BoolVar(&versionOnly, "version", false, "Output version information.") 65 | pflag.BoolVar(&build, "build", false, "Output build data about included device apps and firmwares") 66 | pflag.BoolVar(&helpOnly, "help", false, "Output this help.") 67 | pflag.Usage = usage 68 | pflag.Parse() 69 | 70 | if helpOnly { 71 | pflag.Usage() 72 | os.Exit(0) 73 | } 74 | if versionOnly { 75 | fmt.Printf("%s %s\n", progname, version) 76 | os.Exit(0) 77 | } 78 | 79 | if build { 80 | builtWith() 81 | os.Exit(0) 82 | } 83 | 84 | if pflag.NArg() != 1 { 85 | if pflag.NArg() > 1 { 86 | le.Printf("Unexpected argument: %s\n\n", strings.Join(pflag.Args()[1:], " ")) 87 | } else { 88 | le.Printf("Please pass a command: serve-signer, remote-sign, or verify\n\n") 89 | } 90 | pflag.Usage() 91 | os.Exit(2) 92 | } 93 | 94 | cmd := pflag.Args()[0] 95 | 96 | // Use Lookup to tell if user changed string with default value 97 | if cmd == "verify" && (pflag.CommandLine.Lookup("config").Changed || checkConfigOnly) { 98 | le.Printf("Cannot use --config/--check-config with this command.\n") 99 | os.Exit(2) 100 | } 101 | if cmd != "verify" && (showURLOnly || baseDir != "" || pflag.CommandLine.Lookup("base-url").Changed) { 102 | le.Printf("Cannot use --show-url/--base-dir/--base-url with this command.\n") 103 | os.Exit(2) 104 | } 105 | 106 | // Command funcs exit to OS themselves for now 107 | switch cmd { 108 | case "serve-signer": 109 | conf, err := loadServeSignerConfig(configFile) 110 | if err != nil { 111 | le.Printf("Couldn't load config: %v\n", err) 112 | } 113 | 114 | serveSigner(conf, dev, verbose, checkConfigOnly) 115 | 116 | case "remote-sign": 117 | conf, err := loadRemoteSignConfig(configFile) 118 | if err != nil { 119 | le.Printf("Couldn't load config: %v\n", err) 120 | } 121 | 122 | if checkConfigOnly { 123 | os.Exit(0) 124 | } 125 | 126 | remoteSign(conf, dev, verbose) 127 | 128 | case "verify": 129 | if baseDir != "" && (showURLOnly || pflag.CommandLine.Lookup("base-url").Changed) { 130 | le.Printf("Cannot combine --base-dir and --show-url/--base-url\n") 131 | os.Exit(2) 132 | } 133 | 134 | verify(dev, verbose, showURLOnly, baseDir, baseURL) 135 | 136 | case "show-pubkey": 137 | if binPath == "" { 138 | le.Printf("Needs the path to an app, use `--app PATH`\n") 139 | os.Exit(2) 140 | } 141 | showPubkey(binPath, dev, verbose) 142 | 143 | default: 144 | le.Printf("%s is not a valid command.\n", cmd) 145 | pflag.Usage() 146 | os.Exit(2) 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /cmd/tkey-verification/remotesign.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Tillitis AB 2 | // SPDX-License-Identifier: BSD-2-Clause 3 | 4 | package main 5 | 6 | import ( 7 | "crypto/ed25519" 8 | "crypto/rand" 9 | "crypto/tls" 10 | "fmt" 11 | "net" 12 | "net/rpc" 13 | "os" 14 | 15 | "github.com/tillitis/tkey-verification/internal/tkey" 16 | ) 17 | 18 | func remoteSign(conf ProvConfig, dev Device, verbose bool) { 19 | _, _, err := net.SplitHostPort(conf.ServerAddr) 20 | if err != nil { 21 | le.Printf("SplitHostPort failed: %s", err) 22 | os.Exit(1) 23 | } 24 | 25 | server := Server{ 26 | Addr: conf.ServerAddr, 27 | TLSConfig: tls.Config{ 28 | Certificates: []tls.Certificate{ 29 | loadCert(conf.ClientCert, conf.ClientKey), 30 | }, 31 | RootCAs: loadCA(conf.CACert), 32 | MinVersion: tls.VersionTLS13, 33 | }, 34 | } 35 | 36 | appBin, udi, pubKey, fw, err := signChallenge(conf, dev, verbose) 37 | if err != nil { 38 | le.Printf("Couldn't sign challenge: %s\n", err) 39 | os.Exit(1) 40 | } 41 | 42 | err = vendorSign(&server, udi.Bytes, pubKey, fw, appBin) 43 | if err != nil { 44 | le.Printf("Couldn't get a vendor signature: %s\n", err) 45 | os.Exit(1) 46 | } 47 | 48 | le.Printf("Remote Sign was successful\n") 49 | } 50 | 51 | // Returns the currently used device app, UDI, pubkey, expected 52 | // firmware, and any error 53 | func signChallenge(conf ProvConfig, dev Device, verbose bool) (AppBin, *tkey.UDI, []byte, Firmware, error) { 54 | appBins, err := NewAppBins() 55 | if err != nil { 56 | fmt.Printf("Failed to init embedded device apps: %v\n", err) 57 | os.Exit(1) 58 | } 59 | 60 | // Do we have the configured device app to use for device signature? 61 | var appBin AppBin 62 | 63 | if aBin, ok := appBins.Bins[conf.SigningAppHash]; ok { 64 | appBin = aBin 65 | } else { 66 | fmt.Printf("Compiled in device signing app corresponding to hash %v (signingapphash) not found\n", conf.SigningAppHash) 67 | os.Exit(1) 68 | } 69 | 70 | firmwares, err := NewFirmwares() 71 | if err != nil { 72 | le.Printf("Found no usable firmwares\n") 73 | os.Exit(1) 74 | } 75 | 76 | var fw Firmware 77 | tk, err := tkey.NewTKey(dev.Path, dev.Speed, verbose) 78 | if err != nil { 79 | return appBin, nil, nil, fw, fmt.Errorf("%w", err) 80 | } 81 | 82 | defer tk.Close() 83 | 84 | le.Printf("Loading device app built from %s ...\n", appBin.String()) 85 | pubKey, err := tk.LoadSigner(appBin.Bin) 86 | if err != nil { 87 | return appBin, nil, nil, fw, fmt.Errorf("%w", err) 88 | } 89 | le.Printf("TKey UDI: %s\n", tk.Udi.String()) 90 | 91 | expectfw, err := firmwares.GetFirmware(tk.Udi) 92 | if err != nil { 93 | return appBin, nil, nil, fw, MissingError{what: "couldn't find firmware for UDI"} 94 | } 95 | 96 | fw, err = verifyFirmwareHash(*expectfw, *tk) 97 | if err != nil { 98 | return appBin, nil, nil, fw, fmt.Errorf("%w", err) 99 | } 100 | le.Printf("TKey firmware with size:%d and verified hash:%0x…\n", fw.Size, fw.Hash[:16]) 101 | 102 | // Locally generate a challenge and sign it 103 | challenge := make([]byte, 32) 104 | if _, err = rand.Read(challenge); err != nil { 105 | return appBin, nil, nil, fw, fmt.Errorf("%w", err) 106 | } 107 | 108 | signature, err := tk.Sign(challenge) 109 | if err != nil { 110 | return appBin, nil, nil, fw, fmt.Errorf("%w", err) 111 | } 112 | 113 | fmt.Printf("signature: %x\n", signature) 114 | 115 | // Verify the signature against the extracted public key 116 | if !ed25519.Verify(pubKey, challenge, signature) { 117 | return appBin, nil, nil, fw, ErrVerificationFailed 118 | } 119 | 120 | return appBin, &tk.Udi, pubKey, fw, nil 121 | } 122 | 123 | func vendorSign(server *Server, udi []byte, pubKey []byte, fw Firmware, appBin AppBin) error { 124 | conn, err := tls.Dial("tcp", server.Addr, &server.TLSConfig) 125 | if err != nil { 126 | return fmt.Errorf("%w", err) 127 | } 128 | 129 | client := rpc.NewClient(conn) 130 | 131 | msg, err := buildMessage(udi, fw.Hash[:], pubKey) 132 | if err != nil { 133 | return fmt.Errorf("building message to sign failed: %w", err) 134 | } 135 | 136 | args := Args{ 137 | UDIBE: udi, 138 | AppTag: appBin.Tag, 139 | AppHash: appBin.Hash(), 140 | Message: msg, 141 | } 142 | 143 | err = client.Call("API.Sign", &args, nil) 144 | if err != nil { 145 | return fmt.Errorf("%w", err) 146 | } 147 | 148 | return nil 149 | } 150 | -------------------------------------------------------------------------------- /cmd/tkey-verification/servesigner.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Tillitis AB 2 | // SPDX-License-Identifier: BSD-2-Clause 3 | 4 | package main 5 | 6 | import ( 7 | "bytes" 8 | "crypto/tls" 9 | "encoding/hex" 10 | "fmt" 11 | "net" 12 | "net/rpc" 13 | "os" 14 | 15 | "github.com/tillitis/tkey-verification/internal/tkey" 16 | ) 17 | 18 | type Verification struct { 19 | Timestamp string `json:"timestamp"` 20 | AppTag string `json:"apptag"` 21 | AppHash string `json:"apphash"` 22 | Signature string `json:"signature"` 23 | } 24 | 25 | func serveSigner(conf ServerConfig, dev Device, verbose bool, checkConfigOnly bool) { 26 | tlsConfig := tls.Config{ 27 | Certificates: []tls.Certificate{ 28 | loadCert(conf.ServerCert, conf.ServerKey), 29 | }, 30 | ClientCAs: loadCA(conf.CACert), 31 | ClientAuth: tls.RequireAndVerifyClientCert, 32 | MinVersion: tls.VersionTLS13, 33 | } 34 | 35 | _, _, err := net.SplitHostPort(conf.ListenAddr) 36 | if err != nil { 37 | le.Printf("Config listen: SplitHostPort failed: %s", err) 38 | os.Exit(1) 39 | } 40 | 41 | appBins, err := NewAppBins() 42 | if err != nil { 43 | fmt.Printf("Failed to init embedded device apps: %v\n", err) 44 | os.Exit(1) 45 | } 46 | 47 | vendorKeys, err := NewVendorKeys(appBins) 48 | if err != nil { 49 | le.Printf("Found no usable embedded vendor signing public key: %v\n", err) 50 | os.Exit(1) 51 | } 52 | 53 | // Do we have the configured pubkey to use for vendor signing? 54 | var vendorPubKey PubKey 55 | 56 | if pubkey, ok := vendorKeys.Keys[conf.VendorSigningAppHash]; ok { 57 | vendorPubKey = pubkey 58 | } else { 59 | fmt.Printf("Compiled in vendor key corresponding to signing app hash %v not found\n", conf.VendorSigningAppHash) 60 | os.Exit(1) 61 | } 62 | 63 | if checkConfigOnly { 64 | os.Exit(0) 65 | } 66 | 67 | tk, err := tkey.NewTKey(dev.Path, dev.Speed, verbose) 68 | if err != nil { 69 | le.Printf("Couldn't connect to TKey: %v\n", err) 70 | os.Exit(1) 71 | } 72 | 73 | exit := func(code int) { 74 | tk.Close() 75 | os.Exit(code) 76 | } 77 | 78 | le.Printf("Vendor signing: %s\n", vendorPubKey.String()) 79 | le.Printf("Loading device app built from %s ...\n", vendorPubKey.AppBin.String()) 80 | foundPubKey, err := tk.LoadSigner(vendorPubKey.AppBin.Bin) 81 | if err != nil { 82 | fmt.Printf("Couldn't load device app: %v\n", err) 83 | exit(1) 84 | } 85 | if bytes.Compare(vendorPubKey.PubKey[:], foundPubKey) != 0 { 86 | le.Printf("The public key of the found TKey (\"%s\") does not match the embedded vendor signing public key in use\n", hex.EncodeToString(foundPubKey)) 87 | exit(1) 88 | } 89 | le.Printf("Found signing TKey with the expected public key and UDI: %s\n", tk.Udi.String()) 90 | 91 | if err = os.MkdirAll(signaturesDir, 0o755); err != nil { 92 | le.Printf("MkdirAll failed: %s\n", err) 93 | exit(1) 94 | } 95 | 96 | if err = rpc.Register(NewAPI(vendorPubKey.PubKey[:], *tk)); err != nil { 97 | le.Printf("Register failed: %s\n", err) 98 | exit(1) 99 | } 100 | 101 | listener, err := tls.Listen("tcp", conf.ListenAddr, &tlsConfig) 102 | if err != nil { 103 | le.Printf("Listen failed: %s\n", err) 104 | exit(1) 105 | } 106 | 107 | le.Printf("Listening on %s...\n", conf.ListenAddr) 108 | for { 109 | conn, err := listener.Accept() 110 | if err != nil { 111 | le.Printf("Accept failed: %s\n", err) 112 | // Note: is this really fatal, should we exit? 113 | exit(1) 114 | } 115 | le.Printf("Client from %s\n", conn.RemoteAddr()) 116 | go func() { 117 | defer conn.Close() 118 | rpc.ServeConn(conn) 119 | le.Printf("Closed %s\n", conn.RemoteAddr()) 120 | }() 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /cmd/tkey-verification/showpubkey.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Tillitis AB 2 | // SPDX-License-Identifier: BSD-2-Clause 3 | 4 | package main 5 | 6 | import ( 7 | "crypto/sha512" 8 | "encoding/hex" 9 | "fmt" 10 | "os" 11 | "path/filepath" 12 | "strings" 13 | 14 | "github.com/tillitis/tkey-verification/internal/tkey" 15 | ) 16 | 17 | func showPubkey(binPath string, dev Device, verbose bool) { 18 | tk, err := tkey.NewTKey(dev.Path, dev.Speed, verbose) 19 | if err != nil { 20 | le.Printf("Couldn't connect to TKey: %v\n", err) 21 | os.Exit(1) 22 | } 23 | 24 | exit := func(code int) { 25 | tk.Close() 26 | os.Exit(code) 27 | } 28 | 29 | content, err := os.ReadFile(binPath) 30 | if err != nil { 31 | le.Printf("ReadFile: %v", err) 32 | exit(1) 33 | } 34 | 35 | appHash := sha512.Sum512(content) 36 | 37 | pubKey, err := tk.LoadSigner(content) 38 | if err != nil { 39 | le.Printf("LoadSigner: %v\n", err) 40 | exit(1) 41 | } 42 | 43 | tag := strings.TrimSuffix(filepath.Base(binPath), ".bin") 44 | 45 | le.Printf("Public Key, app tag, and app hash for vendor-signing-pubkeys.txt follows on stdout:\n") 46 | fmt.Printf("%s %s %s\n", hex.EncodeToString(pubKey), tag, hex.EncodeToString(appHash[:])) 47 | 48 | exit(0) 49 | } 50 | -------------------------------------------------------------------------------- /cmd/tkey-verification/util.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Tillitis AB 2 | // SPDX-License-Identifier: BSD-2-Clause 3 | 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | "os" 9 | "runtime/debug" 10 | "strings" 11 | 12 | "github.com/spf13/pflag" 13 | ) 14 | 15 | func readBuildInfo() string { 16 | version := "devel without BuildInfo" 17 | if info, ok := debug.ReadBuildInfo(); ok { 18 | sb := strings.Builder{} 19 | sb.WriteString("devel") 20 | for _, setting := range info.Settings { 21 | if strings.HasPrefix(setting.Key, "vcs") { 22 | sb.WriteString(fmt.Sprintf(" %s=%s", setting.Key, setting.Value)) 23 | } 24 | } 25 | version = sb.String() 26 | } 27 | return version 28 | } 29 | 30 | func builtWith() { 31 | appBins, err := NewAppBins() 32 | if err != nil { 33 | fmt.Printf("Failed to init embedded device apps: %v\n", err) 34 | os.Exit(1) 35 | } 36 | 37 | vendorKeys, err := NewVendorKeys(appBins) 38 | if err != nil { 39 | le.Printf("Found no usable embedded vendor signing public key\n") 40 | os.Exit(1) 41 | } 42 | 43 | firmwares, err := NewFirmwares() 44 | if err != nil { 45 | le.Printf("Found no usable firmwares\n") 46 | os.Exit(1) 47 | } 48 | 49 | fmt.Printf(`Built with: 50 | Supported verisigner-app tags: 51 | %s 52 | Known vendor signing keys: 53 | %s 54 | Known firmwares: 55 | %s 56 | `, 57 | strings.Join(appBins.Tags(), " \n "), 58 | vendorKeys.String(), 59 | strings.Join(firmwares.List(), " \n ")) 60 | 61 | } 62 | 63 | func usage() { 64 | 65 | desc := fmt.Sprintf(`Usage: %s command [flags...] 66 | 67 | Commands: 68 | serve-signer Run the server that offers an API for creating vendor signatures. 69 | 70 | remote-sign Call the remote signing server to sign for a local TKey. 71 | 72 | verify Verify that a TKey is genuine by extracting the TKey UDI and using it 73 | to fetch the verification data, including tag and signature from the 74 | web. Then running the correct verisigner-app on the TKey, extracting 75 | the public key and verifying it using the vendor's signing public key. 76 | 77 | The flags --show-url and --base-dir can be used to show the URL for 78 | downloading the verification data on one machine, and verifying the 79 | TKey on another machine that lacks network, see more below. 80 | 81 | show-pubkey Prints the info needed for the vendor-signing-pubkeys.txt to stdout. 82 | This includes public key, app tag, and app hash in the right format. 83 | 84 | Use the flag --app to specify the path o the desired app to use, i.e., 85 | tkey-verification show-pubkey --app /path/to/app`, progname) 86 | 87 | le.Printf("%s\n\nFlags:\n%s\n", desc, pflag.CommandLine.FlagUsagesWrapped(86)) 88 | } 89 | -------------------------------------------------------------------------------- /cmd/tkey-verification/vendor-signing-pubkeys.txt: -------------------------------------------------------------------------------- 1 | 14274d3570097aea209af1c23b64aa439a4d0d32c62735c5f6d6a29600c9a275 verisigner-v0.0.3 f8ecdcda53a296636a0297c250b27fb649860645626cc8ad935eabb4c43ea3e1841c40300544fade4189aa4143c1ca8fe82361e3d874b42b0e2404793a170142 2 | -------------------------------------------------------------------------------- /cmd/tkey-verification/vendorpubkeys.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Tillitis AB 2 | // SPDX-License-Identifier: BSD-2-Clause 3 | 4 | package main 5 | 6 | import ( 7 | "bytes" 8 | "crypto/ed25519" 9 | "crypto/sha512" 10 | _ "embed" 11 | "encoding/hex" 12 | "fmt" 13 | "strings" 14 | ) 15 | 16 | // nolint:typecheck // Avoid lint error when the embedding file is missing. 17 | // 18 | //go:embed vendor-signing-pubkeys.txt 19 | var pubKeysData []byte 20 | 21 | type PubKey struct { 22 | PubKey [ed25519.PublicKeySize]byte // Vendor public key 23 | Tag string // Name and tag of the device app 24 | AppBin AppBin // The actual device app binary 25 | } 26 | 27 | func (p *PubKey) String() string { 28 | return fmt.Sprintf("pubkey:%0x… %s", p.PubKey[:16], p.AppBin.String()) 29 | } 30 | 31 | // VendorKeys is a built-in database of vendor PubKeys 32 | type VendorKeys struct { 33 | Keys map[string]PubKey 34 | } 35 | 36 | func (v *VendorKeys) String() string { 37 | var sb strings.Builder 38 | 39 | for _, k := range v.Keys { 40 | sb.WriteString(k.String()) 41 | } 42 | 43 | return sb.String() 44 | } 45 | 46 | // NewVendorKeys initializes all the known vendor public keys. It 47 | // needs to know the existing device applications (get them with 48 | // NewAppBins()) 49 | // 50 | // It returns the vendor public keys and any error. 51 | func NewVendorKeys(appBins AppBins) (VendorKeys, error) { 52 | lines := strings.Split(strings.Trim(strings.ReplaceAll(string(pubKeysData), "\r\n", "\n"), "\n"), "\n") 53 | 54 | var vendorKeys = VendorKeys{ 55 | map[string]PubKey{}, 56 | } 57 | 58 | for _, line := range lines { 59 | fields := strings.Fields(line) 60 | 61 | if len(fields) == 0 || strings.HasPrefix(fields[0], "#") { 62 | // ignoring empty/spaces-only lines and comments 63 | continue 64 | } 65 | 66 | if len(fields) != 3 { 67 | return vendorKeys, SimpleParseError{msg: "Expected 3 space-separated fields: pubkey in hex, signer-app tag, and its hash in hex"} 68 | } 69 | 70 | pubKeyHex, tag, appHashHex := fields[0], fields[1], fields[2] 71 | 72 | pubKey, err := hex.DecodeString(pubKeyHex) 73 | if err != nil { 74 | return vendorKeys, ParseError{what: "public key hex", err: err} 75 | } 76 | if l := len(pubKey); l != ed25519.PublicKeySize { 77 | return vendorKeys, ErrWrongLen 78 | } 79 | 80 | appHash, err := hex.DecodeString(appHashHex) 81 | if err != nil { 82 | return vendorKeys, ParseError{what: "app digest hex", err: err} 83 | } 84 | if l := len(appHash); l != sha512.Size { 85 | return vendorKeys, ErrWrongLen 86 | } 87 | 88 | var appBin AppBin 89 | if _, ok := appBins.Bins[appHashHex]; ok { 90 | appBin = appBins.Bins[appHashHex] 91 | if appBin.Tag != tag { 92 | return vendorKeys, EqualError{"embedded app tag", "vendor signing app tag"} 93 | } 94 | 95 | } else { 96 | return vendorKeys, ErrNotFound 97 | } 98 | 99 | for _, pk := range vendorKeys.Keys { 100 | if bytes.Compare(pubKey, pk.PubKey[:]) == 0 { 101 | return vendorKeys, ExistError{what: "public key"} 102 | } 103 | } 104 | 105 | vendorKeys.Keys[appHashHex] = PubKey{ 106 | PubKey: *(*[ed25519.PublicKeySize]byte)(pubKey), 107 | Tag: tag, 108 | AppBin: appBin, 109 | } 110 | } 111 | 112 | return vendorKeys, nil 113 | } 114 | -------------------------------------------------------------------------------- /cmd/tkey-verification/verify.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 Tillitis AB 2 | // SPDX-License-Identifier: BSD-2-Clause 3 | 4 | package main 5 | 6 | import ( 7 | "crypto/ed25519" 8 | "crypto/rand" 9 | "encoding/hex" 10 | "encoding/json" 11 | "fmt" 12 | "io" 13 | "net/http" 14 | "os" 15 | "path" 16 | "time" 17 | 18 | "github.com/tillitis/tkey-verification/internal/tkey" 19 | ) 20 | 21 | const verifyInfoURL = "https://www.tillitis.se/verify" 22 | 23 | func verify(dev Device, verbose bool, showURLOnly bool, baseDir string, verifyBaseURL string) { 24 | appBins, err := NewAppBins() 25 | if err != nil { 26 | missing(fmt.Sprintf("no embedded device apps: %v", err)) 27 | os.Exit(1) 28 | } 29 | 30 | vendorKeys, err := NewVendorKeys(appBins) 31 | if err != nil { 32 | missing(fmt.Sprintf("no vendor signing public key: %v", err)) 33 | os.Exit(1) 34 | } 35 | 36 | firmwares, err := NewFirmwares() 37 | if err != nil { 38 | missing("no firmware digests") 39 | os.Exit(1) 40 | } 41 | 42 | tk, err := tkey.NewTKey(dev.Path, dev.Speed, verbose) 43 | if err != nil { 44 | commFailed(err.Error()) 45 | os.Exit(1) 46 | } 47 | 48 | exit := func(code int) { 49 | tk.Close() 50 | os.Exit(code) 51 | } 52 | 53 | le.Printf("TKey UDI: %s\n", tk.Udi.String()) 54 | 55 | verifyURL := fmt.Sprintf("%s/%s", verifyBaseURL, hex.EncodeToString(tk.Udi.Bytes)) 56 | 57 | if showURLOnly { 58 | le.Printf("URL to verification data follows on stdout:\n") 59 | fmt.Printf("%s\n", verifyURL) 60 | exit(0) 61 | } 62 | 63 | var verification Verification 64 | 65 | if baseDir != "" { 66 | p := path.Join(baseDir, hex.EncodeToString(tk.Udi.Bytes)) 67 | verification, err = verificationFromFile(p) 68 | if err != nil { 69 | commFailed(err.Error()) 70 | exit(1) 71 | } 72 | } else { 73 | if verbose { 74 | le.Printf("Fetching verification data from %s ...\n", verifyURL) 75 | } 76 | 77 | verification, err = verificationFromURL(verifyURL) 78 | if err != nil { 79 | commFailed(err.Error()) 80 | exit(1) 81 | } 82 | } 83 | 84 | if verbose { 85 | le.Printf("Verification data was created %s\n", verification.Timestamp) 86 | } 87 | 88 | if verification.AppTag == "" { 89 | parseFailure("app tag empty") 90 | exit(1) 91 | } 92 | 93 | _, err = hex.DecodeString(verification.AppHash) 94 | if err != nil { 95 | parseFailure("hex decode error") 96 | exit(1) 97 | } 98 | 99 | appBin, err := appBins.Get(verification.AppHash) 100 | if err != nil { 101 | notFound("upstream app digest unknown") 102 | exit(1) 103 | } 104 | 105 | pubKey, err := tk.LoadSigner(appBin.Bin) 106 | if err != nil { 107 | commFailed(err.Error()) 108 | exit(1) 109 | } 110 | 111 | expectedFw, err := firmwares.GetFirmware(tk.Udi) 112 | if err != nil { 113 | notFound("no known firmware for UDI") 114 | exit(1) 115 | } 116 | 117 | fw, err := verifyFirmwareHash(*expectedFw, *tk) 118 | if err != nil { 119 | verificationFailed("unexpected firmware") 120 | exit(1) 121 | } 122 | if verbose { 123 | le.Printf("TKey firmware was verified, size:%d hash:%0x…\n", fw.Size, fw.Hash[:16]) 124 | } 125 | 126 | vSignature, err := hex.DecodeString(verification.Signature) 127 | if err != nil { 128 | parseFailure(err.Error()) 129 | exit(1) 130 | } 131 | 132 | // Verify vendor's signature over known message. 133 | msg, err := buildMessage(tk.Udi.Bytes, fw.Hash[:], pubKey) 134 | if err != nil { 135 | parseFailure(err.Error()) 136 | exit(1) 137 | } 138 | 139 | // We allow for any of the known vendor keys. 140 | var verified = false 141 | 142 | for _, vendorPubKey := range vendorKeys.Keys { 143 | if ed25519.Verify(vendorPubKey.PubKey[:], msg, vSignature) { 144 | le.Printf("Verified with vendor key %x\n", vendorPubKey.PubKey) 145 | verified = true 146 | break 147 | } 148 | } 149 | 150 | if !verified { 151 | verificationFailed("vendor signature not verified") 152 | exit(1) 153 | } 154 | 155 | // Get a device signature over a random challenge 156 | challenge := make([]byte, 32) 157 | if _, err = rand.Read(challenge); err != nil { 158 | le.Printf("rand.Read failed: %s", err) 159 | exit(1) 160 | } 161 | 162 | signature, err := tk.Sign(challenge) 163 | if err != nil { 164 | commFailed(err.Error()) 165 | exit(1) 166 | } 167 | 168 | // Verify device signature against device public key 169 | if !ed25519.Verify(pubKey, challenge, signature) { 170 | verificationFailed("challenge not verified") 171 | exit(1) 172 | } 173 | 174 | fmt.Printf("TKey is genuine!\n") 175 | 176 | exit(0) 177 | } 178 | 179 | // commFailed describes an I/O failure of some kind, perhaps between 180 | // the client and the TKey, an HTTP request that didn't succeed, or 181 | // perhaps reading a file. 182 | func commFailed(msg string) { 183 | fmt.Printf("I/O FAILED: %s\n", msg) 184 | } 185 | 186 | // parseFailure describes an error where we have tried to parse 187 | // something from external sources but failed. 188 | func parseFailure(msg string) { 189 | fmt.Printf("PARSE ERROR: %s\n", msg) 190 | } 191 | 192 | // missing describes an error where something is missing from the 193 | // binary to even complete a verification. 194 | func missing(msg string) { 195 | fmt.Printf("MISSING IN PROGRAM: %s\n", msg) 196 | fmt.Printf("It seems tkey-verification is not built correctly.\n") 197 | } 198 | 199 | // notFound describes an error where we with data from external source 200 | // can't find something, perhaps not finding something on a web 201 | // server, or not finding the device app digest. 202 | func notFound(msg string) { 203 | fmt.Printf("NOT FOUND: %s\n", msg) 204 | } 205 | 206 | // verificationFailed describes a real problem with a manipulated 207 | // TKey. 208 | func verificationFailed(msg string) { 209 | fmt.Printf("VERIFICATION FAILED: %s\n", msg) 210 | fmt.Printf("Please visit %s to understand what this might mean.\n", verifyInfoURL) 211 | } 212 | 213 | func verificationFromURL(verifyURL string) (Verification, error) { 214 | var verification Verification 215 | 216 | client := http.Client{Timeout: 10 * time.Second} 217 | resp, err := client.Get(verifyURL) // #nosec G107 218 | if err != nil { 219 | return verification, IOError{path: verifyURL, err: err} 220 | } 221 | defer resp.Body.Close() 222 | 223 | if resp.StatusCode != http.StatusOK { 224 | le.Printf("HTTP GET status code: %d (%s)", resp.StatusCode, resp.Status) 225 | return verification, ErrIO 226 | } 227 | 228 | verificationJSON, err := io.ReadAll(resp.Body) 229 | if err != nil { 230 | return verification, IOError{path: verifyURL, err: err} 231 | } 232 | 233 | if err = json.Unmarshal(verificationJSON, &verification); err != nil { 234 | return verification, ParseError{what: "JSON Unmarshal", err: err} 235 | } 236 | 237 | return verification, nil 238 | } 239 | 240 | func verificationFromFile(fn string) (Verification, error) { 241 | var verification Verification 242 | 243 | le.Printf("Reading verification data from file %s ...\n", fn) 244 | verificationJSON, err := os.ReadFile(fn) 245 | if err != nil { 246 | return verification, IOError{path: fn, err: err} 247 | } 248 | 249 | if err = json.Unmarshal(verificationJSON, &verification); err != nil { 250 | return verification, ParseError{what: "JSON unmarshal", err: err} 251 | } 252 | 253 | return verification, nil 254 | } 255 | -------------------------------------------------------------------------------- /doc/implementation-notes.md: -------------------------------------------------------------------------------- 1 | # Implementation notes 2 | 3 | ## Assets 4 | 5 | There are some compiled-in assets: 6 | 7 | - application binaries: The device apps used for both vendor signing 8 | and device signatures. 9 | - vendor public keys: The public key extracted from a vendor TKey 10 | during vendor signing and used for verification. Might be several, 11 | including historical keys no longer being used for vendor signing. 12 | - known firmwares: Expected firmwares for all provisioned TKeys. 13 | 14 | Commands are responsible for initializing their own assets. For 15 | instance, `verify` needs all three, but `remote-sign` and 16 | `serve-signer` need only two, and `show-pubkey` needs none. 17 | 18 | ### Application binaries 19 | 20 | Initialized by calling `appbins.go:NewAppBins()`. 21 | 22 | Actual binaries are located in the `bins` directory stored like this: 23 | 24 | - `name-tag`, like `signer-v1.0.1.bin`: the actual device app binary. 25 | - `name-tag.sha512`, like `signer-v1.0.1.bin.sha512`: the SHA-512 26 | digest of the above file to help ensure we don't make mistakes. 27 | 28 | ### Vendor public keys 29 | 30 | Initialized by calling `vendorpubkeys.go:NewVendorKeys()`. Needs to 31 | know the appbins (see above). 32 | 33 | The actual vendor keys are defined in the text file 34 | `vendor-signing-pubkeys.txt`, which is embedded in the binary at build 35 | time and parsed at start. It contains: 36 | 37 | - the public key 38 | - the name and tag of the device app to use for vendor signing. 39 | - the hash digest of that device app. 40 | 41 | Example content: 42 | 43 | ``` 44 | 50d9a125f51d85ffa1fb12011bdae05d39e03cda2a35d0daf3077072daabbb10 verisigner-v0.0.3 f8ecdcda53a296636a0297c250b27fb649860645626cc8ad935eabb4c43ea3e1841c40300544fade4189aa4143c1ca8fe82361e3d874b42b0e2404793a170142 45 | ``` 46 | 47 | In use, it's initialized like this: 48 | 49 | ```go 50 | appBins, err := NewAppBins() 51 | vendorKeys, err := NewVendorKeys() 52 | ``` 53 | 54 | ### Known firmwares 55 | 56 | Initialized by calling `firmwares.go:NewFirmwares()`. 57 | 58 | It's used by probing a TKey for UDI, then looking up a firmware for a 59 | specific UDI with `GetFirmware(udi)`. Now you know the size and the 60 | SHA-512 digest of the expected firmware and call the device app to ask 61 | it for a digest. 62 | 63 | ## `serve-signer` command 64 | 65 | Defined in `servesigner.go`. 66 | 67 | - Initialises its assets using `NewAppBins()` and `NewVendorKeys()`. 68 | 69 | - Connects to a vendor TKey, loads the built-in vendor signing device 70 | app indicated by the configuration in `vendorapphash`. 71 | 72 | - Sets up a HTTPS server exposing a simple API through "net/rpc". The 73 | RPC commands are in `api.go`. 74 | 75 | - Waits for commands. 76 | 77 | - When a `Sign` command appears it requests the device app to sign the 78 | message and returns an OK or error. It writes the signature over the 79 | message and some metadata under the signatures directory. Note that 80 | it doesn't store the message itself. A verifier needs to recreate 81 | the message themselves from their TKey. 82 | 83 | - Goes back to wait for more commands. 84 | 85 | ## `remote-sign` command 86 | 87 | Defined in `remotesign.go`. 88 | 89 | - Sets up its assets using `NewAppBins()` and `NewFirmwares()`. 90 | 91 | - Connects to a TKey during provisioning and loads built-in device app 92 | indicated by the configuration in `signingapphash`. 93 | 94 | - Extracts the public key. 95 | 96 | - Verifies that firmware digest is as expected. 97 | 98 | - Verifies that the TKey can sign a random challenge, proving that it 99 | has the corresponding private key. 100 | 101 | - Builds a message to be signed. 102 | 103 | - Asks the server (same program but started with `serve-signer`) to 104 | sign the message. 105 | 106 | ## `verify` command 107 | 108 | Defined in `verify.go`. 109 | 110 | - Initialises its assets by using `NewAppBins()`, `NewVendorKeys()`, 111 | and `NewFirmwares()`. 112 | 113 | - Connects to a TKey, request UDI. 114 | 115 | - If this is `verify --show-url` we just construct the URL from the 116 | UDI, outputs that and exits. 117 | 118 | - Otherwise, we fetch the verification data from something like 119 | `https://tkey.tillitis.se/verify/0133708100000002`. 120 | 121 | - Look up the necessary device app to load from our embedded assets 122 | and load it. 123 | 124 | - Extract the public key. 125 | 126 | - Look up the expected firmware hash digest from the UDI. 127 | 128 | - Compare the firmware digest of the TKey compared to the expected. 129 | 130 | - Recreate the message seen during provisioning. 131 | 132 | - Verify vendor's signature over the message against any of the 133 | vendor's known public keys. 134 | 135 | - Sign and verify a random challenge, proving that the TKey knows the 136 | corresponding private key. 137 | 138 | ## `show-pubkey` command 139 | 140 | Defined in `showpubkey.go`. 141 | 142 | - Connects to a TKey. 143 | 144 | - Reads the device app file, on the path passed to it in `--app`. 145 | 146 | - Computes the SHA-512 digest of the file. 147 | 148 | - Loads the app device. 149 | 150 | - Extracts the public key. 151 | 152 | - Prints the public key, the name-tag, and the app digest. 153 | -------------------------------------------------------------------------------- /doc/tkey-verification.1: -------------------------------------------------------------------------------- 1 | .\" Generated by scdoc 1.11.3 2 | .\" Complete documentation for this program is not available as a GNU info page 3 | .ie \n(.g .ds Aq \(aq 4 | .el .ds Aq ' 5 | .nh 6 | .ad l 7 | .\" Begin generated content: 8 | .TH "tkey-verification" "1" "2024-07-03" 9 | .PP 10 | .SH NAME 11 | .PP 12 | A program to sign or verify the identity of a Tillitis TKey.\& 13 | .PP 14 | .SH SYNOPSIS 15 | .PP 16 | \fBtkey-verification\fR -h/--help 17 | .PP 18 | \fBtkey-verification\fR remote-sign [--port port] [--speed speed] 19 | .PP 20 | \fBtkey-verification\fR serve-signer [--config path] [--check-config] [--port 21 | port] [--speed speed] 22 | .PP 23 | \fBtkey-verification\fR show-pubkey [--port port] [--speed speed] 24 | .PP 25 | \fBtkey-verification\fR verify [--base-url url] [-d | --base-dir] [--port 26 | port] [-u | --show-url] [--speed speed] 27 | .PP 28 | .SH DESCRIPTION 29 | .PP 30 | \fBtkey-verification\fR is a program to sign or verify the identity of a 31 | Tillitis TKey.\& 32 | .PP 33 | A typical end user will only be interested in the \fBverify\fR command.\& 34 | .PP 35 | The commands are as follows: 36 | .PP 37 | \fBremote-sign\fR 38 | .PP 39 | .RS 4 40 | Request that the tkey-verification serve-signer sign the identity 41 | of a TKey.\& 42 | .PP 43 | To use, first insert a TKey and then run the command.\& If one is 44 | already running a TKey program, remove it and re-insert before 45 | running the command.\& 46 | .PP 47 | Options: 48 | .PP 49 | \fB--port\fR port 50 | .PP 51 | .RS 4 52 | Path to the TKey device port.\& If not given, autodetection will be 53 | attempted.\& 54 | .PP 55 | .RE 56 | \fB--speed\fR speed 57 | .PP 58 | .RS 4 59 | Speed in bit/s of the TKey device port.\& 60 | .PP 61 | .RE 62 | .RE 63 | \fBserve-signer\fR 64 | .PP 65 | .RS 4 66 | Provide a signing server with its own TKey, the vendor key.\& 67 | .PP 68 | When it receives a signing request it signs the data and creates a new 69 | file with metadata and a signature.\& See FILES.\& 70 | .PP 71 | Options: 72 | .PP 73 | \fB--config\fR path 74 | .PP 75 | .RS 4 76 | Path to the configuration file.\& 77 | .PP 78 | .RE 79 | \fB--check-config\fR 80 | .PP 81 | .RS 4 82 | Check if the configuration file is OK and exit.\& 83 | .PP 84 | .RE 85 | \fB--port\fR port 86 | .PP 87 | .RS 4 88 | Path to the TKey device port.\& If not given, autodetection will be 89 | attempted.\& 90 | .PP 91 | .RE 92 | \fB--speed\fR speed 93 | .PP 94 | .RS 4 95 | Speed in bit/s of the TKey device port.\& 96 | .PP 97 | .RE 98 | .RE 99 | \fBshow-pubkey\fR 100 | .PP 101 | .RS 4 102 | Output public key data to populate "vendor-signing-pubkeys.\&txt" 103 | for building into \fBtkey-verification\fR to be able to use some other 104 | commands.\& This is necessary in order to bootstrap \fBtkey-verification\fR 105 | from scratch or to add another vendor signing key.\& 106 | .PP 107 | The output includes public key, app tag, and app hash in the 108 | right format for the file.\& 109 | .PP 110 | Use the \fB--app\fR to specify the path of the app to load.\& 111 | .PP 112 | Options: 113 | .PP 114 | \fB--app\fR path 115 | .PP 116 | .RS 4 117 | Load app in \fBpath\fR into TKey.\& 118 | .PP 119 | .RE 120 | \fB--port\fR port 121 | .PP 122 | .RS 4 123 | Path to the TKey device port.\& If not given, autodetection will be 124 | attempted.\& 125 | .PP 126 | .RE 127 | \fB--speed\fR speed 128 | .PP 129 | .RS 4 130 | Speed in bit/s of the TKey device port.\& 131 | .PP 132 | .RE 133 | .RE 134 | \fBverify\fR 135 | .PP 136 | .RS 4 137 | Verify a TKey identity.\& 138 | .PP 139 | To use, first insert a TKey and then run the command.\& If one is 140 | already running a TKey program, remove it and re-insert before running 141 | the command.\& 142 | .PP 143 | Options: 144 | .PP 145 | \fB--base-url\fR url 146 | .PP 147 | .RS 4 148 | Set the base URL of verification server for fetching verification 149 | data.\& Default is "https://example.\&com/verify".\& 150 | .PP 151 | .RE 152 | \fB-d\fR | \fB--base-dir\fR directory 153 | .PP 154 | .RS 4 155 | Read verification data from a file located in directory 156 | and named after the TKey Unique Device Identifier in hex, instead of 157 | from a URL.\& 158 | .PP 159 | .RE 160 | \fB--port\fR port 161 | .PP 162 | .RS 4 163 | Path to the TKey device port.\& If not given, autodetection will be 164 | attempted.\& 165 | .PP 166 | .RE 167 | \fB-u\fR | \fB--show-url\fR 168 | .PP 169 | .RS 4 170 | Only output the URL to the verification data that should be 171 | downloaded, then exit.\& 172 | .PP 173 | .RE 174 | \fB--speed\fR speed 175 | .PP 176 | .RS 4 177 | Speed in bit/s of the TKey device port.\& 178 | .PP 179 | .RE 180 | .RE 181 | .SS Verification on a machine without network 182 | .PP 183 | If you'\&re on a machine without network and need to verify a TKey you 184 | can run 185 | .PP 186 | .nf 187 | .RS 4 188 | $ tkey-verification verify ---show-url 189 | .fi 190 | .RE 191 | .PP 192 | which will output the URL to the verification file.\& Download the file 193 | using another, networked, computer and somehow bring the file or type 194 | it in again on your airgapped computer.\& Then run: 195 | .PP 196 | .nf 197 | .RS 4 198 | tkey-verification verify -d=\&. 199 | .fi 200 | .RE 201 | .PP 202 | to read from the current directory.\& 203 | .PP 204 | .SH FILES 205 | .PP 206 | \fBtkey-verification\fR serve-signer produces a file which is named after 207 | the Unique Device Identifier (in hexadecimal) for every signature 208 | made.\& An example filename would be "signatures/0133704100000015".\& 209 | .PP 210 | The file contains: 211 | .PP 212 | .PD 0 213 | .IP \(bu 4 214 | timestamp: RFC3339 UTC timestamp when the signature was done.\& 215 | .IP \(bu 4 216 | tag: The Git tag of the signer program used on the device under 217 | verification, 218 | .IP \(bu 4 219 | signature: Vendor ed25519 signature of the device public key.\& Stored 220 | in hexadecimal.\& 221 | .PD 222 | .PP 223 | The files generated will later be published on a public web server.\& 224 | The publication is out of scope for the current program.\& 225 | .PP 226 | \fBtkey-verification\fR show-pubkey generates output for a file called 227 | "vendor-signing-pubkeys.\&txt" to be included in the build of a new 228 | \fBtkey-verification\fR.\& It'\&s output is: 229 | .PP 230 | .PD 0 231 | .IP \(bu 4 232 | Ed25519 public key 233 | .IP \(bu 4 234 | app name and tag.\& 235 | .IP \(bu 4 236 | digest of hash 237 | .PD 238 | .PP 239 | Example file: 240 | .PP 241 | .nf 242 | .RS 4 243 | 038dd0b898c601517a09cd249d3c4f2de8e9aab38c5fa02701ae29bb41a6d863 verisigner-v0\&.0\&.1 9598910ec9ebe2504a5f894de6f8e0677dc94c156c7bd6f7e805a35354b3c85daa4ca66ab93f4d75221b501def457b4cafc933c6cdcf16d1eb8ccba6cccf6630 244 | .fi 245 | .RE 246 | .PP 247 | .SH EXAMPLES 248 | .PP 249 | Verifying the identity of a Tillitis TKey using a networked computer.\& 250 | .PP 251 | .nf 252 | .RS 4 253 | $ tkey-verification verify 254 | TKey UDI: 0x0133708100000002(BE) VendorID: 0x1337 ProductID: 2 ProductRev: 1 255 | TKey is genuine! 256 | .fi 257 | .RE 258 | .PP 259 | Verifying the identity with a non-networked computer: First 260 | .PP 261 | .nf 262 | .RS 4 263 | $ tkey-verification --show-url 264 | .fi 265 | .RE 266 | .PP 267 | Then download the file and move it to your current working directory.\& 268 | Keep the name of the file intact since it'\&s named after the TKey 269 | Unique Device Identifier.\& and run: 270 | .PP 271 | .nf 272 | .RS 4 273 | $ tkey-verification verify -d=\&. 274 | TKey UDI: 0x0133708100000002(BE) VendorID: 0x1337 ProductID: 2 ProductRev: 1 275 | Reading verification data from file \&./0133708100000002 \&.\&.\&. 276 | TKey is genuine! 277 | .fi 278 | .RE 279 | .PP 280 | In order to include a new vendor signing key, use: 281 | .PP 282 | .nf 283 | .RS 4 284 | % \&./tkey-verification show-pubkey --port /dev/pts/12 --app cmd/tkey-verification/bins/signer-v1\&.0\&.0\&.bin 285 | Connecting to device on serial port /dev/pts/12 \&.\&.\&. 286 | Firmware name0:\&'tk1 \&' name1:\&'mkdf\&' version:5 287 | Public Key, app tag, and app hash for vendor-signing-pubkeys\&.txt follows on stdout: 288 | 038dd0b898c601517a09cd249d3c4f2de8e9aab38c5fa02701ae29bb41a6d863 verisigner-v0\&.0\&.1 9598910ec9ebe2504a5f894de6f8e0677dc94c156c7bd6f7e805a35354b3c85daa4ca66ab93f4d75221b501def457b4cafc933c6cdcf16d1eb8ccba6cccf6630 289 | .fi 290 | .RE 291 | .PP 292 | .SH AUTHORS 293 | .PP 294 | Tillitis AB, https://tillitis.\&se/ 295 | .PP 296 | .SH CAVEATS 297 | .PP 298 | You can currently not use several TKeys on the same computer at the 299 | same time, which means you can'\&t use \fBserve-signer\fR and the other 300 | commands on the same computer.\& 301 | .PP 302 | .SH SECURITY CONSIDERATIONS 303 | .PP 304 | \fBtkey-verification\fR only verifies the /identity/ of the TKey hasn'\&t 305 | changed since signing by the vendor.\& It might have been manipulated in 306 | other ways.\& 307 | .PP 308 | The device public key isn'\&t published in the files generated by the 309 | \fBserve-signer\fR but is retrievable by anyone with access to the device 310 | under verification.\& 311 | .PP 312 | You probably shouldn'\&t expose the computers running \fBserve-signer\fR or 313 | \fBremote-sign\fR on the Internet.\& 314 | -------------------------------------------------------------------------------- /doc/tkey-verification.scd: -------------------------------------------------------------------------------- 1 | tkey-verification(1) 2 | 3 | # NAME 4 | 5 | A program to sign or verify the identity of a Tillitis TKey. 6 | 7 | # SYNOPSIS 8 | 9 | *tkey-verification* -h/--help 10 | 11 | *tkey-verification* remote-sign [--port port] [--speed speed] 12 | 13 | *tkey-verification* serve-signer [--config path] [--check-config] [--port 14 | port] [--speed speed] 15 | 16 | *tkey-verification* show-pubkey [--port port] [--speed speed] 17 | 18 | *tkey-verification* verify [--base-url url] [-d | --base-dir] [--port 19 | port] [-u | --show-url] [--speed speed] 20 | 21 | # DESCRIPTION 22 | 23 | *tkey-verification* is a program to sign or verify the identity of a 24 | Tillitis TKey. 25 | 26 | A typical end user will only be interested in the *verify* command. 27 | 28 | The commands are as follows: 29 | 30 | *remote-sign* 31 | 32 | Request that the tkey-verification serve-signer sign the identity 33 | of a TKey. 34 | 35 | To use, first insert a TKey and then run the command. If one is 36 | already running a TKey program, remove it and re-insert before 37 | running the command. 38 | 39 | Options: 40 | 41 | *--port* port 42 | 43 | Path to the TKey device port. If not given, autodetection will be 44 | attempted. 45 | 46 | *--speed* speed 47 | 48 | Speed in bit/s of the TKey device port. 49 | 50 | *serve-signer* 51 | 52 | Provide a signing server with its own TKey, the vendor key. 53 | 54 | When it receives a signing request it signs the data and creates a new 55 | file with metadata and a signature. See FILES. 56 | 57 | Options: 58 | 59 | *--config* path 60 | 61 | Path to the configuration file. 62 | 63 | *--check-config* 64 | 65 | Check if the configuration file is OK and exit. 66 | 67 | *--port* port 68 | 69 | Path to the TKey device port. If not given, autodetection will be 70 | attempted. 71 | 72 | *--speed* speed 73 | 74 | Speed in bit/s of the TKey device port. 75 | 76 | *show-pubkey* 77 | 78 | Output public key data to populate "vendor-signing-pubkeys.txt" 79 | for building into *tkey-verification* to be able to use some other 80 | commands. This is necessary in order to bootstrap *tkey-verification* 81 | from scratch or to add another vendor signing key. 82 | 83 | The output includes public key, app tag, and app hash in the 84 | right format for the file. 85 | 86 | Use the *--app* to specify the path of the app to load. 87 | 88 | Options: 89 | 90 | *--app* path 91 | 92 | Load app in *path* into TKey. 93 | 94 | *--port* port 95 | 96 | Path to the TKey device port. If not given, autodetection will be 97 | attempted. 98 | 99 | *--speed* speed 100 | 101 | Speed in bit/s of the TKey device port. 102 | 103 | *verify* 104 | 105 | Verify a TKey identity. 106 | 107 | To use, first insert a TKey and then run the command. If one is 108 | already running a TKey program, remove it and re-insert before running 109 | the command. 110 | 111 | Options: 112 | 113 | *--base-url* url 114 | 115 | Set the base URL of verification server for fetching verification 116 | data. Default is "https://example.com/verify". 117 | 118 | *-d* | *--base-dir* directory 119 | 120 | Read verification data from a file located in directory 121 | and named after the TKey Unique Device Identifier in hex, instead of 122 | from a URL. 123 | 124 | *--port* port 125 | 126 | Path to the TKey device port. If not given, autodetection will be 127 | attempted. 128 | 129 | *-u* | *--show-url* 130 | 131 | Only output the URL to the verification data that should be 132 | downloaded, then exit. 133 | 134 | *--speed* speed 135 | 136 | Speed in bit/s of the TKey device port. 137 | 138 | ## Verification on a machine without network 139 | 140 | If you're on a machine without network and need to verify a TKey you 141 | can run 142 | 143 | ``` 144 | $ tkey-verification verify ---show-url 145 | ``` 146 | 147 | which will output the URL to the verification file. Download the file 148 | using another, networked, computer and somehow bring the file or type 149 | it in again on your airgapped computer. Then run: 150 | 151 | ``` 152 | tkey-verification verify -d=. 153 | ``` 154 | 155 | to read from the current directory. 156 | 157 | # FILES 158 | 159 | *tkey-verification* serve-signer produces a file which is named after 160 | the Unique Device Identifier (in hexadecimal) for every signature 161 | made. An example filename would be "signatures/0133704100000015". 162 | 163 | The file contains: 164 | 165 | - timestamp: RFC3339 UTC timestamp when the signature was done. 166 | - tag: The Git tag of the signer program used on the device under 167 | verification, 168 | - signature: Vendor ed25519 signature of the device public key. Stored 169 | in hexadecimal. 170 | 171 | The files generated will later be published on a public web server. 172 | The publication is out of scope for the current program. 173 | 174 | *tkey-verification* show-pubkey generates output for a file called 175 | "vendor-signing-pubkeys.txt" to be included in the build of a new 176 | *tkey-verification*. It's output is: 177 | 178 | - Ed25519 public key 179 | - app name and tag. 180 | - digest of hash 181 | 182 | Example file: 183 | 184 | ``` 185 | 038dd0b898c601517a09cd249d3c4f2de8e9aab38c5fa02701ae29bb41a6d863 verisigner-v0.0.1 9598910ec9ebe2504a5f894de6f8e0677dc94c156c7bd6f7e805a35354b3c85daa4ca66ab93f4d75221b501def457b4cafc933c6cdcf16d1eb8ccba6cccf6630 186 | ``` 187 | 188 | # EXAMPLES 189 | 190 | Verifying the identity of a Tillitis TKey using a networked computer. 191 | 192 | ``` 193 | $ tkey-verification verify 194 | TKey UDI: 0x0133708100000002(BE) VendorID: 0x1337 ProductID: 2 ProductRev: 1 195 | TKey is genuine! 196 | ``` 197 | 198 | Verifying the identity with a non-networked computer: First 199 | 200 | ``` 201 | $ tkey-verification --show-url 202 | ``` 203 | 204 | Then download the file and move it to your current working directory. 205 | Keep the name of the file intact since it's named after the TKey 206 | Unique Device Identifier. and run: 207 | 208 | ``` 209 | $ tkey-verification verify -d=. 210 | TKey UDI: 0x0133708100000002(BE) VendorID: 0x1337 ProductID: 2 ProductRev: 1 211 | Reading verification data from file ./0133708100000002 ... 212 | TKey is genuine! 213 | ``` 214 | 215 | In order to include a new vendor signing key, use: 216 | 217 | ``` 218 | % ./tkey-verification show-pubkey --port /dev/pts/12 --app cmd/tkey-verification/bins/signer-v1.0.0.bin 219 | Connecting to device on serial port /dev/pts/12 ... 220 | Firmware name0:'tk1 ' name1:'mkdf' version:5 221 | Public Key, app tag, and app hash for vendor-signing-pubkeys.txt follows on stdout: 222 | 038dd0b898c601517a09cd249d3c4f2de8e9aab38c5fa02701ae29bb41a6d863 verisigner-v0.0.1 9598910ec9ebe2504a5f894de6f8e0677dc94c156c7bd6f7e805a35354b3c85daa4ca66ab93f4d75221b501def457b4cafc933c6cdcf16d1eb8ccba6cccf6630 223 | ``` 224 | 225 | # AUTHORS 226 | 227 | Tillitis AB, https://tillitis.se/ 228 | 229 | # CAVEATS 230 | 231 | You can currently not use several TKeys on the same computer at the 232 | same time, which means you can't use *serve-signer* and the other 233 | commands on the same computer. 234 | 235 | # SECURITY CONSIDERATIONS 236 | 237 | *tkey-verification* only verifies the /identity/ of the TKey hasn't 238 | changed since signing by the vendor. It might have been manipulated in 239 | other ways. 240 | 241 | The device public key isn't published in the files generated by the 242 | *serve-signer* but is retrievable by anyone with access to the device 243 | under verification. 244 | 245 | You probably shouldn't expose the computers running *serve-signer* or 246 | *remote-sign* on the Internet. 247 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/tillitis/tkey-verification 2 | 3 | go 1.21 4 | 5 | require ( 6 | github.com/spf13/pflag v1.0.5 7 | github.com/tillitis/tkeyclient v1.1.0 8 | github.com/tillitis/tkeysign v1.0.1 9 | gopkg.in/yaml.v2 v2.4.0 10 | ) 11 | 12 | require ( 13 | github.com/ccoveille/go-safecast v1.1.0 // indirect 14 | github.com/creack/goselect v0.1.2 // indirect 15 | go.bug.st/serial v1.6.2 // indirect 16 | golang.org/x/crypto v0.28.0 // indirect 17 | golang.org/x/sys v0.26.0 // indirect 18 | ) 19 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/ccoveille/go-safecast v1.1.0 h1:iHKNWaZm+OznO7Eh6EljXPjGfGQsSfa6/sxPlIEKO+g= 2 | github.com/ccoveille/go-safecast v1.1.0/go.mod h1:QqwNjxQ7DAqY0C721OIO9InMk9zCwcsO7tnRuHytad8= 3 | github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0= 4 | github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY= 5 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 6 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 7 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 8 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 9 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 10 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 11 | github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= 12 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 13 | github.com/tillitis/tkeyclient v1.1.0 h1:/RTIGMZGR8cZ6bUkgZLUmDvTNH5C7qSU2qaSDl3nxhA= 14 | github.com/tillitis/tkeyclient v1.1.0/go.mod h1:K3b/zU58CLtP5iaOMypc4c7PAAmNqyw7F3bs5iNYtG0= 15 | github.com/tillitis/tkeysign v1.0.1 h1:E+nycj/iv9247MOBval1PmWcZ68J7LUXhXdlW49+N08= 16 | github.com/tillitis/tkeysign v1.0.1/go.mod h1:C4gYuqI2HHJ86S0PoWlk30eFsKDnMvdEgIYIVyNfzvY= 17 | go.bug.st/serial v1.6.2 h1:kn9LRX3sdm+WxWKufMlIRndwGfPWsH1/9lCWXQCasq8= 18 | go.bug.st/serial v1.6.2/go.mod h1:UABfsluHAiaNI+La2iESysd9Vetq7VRdpxvjx7CmmOE= 19 | golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= 20 | golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= 21 | golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= 22 | golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 23 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 24 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 25 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 26 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 27 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 28 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 29 | -------------------------------------------------------------------------------- /gon.hcl: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2024 Tillitis AB 2 | # SPDX-License-Identifier: BSD-2-Clause 3 | 4 | source = ["dist/tkey-verification_darwin_all/tkey-verification"] 5 | bundle_id = "com.tillitis.tkey-verification" 6 | 7 | apple_id { 8 | username = "[email protected]" 9 | password = "@keychain:[email protected]" 10 | provider = "34722S433A" 11 | } 12 | 13 | sign { 14 | application_identity = "Developer ID Application: Tillitis AB" 15 | } 16 | -------------------------------------------------------------------------------- /internal/tkey/errors.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 Tillitis AB 2 | // SPDX-License-Identifier: BSD-2-Clause 3 | 4 | package tkey 5 | 6 | import "fmt" 7 | 8 | // Simple errors with no further information 9 | type constError string 10 | 11 | func (err constError) Error() string { 12 | return string(err) 13 | } 14 | 15 | const ( 16 | ErrNoDevice = constError("no TKey connected") 17 | ErrNotFirmware = constError("not firmware") 18 | ErrWrongUDILen = constError("wrong UDI length") 19 | ErrWrongUDIData = constError("reserved UDI bits not zero") 20 | ) 21 | 22 | // More complex errors get their own type below 23 | 24 | type ConnError struct { 25 | devPath string 26 | err error 27 | } 28 | 29 | func (e ConnError) Error() string { 30 | return fmt.Sprintf("could not open device: %v: %v", e.devPath, e.err) 31 | } 32 | 33 | func (e ConnError) Unwrap() error { 34 | return e.err 35 | } 36 | -------------------------------------------------------------------------------- /internal/tkey/tkey.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 Tillitis AB 2 | // SPDX-License-Identifier: BSD-2-Clause 3 | 4 | // Package tkey offers... 5 | // 6 | // Start by 7 | // 8 | // tk, err := NewTKey("/dev/ttyACM0", false) 9 | // 10 | // Load a signer program: 11 | // 12 | // pubkey, err := tk.LoadSigner(...) 13 | // 14 | // Ask it to sign something 15 | // 16 | // sig, err := tk.Sign(message) 17 | // 18 | // digest, err = tk.GetFirmwareHash(4711) 19 | // tk.Close() 20 | package tkey 21 | 22 | import ( 23 | "fmt" 24 | "log" 25 | "os" 26 | "os/signal" 27 | "syscall" 28 | 29 | "github.com/tillitis/tkeyclient" 30 | "github.com/tillitis/tkeysign" 31 | ) 32 | 33 | var le = log.New(os.Stderr, "", 0) 34 | 35 | type TKey struct { 36 | client tkeyclient.TillitisKey 37 | Udi UDI 38 | signer tkeysign.Signer 39 | verbose bool 40 | } 41 | 42 | func NewTKey(devPath string, speed int, verbose bool) (*TKey, error) { 43 | if !verbose { 44 | tkeyclient.SilenceLogging() 45 | } 46 | 47 | if devPath == "" { 48 | var err error 49 | 50 | devPath, err = tkeyclient.DetectSerialPort(verbose) 51 | if err != nil { 52 | return nil, ErrNoDevice 53 | } 54 | } 55 | 56 | // Initialize part of tkey - UDI is filled in later when we 57 | // have a connection. See below. 58 | tkey := TKey{ 59 | client: *tkeyclient.New(), 60 | verbose: verbose, 61 | } 62 | 63 | if verbose { 64 | le.Printf("Connecting to device on serial port %s ...\n", devPath) 65 | } 66 | 67 | if err := tkey.client.Connect(devPath, tkeyclient.WithSpeed(speed)); err != nil { 68 | return nil, ConnError{devPath: devPath, err: err} 69 | } 70 | 71 | // We now have a connection, so close it if we get any of 72 | // these signals. 73 | handleSignals(func() { 74 | tkey.Close() 75 | os.Exit(1) 76 | }, os.Interrupt, syscall.SIGTERM) 77 | 78 | nameVer, err := tkey.client.GetNameVersion() 79 | if err != nil { 80 | le.Printf("Please unplug the TKey and plug it in again to put it in firmware-mode.\n") 81 | le.Printf("Either the device path (%s) is wrong, or the TKey is not in firmware-mode (already running an app).\n", devPath) 82 | return nil, ErrNotFirmware 83 | } 84 | 85 | if verbose { 86 | le.Printf("Firmware name0:'%s' name1:'%s' version:%d\n", 87 | nameVer.Name0, nameVer.Name1, nameVer.Version) 88 | } 89 | 90 | tkUDI, err := tkey.client.GetUDI() 91 | if err != nil { 92 | return nil, fmt.Errorf("%w", err) 93 | } 94 | 95 | var udi UDI 96 | 97 | if err = udi.fromRawLE(tkUDI.RawBytes()); err != nil { 98 | return nil, fmt.Errorf("%w", err) 99 | } 100 | 101 | tkey.Udi = udi 102 | 103 | return &tkey, nil 104 | } 105 | 106 | // GetUDI gets the UDI of a TKey 107 | func (t TKey) GetUDI() UDI { 108 | return t.Udi 109 | } 110 | 111 | func (t TKey) Close() { 112 | t.client.Close() 113 | } 114 | 115 | // LoadSigner loads device app BIN without USS. 116 | // 117 | // Returns the public key and any error. 118 | func (t *TKey) LoadSigner(bin []byte) ([]byte, error) { 119 | var err error 120 | 121 | // No USS. 122 | if err = t.client.LoadApp(bin, []byte{}); err != nil { 123 | return nil, fmt.Errorf("%w", err) 124 | } 125 | if t.verbose { 126 | le.Printf("App loaded.\n") 127 | } 128 | 129 | t.signer = tkeysign.New(&t.client) 130 | 131 | nameVer, err := t.signer.GetAppNameVersion() 132 | if err != nil { 133 | return nil, fmt.Errorf("%w", err) 134 | } 135 | 136 | if t.verbose { 137 | le.Printf("App name0:'%s' name1:'%s' version:%d\n", 138 | nameVer.Name0, nameVer.Name1, nameVer.Version) 139 | } 140 | 141 | pubKey, err := t.signer.GetPubkey() 142 | if err != nil { 143 | return nil, fmt.Errorf("%w", err) 144 | } 145 | 146 | return pubKey, nil 147 | } 148 | 149 | // Sign connects to a TKey and asks an already running device app to 150 | // sign a message. 151 | func (t TKey) Sign(message []byte) ([]byte, error) { 152 | signature, err := t.signer.Sign(message) 153 | if err != nil { 154 | return nil, fmt.Errorf("%w", err) 155 | } 156 | 157 | return signature, nil 158 | } 159 | 160 | // GetFirmwareHash connects to a TKey and asks an already running 161 | // verisigner-app for a hash (sha512) of the TKey's firmware binary. 162 | func (t TKey) GetFirmwareHash(firmwareSize int) ([]byte, error) { 163 | fwHash, err := t.signer.GetFWDigest(firmwareSize) 164 | if err != nil { 165 | return nil, fmt.Errorf("%w", err) 166 | } 167 | 168 | return fwHash, nil 169 | } 170 | 171 | func handleSignals(action func(), sig ...os.Signal) chan<- os.Signal { 172 | ch := make(chan os.Signal, 1) 173 | signal.Notify(ch, sig...) 174 | go func() { 175 | for { 176 | <-ch 177 | action() 178 | } 179 | }() 180 | return ch 181 | } 182 | -------------------------------------------------------------------------------- /internal/tkey/udi.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Tillitis AB 2 | // SPDX-License-Identifier: BSD-2-Clause 3 | 4 | package tkey 5 | 6 | import ( 7 | "encoding/binary" 8 | "encoding/hex" 9 | "fmt" 10 | ) 11 | 12 | const UDISize = 8 13 | 14 | // Holds Big Endian UDI 15 | type UDI struct { 16 | VendorID uint16 17 | ProductID uint8 // 6 bits 18 | ProductRev uint8 // 6 bits 19 | Bytes []byte 20 | } 21 | 22 | func (u *UDI) String() string { 23 | return fmt.Sprintf("0x%s(BE) VendorID: 0x%04x ProductID: %d ProductRev: %d", hex.EncodeToString(u.Bytes), u.VendorID, u.ProductID, u.ProductRev) 24 | } 25 | 26 | // fromRawLE parses the 2 Little Endian uint32s of the Unique Device 27 | // Identifier (as from the firmware protocol) to Big Endian. 28 | func (u *UDI) fromRawLE(udiLE []byte) error { 29 | if l := len(udiLE); l != UDISize { 30 | return ErrWrongUDILen 31 | } 32 | 33 | vpr := binary.LittleEndian.Uint32(udiLE[0:4]) 34 | if reserved := uint8((vpr >> 28) & 0xf); reserved != 0 { 35 | return ErrWrongUDIData 36 | } 37 | u.VendorID = uint16((vpr >> 12) & 0xffff) 38 | u.ProductID = uint8((vpr >> 6) & 0x3f) 39 | u.ProductRev = uint8(vpr & 0x3f) 40 | // u.Serial = binary.LittleEndian.Uint32(udiLE[4:8]) 41 | 42 | u.Bytes = make([]byte, 8) 43 | u.Bytes[0], u.Bytes[1], u.Bytes[2], u.Bytes[3] = udiLE[3], udiLE[2], udiLE[1], udiLE[0] 44 | u.Bytes[4], u.Bytes[5], u.Bytes[6], u.Bytes[7] = udiLE[7], udiLE[6], udiLE[5], udiLE[4] 45 | return nil 46 | } 47 | -------------------------------------------------------------------------------- /release-builds/tkey-verification_0.0.2_linux-amd64.sha512: -------------------------------------------------------------------------------- 1 | c893610b432fc59b27620f5f3857e4e76bf0cdd20f9ece3e456f3403c745ff81cea5cb8b7558728c35d9dd65f3a1a5f508b1cb2dd8a5def9aabf1739ebdde99b tkey-verification_0.0.2_linux-amd64 2 | -------------------------------------------------------------------------------- /release-builds/tkey-verification_0.0.2_macos-amd64.sha512: -------------------------------------------------------------------------------- 1 | b3d21cd7de0b4c10d59bee25be43097ed16d2c810e9a1c1ca51229475edc26cddd85c150cbc9380768c57cc56200333ba6f347cf41607a6565a661868424efce tkey-verification_0.0.2_macos-amd64 2 | -------------------------------------------------------------------------------- /release-builds/tkey-verification_0.0.2_macos-arm64.sha512: -------------------------------------------------------------------------------- 1 | c4bcaeaa8285f75c0f89ca61f45767c6480c81b84e09efa00005f9e8b027bf13ef2634a6a56dcbd7c056952267f721bf390077f361e21a14dbeb935e86cd9b12 tkey-verification_0.0.2_macos-arm64 2 | -------------------------------------------------------------------------------- /release-builds/tkey-verification_0.0.2_macos-universal.sha512: -------------------------------------------------------------------------------- 1 | 3d6f6b34fc0ad9494201148b9e8140d5635237eab7169a911fa8712b5ef0ed4d2857061f4ccbc7b0eff6b274b628ca699c3e52012ae02488437d0c07cd5a8af9 tkey-verification_0.0.2_macos-universal 2 | -------------------------------------------------------------------------------- /release-builds/tkey-verification_0.0.2_windows-amd64.exe.sha512: -------------------------------------------------------------------------------- 1 | 73430154e1c1f49db86d4f57ef50a722df7201752de88d0943385870f86509381de820109a81c87c58b388ff9b15e47934f3fe0fb07a8b74b25157d8b03c0eac tkey-verification_0.0.2_windows-amd64.exe 2 | -------------------------------------------------------------------------------- /release-builds/tkey-verification_0.0.3_linux-amd64.sha512: -------------------------------------------------------------------------------- 1 | 4decaf6aa00c524e5d6ccb4e08f876bd325396762b25ad2a0b0f545cdaea606f0f79961856a985cce71f905d308b510372b4a7e88f3eb71196a7d72d099d69c2 tkey-verification_0.0.3_linux-amd64 2 | -------------------------------------------------------------------------------- /release-builds/tkey-verification_0.0.3_macos-amd64.sha512: -------------------------------------------------------------------------------- 1 | 23220da222ab0261403e53720e49d9709a766a7540ca195134781cdb4a2c8ec15a77ff1072da1902905d8d7fa13b67256dcc9cd22b3bb609e4ca6bb49e65c1f6 tkey-verification_0.0.3_macos-amd64 2 | -------------------------------------------------------------------------------- /release-builds/tkey-verification_0.0.3_macos-arm64.sha512: -------------------------------------------------------------------------------- 1 | 972b45c60e4add0a01e8feefe885cb7f0d6bd061a1b0dc0f923822bb9a675d66a56945a7a9e0e0eb2bb8b1db5f6124d8ec2610893242e4603d92e3b40a33a57f tkey-verification_0.0.3_macos-arm64 2 | -------------------------------------------------------------------------------- /release-builds/tkey-verification_0.0.3_macos-universal.sha512: -------------------------------------------------------------------------------- 1 | 830cd7acc9f2204816bbad6312677f8d77bc9b2cf2f2c742c958f7216ea8cc89cd7d82ab51d5e53f9ecbe42235fecd5866171f167c808ac67147456edf8155a9 tkey-verification_0.0.3_macos-universal 2 | -------------------------------------------------------------------------------- /release-builds/tkey-verification_0.0.3_windows-amd64.exe.sha512: -------------------------------------------------------------------------------- 1 | 0fc213a03c5335ed4faf15ad1df7ad3fabee0f98805dd09968f0d5e2e78ef0716f6dc6262103431d4c9737e7fa4902f1ebd9aa12b397d15d79c846eb3698eab9 tkey-verification_0.0.3_windows-amd64.exe 2 | -------------------------------------------------------------------------------- /test-vendor-signing-pubkeys.txt: -------------------------------------------------------------------------------- 1 | 50d9a125f51d85ffa1fb12011bdae05d39e03cda2a35d0daf3077072daabbb10 verisigner-v0.0.3 f8ecdcda53a296636a0297c250b27fb649860645626cc8ad935eabb4c43ea3e1841c40300544fade4189aa4143c1ca8fe82361e3d874b42b0e2404793a170142 2 | -------------------------------------------------------------------------------- /tkey-verification.yaml.example-remote-sign: -------------------------------------------------------------------------------- 1 | --- 2 | cacert: "certs/tillitis.crt" 3 | clientcert: "certs/client.crt" 4 | clientkey: "certs/client.key" 5 | server: "localhost:1337" 6 | 7 | # Hash digest of signing device app for device signature during provisioning. 8 | signingapphash: "cd3c4f433f84648428113bd0a0cc407b2150e925a51b478006321e5a903c1638ce807138d1cc1f8f03cfb6236a87de0febde3ce0ddf177208e5483d1c169bac4" 9 | -------------------------------------------------------------------------------- /tkey-verification.yaml.example-serve-signer: -------------------------------------------------------------------------------- 1 | --- 2 | cacert: "certs/tillitis.crt" 3 | servercert: "certs/server.crt" 4 | serverkey: "certs/server.key" 5 | listen: "localhost:1337" 6 | 7 | # SHA-512 digest of the app used for vendor signing 8 | vendorapphash: "f8ecdcda53a296636a0297c250b27fb649860645626cc8ad935eabb4c43ea3e1841c40300544fade4189aa4143c1ca8fe82361e3d874b42b0e2404793a170142" 9 | -------------------------------------------------------------------------------- /tools/spdx-ensure: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # SPDX-FileCopyrightText: 2022 Tillitis AB 3 | # SPDX-License-Identifier: BSD-2-Clause 4 | 5 | set -eu 6 | 7 | # Check for the SPDX tag in all files in the repo. Exit with a non-zero code if 8 | # some is missing. The missingok arrays below contain files and directories 9 | # with files where the the tag is not required. 10 | 11 | cd "${0%/*}" 12 | cd .. 13 | 14 | tag="SPDX-License-Identifier:" 15 | 16 | missingok_dirs=( 17 | certs 18 | LICENSES 19 | ) 20 | 21 | missingok_files=( 22 | .editorconfig 23 | .gitignore 24 | .github/workflows/ci.yaml 25 | .github/workflows/golangci-lint.yml 26 | .golangci.yml 27 | .goreleaser.yaml 28 | LICENSE 29 | Makefile 30 | README.md 31 | RELEASE.md 32 | REUSE.toml 33 | go.mod 34 | go.sum 35 | gon.hcl 36 | .clang-format 37 | cmd/tkey-verification/bins/README.md 38 | cmd/tkey-verification/bins/signer-v1.0.1.bin 39 | cmd/tkey-verification/bins/signer-v1.0.1.bin.sha512 40 | cmd/tkey-verification/bins/verisigner-v0.0.3.bin 41 | cmd/tkey-verification/bins/verisigner-v0.0.3.bin.deps 42 | cmd/tkey-verification/bins/verisigner-v0.0.3.bin.sha512 43 | cmd/tkey-verification/vendor-signing-pubkeys.txt 44 | release-builds/tkey-verification_0.0.2_linux-amd64.sha512 45 | release-builds/tkey-verification_0.0.2_macos-amd64.sha512 46 | release-builds/tkey-verification_0.0.2_macos-arm64.sha512 47 | release-builds/tkey-verification_0.0.2_macos-universal.sha512 48 | release-builds/tkey-verification_0.0.2_windows-amd64.exe.sha512 49 | release-builds/tkey-verification_0.0.3_linux-amd64.sha512 50 | release-builds/tkey-verification_0.0.3_macos-amd64.sha512 51 | release-builds/tkey-verification_0.0.3_macos-arm64.sha512 52 | release-builds/tkey-verification_0.0.3_macos-universal.sha512 53 | release-builds/tkey-verification_0.0.3_windows-amd64.exe.sha512 54 | doc/implementation-notes.md 55 | doc/tkey-verification.1 56 | doc/tkey-verification.scd 57 | test-vendor-signing-pubkeys.txt 58 | tkey-verification.yaml.example-remote-sign 59 | tkey-verification.yaml.example-serve-signer 60 | ) 61 | 62 | is_missingok() { 63 | item="$1" 64 | # ok for empty files 65 | [[ -f "$item" ]] && [[ ! -s "$item" ]] && return 0 66 | for fileok in "${missingok_files[@]}"; do 67 | [[ "$item" = "$fileok" ]] && return 0 68 | done 69 | for dirok in "${missingok_dirs[@]}"; do 70 | [[ "$item" =~ ^$dirok ]] && return 0 71 | done 72 | return 1 73 | } 74 | 75 | printf "* Checking for SPDX tags in %s\n" "$PWD" 76 | 77 | mapfile -t repofiles < <(git ls-files || true) 78 | if [[ -z "${repofiles[*]}" ]]; then 79 | printf "* No files in the repo?!\n" 80 | exit 1 81 | fi 82 | 83 | failed=0 84 | 85 | printed=0 86 | for fileok in "${missingok_files[@]}"; do 87 | [[ -f "$fileok" ]] && continue 88 | if (( !printed )); then 89 | printf "* Some files in missingok_files are themselves missing:\n" 90 | printed=1 91 | failed=1 92 | fi 93 | printf "%s\n" "$fileok" 94 | done 95 | 96 | printed=0 97 | for dirok in "${missingok_dirs[@]}"; do 98 | [[ -d "$dirok" ]] && continue 99 | if (( !printed )); then 100 | printf "* Some dirs in missingok_dirs are themselves missing:\n" 101 | printed=1 102 | failed=1 103 | fi 104 | printf "%s\n" "$dirok" 105 | done 106 | 107 | printed=0 108 | for file in "${repofiles[@]}"; do 109 | is_missingok "$file" && continue 110 | if ! grep -q "$tag" "$file"; then 111 | if (( !printed )); then 112 | printf "* Files missing the SPDX tag:\n" 113 | printed=1 114 | failed=1 115 | fi 116 | printf "%s\n" "$file" 117 | fi 118 | done 119 | 120 | exit "$failed" 121 | --------------------------------------------------------------------------------