├── .editorconfig ├── .git-blame-ignore-revs ├── .gitattributes ├── .github ├── renovate.json5 └── workflows │ ├── check.yml │ └── release.yml ├── .gitignore ├── .idea └── vcs.xml ├── AUTHORS ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── build.gradle.kts ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle.kts ├── spotless.license └── src ├── kotlin └── kage │ ├── Age.kt │ ├── Identity.kt │ ├── Primitives.kt │ ├── Recipient.kt │ ├── crypto │ ├── scrypt │ │ ├── ScryptIdentity.kt │ │ └── ScryptRecipient.kt │ ├── stream │ │ ├── ArmorInputStream.kt │ │ ├── ArmorOutputStream.kt │ │ ├── ChaCha20Poly1305.kt │ │ ├── DecryptInputStream.kt │ │ ├── EncryptOutputStream.kt │ │ └── Stream.kt │ └── x25519 │ │ ├── X25519.kt │ │ ├── X25519Identity.kt │ │ └── X25519Recipient.kt │ ├── errors │ ├── Bech32Exception.kt │ ├── CryptoException.kt │ └── ParseException.kt │ ├── format │ ├── AgeFile.kt │ ├── AgeHeader.kt │ ├── AgeKeyFile.kt │ ├── AgeStanza.kt │ ├── Bech32.kt │ └── ParseUtils.kt │ └── utils │ └── Extensions.kt └── test ├── kotlin ├── AgeTest.kt └── kage │ ├── ArmorTest.kt │ ├── UpstreamTestSuite.kt │ ├── crypto │ ├── scrypt │ │ └── ScryptRecipientTest.kt │ └── x25519 │ │ └── X25519RecipientTest.kt │ ├── format │ ├── AgeFileTest.kt │ ├── AgeHeaderTest.kt │ ├── AgeKeyFileTest.kt │ ├── AgeStanzaTest.kt │ └── Bech32Test.kt │ ├── test │ └── utils │ │ ├── Bytes.kt │ │ ├── TestSuite.kt │ │ └── types.kt │ └── utils │ ├── Extensions.kt │ └── ExtensionsTest.kt └── resources └── CCTV ├── README.md ├── age ├── README.md ├── go.mod ├── go.sum ├── internal │ ├── LICENSE │ ├── bech32 │ │ └── bech32.go │ ├── generate.go │ ├── testkit │ │ └── testkit.go │ └── tests │ │ ├── armor.go │ │ ├── armor_crlf.go │ │ ├── armor_empty_line_begin.go │ │ ├── armor_empty_line_end.go │ │ ├── armor_eol_between_padding.go │ │ ├── armor_full_last_line.go │ │ ├── armor_garbage_encoded.go │ │ ├── armor_garbage_leading.go │ │ ├── armor_garbage_trailing.go │ │ ├── armor_header_crlf.go │ │ ├── armor_headers.go │ │ ├── armor_invalid_character_header.go │ │ ├── armor_invalid_character_payload.go │ │ ├── armor_long_line.go │ │ ├── armor_lowercase.go │ │ ├── armor_no_end_line.go │ │ ├── armor_no_eol.go │ │ ├── armor_no_match.go │ │ ├── armor_no_padding.go │ │ ├── armor_not_canonical.go │ │ ├── armor_pgp_checksum.go │ │ ├── armor_short_line.go │ │ ├── armor_whitespace_begin.go │ │ ├── armor_whitespace_end.go │ │ ├── armor_whitespace_eol.go │ │ ├── armor_whitespace_last_line.go │ │ ├── armor_whitespace_line_start.go │ │ ├── armor_whitespace_outside.go │ │ ├── armor_wrong_type.go │ │ ├── header_crlf.go │ │ ├── hmac_bad.go │ │ ├── hmac_extra_space.go │ │ ├── hmac_garbage.go │ │ ├── hmac_missing.go │ │ ├── hmac_no_space.go │ │ ├── hmac_not_canonical.go │ │ ├── hmac_trailing_space.go │ │ ├── hmac_truncated.go │ │ ├── scrypt.go │ │ ├── scrypt_and_x25519.go │ │ ├── scrypt_bad_tag.go │ │ ├── scrypt_double.go │ │ ├── scrypt_extra_argument.go │ │ ├── scrypt_long_file_key.go │ │ ├── scrypt_no_match.go │ │ ├── scrypt_not_canonical_body.go │ │ ├── scrypt_not_canonical_salt.go │ │ ├── scrypt_salt_long.go │ │ ├── scrypt_salt_missing.go │ │ ├── scrypt_salt_short.go │ │ ├── scrypt_uppercase.go │ │ ├── scrypt_work_factor_23.go │ │ ├── scrypt_work_factor_hex.go │ │ ├── scrypt_work_factor_leading_garbage.go │ │ ├── scrypt_work_factor_leading_plus.go │ │ ├── scrypt_work_factor_leading_zero_decimal.go │ │ ├── scrypt_work_factor_leading_zero_octal.go │ │ ├── scrypt_work_factor_missing.go │ │ ├── scrypt_work_factor_negative.go │ │ ├── scrypt_work_factor_overflow.go │ │ ├── scrypt_work_factor_trailing_garbage.go │ │ ├── scrypt_work_factor_wrong.go │ │ ├── scrypt_work_factor_zero.go │ │ ├── stanza_bad_start.go │ │ ├── stanza_base64_padding.go │ │ ├── stanza_empty_argument.go │ │ ├── stanza_empty_body.go │ │ ├── stanza_empty_last_line.go │ │ ├── stanza_invalid_character.go │ │ ├── stanza_long_line.go │ │ ├── stanza_missing_body.go │ │ ├── stanza_missing_final_line.go │ │ ├── stanza_multiple_short_lines.go │ │ ├── stanza_no_arguments.go │ │ ├── stanza_not_canonical.go │ │ ├── stanza_spurious_cr.go │ │ ├── stanza_valid_characters.go │ │ ├── stream_bad_tag.go │ │ ├── stream_bad_tag_second_chunk.go │ │ ├── stream_bad_tag_second_chunk_full.go │ │ ├── stream_empty_payload.go │ │ ├── stream_last_chunk_empty.go │ │ ├── stream_last_chunk_full.go │ │ ├── stream_last_chunk_full_second.go │ │ ├── stream_missing_tag.go │ │ ├── stream_no_chunks.go │ │ ├── stream_no_final.go │ │ ├── stream_no_final_full.go │ │ ├── stream_no_final_two_chunks.go │ │ ├── stream_no_final_two_chunks_full.go │ │ ├── stream_no_nonce.go │ │ ├── stream_short_chunk.go │ │ ├── stream_short_nonce.go │ │ ├── stream_short_second_chunk.go │ │ ├── stream_three_chunks.go │ │ ├── stream_trailing_garbage_long.go │ │ ├── stream_trailing_garbage_short.go │ │ ├── stream_two_chunks.go │ │ ├── stream_two_final_chunks.go │ │ ├── version_unsupported.go │ │ ├── x25519.go │ │ ├── x25519_bad_tag.go │ │ ├── x25519_extra_argument.go │ │ ├── x25519_grease.go │ │ ├── x25519_identity.go │ │ ├── x25519_long_file_key.go │ │ ├── x25519_long_share.go │ │ ├── x25519_low_order.go │ │ ├── x25519_lowercase.go │ │ ├── x25519_multiple_recipients.go │ │ ├── x25519_no_match.go │ │ ├── x25519_not_canonical_body.go │ │ ├── x25519_not_canonical_share.go │ │ └── x25519_short_share.go └── testdata │ ├── armor │ ├── armor_crlf │ ├── armor_empty_line_begin │ ├── armor_empty_line_end │ ├── armor_eol_between_padding │ ├── armor_full_last_line │ ├── armor_garbage_encoded │ ├── armor_garbage_leading │ ├── armor_garbage_trailing │ ├── armor_header_crlf │ ├── armor_headers │ ├── armor_invalid_character_header │ ├── armor_invalid_character_payload │ ├── armor_long_line │ ├── armor_lowercase │ ├── armor_no_end_line │ ├── armor_no_eol │ ├── armor_no_match │ ├── armor_no_padding │ ├── armor_not_canonical │ ├── armor_pgp_checksum │ ├── armor_short_line │ ├── armor_whitespace_begin │ ├── armor_whitespace_end │ ├── armor_whitespace_eol │ ├── armor_whitespace_last_line │ ├── armor_whitespace_line_start │ ├── armor_whitespace_outside │ ├── armor_wrong_type │ ├── header_crlf │ ├── hmac_bad │ ├── hmac_extra_space │ ├── hmac_garbage │ ├── hmac_missing │ ├── hmac_no_space │ ├── hmac_not_canonical │ ├── hmac_trailing_space │ ├── hmac_truncated │ ├── scrypt │ ├── scrypt_and_x25519 │ ├── scrypt_bad_tag │ ├── scrypt_double │ ├── scrypt_extra_argument │ ├── scrypt_long_file_key │ ├── scrypt_no_match │ ├── scrypt_not_canonical_body │ ├── scrypt_not_canonical_salt │ ├── scrypt_salt_long │ ├── scrypt_salt_missing │ ├── scrypt_salt_short │ ├── scrypt_uppercase │ ├── scrypt_work_factor_23 │ ├── scrypt_work_factor_hex │ ├── scrypt_work_factor_leading_garbage │ ├── scrypt_work_factor_leading_plus │ ├── scrypt_work_factor_leading_zero_decimal │ ├── scrypt_work_factor_leading_zero_octal │ ├── scrypt_work_factor_missing │ ├── scrypt_work_factor_negative │ ├── scrypt_work_factor_overflow │ ├── scrypt_work_factor_trailing_garbage │ ├── scrypt_work_factor_wrong │ ├── scrypt_work_factor_zero │ ├── stanza_bad_start │ ├── stanza_base64_padding │ ├── stanza_empty_argument │ ├── stanza_empty_body │ ├── stanza_empty_last_line │ ├── stanza_invalid_character │ ├── stanza_long_line │ ├── stanza_missing_body │ ├── stanza_missing_final_line │ ├── stanza_multiple_short_lines │ ├── stanza_no_arguments │ ├── stanza_not_canonical │ ├── stanza_spurious_cr │ ├── stanza_valid_characters │ ├── stream_bad_tag │ ├── stream_bad_tag_second_chunk │ ├── stream_bad_tag_second_chunk_full │ ├── stream_empty_payload │ ├── stream_last_chunk_empty │ ├── stream_last_chunk_full │ ├── stream_last_chunk_full_second │ ├── stream_missing_tag │ ├── stream_no_chunks │ ├── stream_no_final │ ├── stream_no_final_full │ ├── stream_no_final_two_chunks │ ├── stream_no_final_two_chunks_full │ ├── stream_no_nonce │ ├── stream_short_chunk │ ├── stream_short_nonce │ ├── stream_short_second_chunk │ ├── stream_three_chunks │ ├── stream_trailing_garbage_long │ ├── stream_trailing_garbage_short │ ├── stream_two_chunks │ ├── stream_two_final_chunks │ ├── version_unsupported │ ├── x25519 │ ├── x25519_bad_tag │ ├── x25519_extra_argument │ ├── x25519_grease │ ├── x25519_identity │ ├── x25519_long_file_key │ ├── x25519_long_share │ ├── x25519_low_order │ ├── x25519_lowercase │ ├── x25519_multiple_recipients │ ├── x25519_no_match │ ├── x25519_not_canonical_body │ ├── x25519_not_canonical_share │ └── x25519_short_share └── ed25519vectors ├── LICENSE ├── README.md ├── ed25519vectors.go ├── ed25519vectors.json ├── ed25519vectors_test.go ├── go.mod ├── go.sum └── reencoded25519.go /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # ktfmt 0.47 upgrade 2 | 1560ff756b57b38ae004032e8e52e3986a6e820b 3 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.bat text eol=crlf 2 | * text=auto eol=lf 3 | src/test/resources/CCTV/** linguist-vendored 4 | -------------------------------------------------------------------------------- /.github/renovate.json5: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ], 5 | "automerge": true, 6 | "automergeType": "pr", 7 | "platformAutomerge": true, 8 | "dependencyDashboard": false, 9 | "packageRules": [ 10 | { 11 | matchDatasources: [ 12 | "maven" 13 | ], 14 | registryUrls: [ 15 | "https://maven.google.com/", 16 | "https://repo1.maven.org/maven2", 17 | "https://plugins.gradle.org/m2/", 18 | ], 19 | }, 20 | ], 21 | "regexManagers": [ 22 | { 23 | "fileMatch": [ 24 | ".kts$" 25 | ], 26 | "matchStrings": [ 27 | "ktfmtVersion = \"(?.*)\"", 28 | ], 29 | "datasourceTemplate": "maven", 30 | "depNameTemplate": "com.facebook:ktfmt", 31 | }, 32 | ], 33 | } 34 | -------------------------------------------------------------------------------- /.github/workflows/check.yml: -------------------------------------------------------------------------------- 1 | name: Run tests 2 | on: 3 | pull_request: 4 | branches: 5 | - main 6 | 7 | concurrency: 8 | group: ${{ github.head_ref }} 9 | cancel-in-progress: true 10 | 11 | jobs: 12 | check: 13 | strategy: 14 | matrix: 15 | os: 16 | - ubuntu-latest 17 | - macOS-latest 18 | - windows-latest 19 | runs-on: ${{ matrix.os }} 20 | steps: 21 | 22 | - name: Checkout repository 23 | uses: actions/checkout@v4.2.2 24 | with: 25 | fetch-depth: 0 26 | 27 | - name: Set up JDK 28 | uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 29 | with: 30 | distribution: temurin 31 | java-version: 23 32 | 33 | - name: Setup Gradle 34 | uses: gradle/actions/setup-gradle@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4 35 | with: 36 | add-job-summary: always 37 | validate-wrappers: true 38 | 39 | - name: Run unit tests 40 | shell: bash 41 | run: ./gradlew --no-daemon --no-configuration-cache check 42 | 43 | - name: (Fail-only) Upload test report 44 | if: "${{ failure() }}" 45 | uses: actions/upload-artifact@v4.6.2 46 | with: 47 | name: Test report 48 | path: build/reports 49 | 50 | check-coverage: 51 | permissions: 52 | pull-requests: write 53 | runs-on: ubuntu-latest 54 | steps: 55 | - name: Checkout repository 56 | uses: actions/checkout@v4.2.2 57 | with: 58 | fetch-depth: 0 59 | 60 | - name: Set up JDK 61 | uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 62 | with: 63 | distribution: temurin 64 | java-version: 23 65 | 66 | - name: Setup Gradle 67 | uses: gradle/actions/setup-gradle@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4 68 | with: 69 | add-job-summary: always 70 | validate-wrappers: true 71 | 72 | - name: Check coverage 73 | shell: bash 74 | run: ./gradlew --no-daemon --no-configuration-cache koverXmlReport 75 | 76 | - name: Add coverage report to PR 77 | id: kover 78 | uses: mi-kas/kover-report@v1 79 | with: 80 | path: ${{ github.workspace }}/build/reports/kover/report.xml 81 | token: ${{ secrets.GITHUB_TOKEN }} 82 | title: Code Coverage 83 | update-comment: true 84 | min-coverage-overall: 80 85 | min-coverage-changed-files: 80 86 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Publish snapshots 2 | on: 3 | push: 4 | branches: 5 | - main 6 | 7 | jobs: 8 | publish-release: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout repository 12 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 13 | 14 | - name: Set up JDK 15 | uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 16 | with: 17 | distribution: temurin 18 | java-version: 23 19 | 20 | - name: Setup Gradle 21 | uses: gradle/actions/setup-gradle@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4 22 | with: 23 | add-job-summary: always 24 | dependency-graph: generate-and-submit 25 | validate-wrappers: true 26 | 27 | - name: Upload binaries 28 | shell: bash 29 | run: ./gradlew --no-daemon --no-configuration-cache publishAllPublicationsToMavenCentralRepository 30 | env: 31 | ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.NEXUS_PUBLISH_USERNAME }} 32 | ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.NEXUS_PUBLISH_PASSWORD }} 33 | ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.NEXUS_PUBLISH_GPG_KEY }} 34 | ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.NEXUS_PUBLISH_GPG_KEY_PASSWORD }} 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Seb's .gitignore template 2 | # You can find the most up-to-date version at https://go.sebastiano.dev/gitignore 3 | # Partly based on templates by https://plugins.jetbrains.com/plugin/7495--ignore 4 | # Released under a CC-0 License https://creativecommons.org/share-your-work/public-domain/cc0/ 5 | 6 | ### Windows template 7 | # Windows thumbnail cache files 8 | Thumbs.db 9 | Thumbs.db:encryptable 10 | ehthumbs.db 11 | ehthumbs_vista.db 12 | 13 | # Dump file 14 | *.stackdump 15 | 16 | # Folder config file 17 | [Dd]esktop.ini 18 | 19 | # Recycle Bin used on file shares 20 | $RECYCLE.BIN/ 21 | 22 | # Windows Installer files 23 | *.cab 24 | *.msi 25 | *.msix 26 | *.msm 27 | *.msp 28 | 29 | # Windows shortcuts 30 | *.lnk 31 | 32 | ### macOS template 33 | # General 34 | .DS_Store 35 | .AppleDouble 36 | .LSOverride 37 | 38 | # Icon must end with two \r 39 | Icon 40 | 41 | # Thumbnails 42 | ._* 43 | 44 | # Files that might appear in the root of a volume 45 | .DocumentRevisions-V100 46 | .fseventsd 47 | .Spotlight-V100 48 | .TemporaryItems 49 | .Trashes 50 | .VolumeIcon.icns 51 | .com.apple.timemachine.donotpresent 52 | 53 | # Directories potentially created on remote AFP share 54 | .AppleDB 55 | .AppleDesktop 56 | Network Trash Folder 57 | Temporary Items 58 | .apdisk 59 | 60 | ### Linux template 61 | *~ 62 | 63 | # temporary files which can be created if a process still has a handle open of a deleted file 64 | .fuse_hidden* 65 | 66 | # KDE directory preferences 67 | .directory 68 | 69 | # Linux trash folder which might appear on any partition or disk 70 | .Trash-* 71 | 72 | # .nfs files are created when an open file is removed but is still being accessed 73 | .nfs* 74 | 75 | ### JetBrains template 76 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 77 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 78 | 79 | *.iml 80 | *.ipr 81 | *.iws 82 | /.idea/* 83 | 84 | # Exclude non-user-specific stuff 85 | !.idea/.name 86 | !.idea/codeInsightSettings.xml 87 | !.idea/codeStyles/ 88 | !.idea/copyright/ 89 | !.idea/dataSources.xml 90 | !.idea/detekt.xml 91 | !.idea/encodings.xml 92 | !.idea/externalDependencies.xml 93 | !.idea/file.template.settings.xml 94 | !.idea/fileTemplates/ 95 | !.idea/icon.svg 96 | !.idea/inspectionProfiles/ 97 | !.idea/runConfigurations/ 98 | !.idea/scopes/ 99 | !.idea/vcs.xml 100 | 101 | ### Kotlin template 102 | # Compiled class file 103 | *.class 104 | 105 | # Log file 106 | *.log 107 | 108 | # Package Files # 109 | *.jar 110 | *.war 111 | *.nar 112 | *.ear 113 | *.zip 114 | *.tar.gz 115 | *.rar 116 | 117 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 118 | hs_err_pid* 119 | 120 | ### Gradle template 121 | .gradle 122 | 123 | # Note that you may need to exclude by hand other folders 124 | # named build if necessary (e.g., in src/) 125 | **/build/ 126 | 127 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 128 | !gradle/wrapper/gradle-wrapper.jar 129 | 130 | # Cache of project 131 | .gradletasknamecache 132 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the official list of kage authors for copyright purposes. 2 | # To be included, send a change adding the individual or company 3 | # who owns a contribution's copyright. 4 | 5 | Harsh Shandilya 6 | Aditya Wasan 7 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021 The kage authors 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx2g -XX:+HeapDumpOnOutOfMemoryError -XX:+UseParallelGC -Dkotlin.daemon.jvm.options=-XX:MaxMetaspaceSize=1g 2 | 3 | org.gradle.parallel=true 4 | org.gradle.configureondemand=false 5 | org.gradle.caching=true 6 | 7 | # Enable filesystem watching 8 | org.gradle.vfs.watch=true 9 | 10 | # Enable experimental configuration caching 11 | org.gradle.unsafe.configuration-cache=true 12 | 13 | # Enable Kotlin incremental compilation 14 | kotlin.incremental=true 15 | 16 | # New incremental compilation for Kotlin 17 | kotlin.incremental.useClasspathSnapshot=true 18 | kotlin.build.report.output=file 19 | 20 | # OSSRH sometimes struggles with slow deployments, so this makes Gradle 21 | # more tolerant to those delays. 22 | systemProp.org.gradle.internal.http.connectionTimeout=500000 23 | systemProp.org.gradle.internal.http.socketTimeout=500000 24 | 25 | # Maven publishing 26 | GROUP=com.github.android-password-store 27 | VERSION_NAME=0.3.0-SNAPSHOT 28 | POM_ARTIFACT_ID=kage 29 | POM_NAME=kage 30 | POM_DESCRIPTION=Kotlin implementation of the age file encryption library 31 | POM_INCEPTION_YEAR=2021 32 | 33 | POM_URL=https://github.com/Android-Password-Store/kage 34 | POM_SCM_URL=https://github.com/Android-Password-Store/kage 35 | POM_SCM_CONNECTION=scm:git:https://github.com/Android-Password-Store/kage.git 36 | POM_SCM_DEV_CONNECTION=scm:git:ssh://git@github.com:Android-Password-Store/kage 37 | 38 | POM_LICENSE_NAME=The Apache Software License, Version 2.0 39 | POM_LICENSE_URL=https://www.apache.org/licenses/LICENSE-2.0.txt 40 | POM_LICENSE_DIST=repo 41 | 42 | POM_DEVELOPER_ID=android-password-store 43 | POM_DEVELOPER_NAME=The Android Password Store Authors 44 | POM_DEVELOPER_EMAIL=oss@passwordstore.app 45 | 46 | SONATYPE_HOST=DEFAULT 47 | RELEASE_SIGNING_ENABLED=true 48 | SONATYPE_AUTOMATIC_RELEASE=true 49 | 50 | # OSSRH sometimes struggles with slow deployments, so this makes Gradle 51 | # more tolerant to those delays. 52 | SONATYPE_CONNECT_TIMEOUT_SECONDS=120 53 | SONATYPE_CLOSE_TIMEOUT_SECONDS=1800 54 | 55 | org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled 56 | org.jetbrains.dokka.experimental.gradle.pluginMode.noWarn=true 57 | -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | junit = "5.11.4" 3 | kotlin = "2.1.21" 4 | 5 | [libraries] 6 | animalsniffer-signature-android = "net.sf.androidscents.signature:android-api-level-23:6.0_r3" 7 | bouncycastle-bcprov = "org.bouncycastle:bcprov-jdk15to18:1.80" 8 | hkdf = "at.favre.lib:hkdf:2.0.0" 9 | junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" } 10 | junit-legacy = "junit:junit:4.13.2" 11 | kotlinresult = "com.michael-bull.kotlin-result:kotlin-result:2.0.1" 12 | kotlintest-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" } 13 | truth = "com.google.truth:truth:1.4.4" 14 | 15 | [plugins] 16 | animalsniffer = "ru.vyarus.animalsniffer:1.7.2" 17 | dokka = "org.jetbrains.dokka:2.0.0" 18 | kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } 19 | kover = "org.jetbrains.kotlinx.kover:0.9.1" 20 | mavenPublish = "com.vanniktech.maven.publish.base:0.32.0" 21 | spotless = "com.diffplug.spotless:7.0.4" 22 | vcu = "nl.littlerobots.version-catalog-update:1.0.0" 23 | versions = "com.github.ben-manes.versions:0.52.0" 24 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionSha256Sum=845952a9d6afa783db70bb3b0effaae45ae5542ca2bb7929619e8af49cb634cf 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip 5 | networkTimeout=10000 6 | validateDistributionUrl=true 7 | zipStoreBase=GRADLE_USER_HOME 8 | zipStorePath=wrapper/dists 9 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH= 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021-2022 The kage Authors. All rights reserved. Use of this source code is governed by 3 | * either an Apache 2.0 or MIT license at your discretion, that can be found in the LICENSE-APACHE 4 | * or LICENSE-MIT files respectively. 5 | */ 6 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") 7 | 8 | rootProject.name = "kage" 9 | 10 | pluginManagement { 11 | repositories { 12 | exclusiveContent { 13 | forRepository(::gradlePluginPortal) 14 | filter { 15 | listOf( 16 | "ru.vyarus.animalsniffer", 17 | "org.jetbrains.kotlin.jvm", 18 | "com.diffplug.spotless", 19 | "com.github.ben-manes.versions", 20 | ) 21 | .forEach { plugin -> includeModule(plugin, "${plugin}.gradle.plugin") } 22 | includeModule("com.github.ben-manes", "gradle-versions-plugin") 23 | } 24 | } 25 | mavenCentral() 26 | } 27 | } 28 | 29 | dependencyResolutionManagement { 30 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 31 | repositories { mavenCentral() } 32 | } 33 | -------------------------------------------------------------------------------- /spotless.license: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright $YEAR The kage Authors. All rights reserved. Use of this source code is governed by 3 | * either an Apache 2.0 or MIT license at your discretion, that can be found in the LICENSE-APACHE 4 | * or LICENSE-MIT files respectively. 5 | */ 6 | -------------------------------------------------------------------------------- /src/kotlin/kage/Identity.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 The kage Authors. All rights reserved. Use of this source code is governed by 3 | * either an Apache 2.0 or MIT license at your discretion, that can be found in the LICENSE-APACHE 4 | * or LICENSE-MIT files respectively. 5 | */ 6 | package kage 7 | 8 | import kage.format.AgeStanza 9 | 10 | /** 11 | * An Identity is passed to Decrypt to unwrap an opaque file key from a recipient stanza. It can be 12 | * for example a secret key like X25519Identity, a plugin, or a custom implementation. 13 | * 14 | * Unwrap must return an error wrapping IncorrectIdentityError if none of the recipient stanzas 15 | * match the identity, any other error will be considered fatal. 16 | * 17 | * [Age docs](https://github.com/FiloSottile/age/blob/ab3707c085f2c1/age.go#L59-L72) 18 | */ 19 | public interface Identity { 20 | public fun unwrap(stanzas: List): ByteArray 21 | } 22 | 23 | internal fun multiUnwrap(unwrapFn: (AgeStanza) -> ByteArray, stanzas: List): ByteArray { 24 | val exceptions = mutableListOf() 25 | 26 | stanzas.forEach { stanza -> 27 | try { 28 | return unwrapFn(stanza) 29 | } catch (err: Exception) { 30 | // will try next stanza 31 | exceptions.add(err) 32 | } 33 | } 34 | 35 | throw exceptions.reduce { acc, exception -> acc.apply { addSuppressed(exception) } } 36 | } 37 | -------------------------------------------------------------------------------- /src/kotlin/kage/Primitives.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 The kage Authors. All rights reserved. Use of this source code is governed by 3 | * either an Apache 2.0 or MIT license at your discretion, that can be found in the LICENSE-APACHE 4 | * or LICENSE-MIT files respectively. 5 | */ 6 | package kage 7 | 8 | import at.favre.lib.hkdf.HKDF 9 | import java.io.ByteArrayOutputStream 10 | import javax.crypto.Mac 11 | import javax.crypto.spec.SecretKeySpec 12 | import kage.crypto.stream.ChaCha20Poly1305.KEY_LENGTH 13 | import kage.format.AgeHeader 14 | 15 | internal object Primitives { 16 | 17 | private const val MAC_ALGORITHM = "HmacSHA256" 18 | private const val MAC_KEY_LENGTH = 32 19 | private const val HEADER_INFO = "header" 20 | private const val PAYLOAD_INFO = "payload" 21 | 22 | fun headerMAC(fileKey: ByteArray, header: AgeHeader): ByteArray { 23 | // Passing null directly in extractAndExpand causes overload ambiguity since both SecretKey and 24 | // ByteArray can be null so create a null variable of type ByteArray 25 | val saltExtract: ByteArray? = null 26 | val headerByteArray = HEADER_INFO.encodeToByteArray() 27 | val hkdf = HKDF.fromHmacSha256() 28 | 29 | val hmacKey = hkdf.extractAndExpand(saltExtract, fileKey, headerByteArray, MAC_KEY_LENGTH) 30 | val secretKey = SecretKeySpec(hmacKey, MAC_ALGORITHM) 31 | 32 | val mac = Mac.getInstance(MAC_ALGORITHM) 33 | mac.init(secretKey) 34 | 35 | val outputStream = ByteArrayOutputStream() 36 | outputStream.bufferedWriter().use { writer -> AgeHeader.writeWithoutMac(writer, header) } 37 | 38 | // Here we need to pass the complete header including the footer prefix 39 | return mac.doFinal(outputStream.toByteArray()) 40 | } 41 | 42 | fun streamKey(fileKey: ByteArray, nonce: ByteArray): ByteArray { 43 | val payloadByteArray = PAYLOAD_INFO.encodeToByteArray() 44 | val hkdf = HKDF.fromHmacSha256() 45 | return hkdf.extractAndExpand(nonce, fileKey, payloadByteArray, KEY_LENGTH) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/kotlin/kage/Recipient.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 The kage Authors. All rights reserved. Use of this source code is governed by 3 | * either an Apache 2.0 or MIT license at your discretion, that can be found in the LICENSE-APACHE 4 | * or LICENSE-MIT files respectively. 5 | */ 6 | package kage 7 | 8 | import kage.format.AgeStanza 9 | 10 | /* 11 | * From the age docs: 12 | * A Recipient is passed to Encrypt to wrap an opaque file key to one or more 13 | * recipient stanza(s). It can be for example a public key like X25519Recipient, 14 | * a plugin, or a custom implementation. 15 | * https://github.com/FiloSottile/age/blob/ab3707c085f2c1fdfd767a2ed718423e3925f4c4/age.go#L76-L85 16 | * 17 | * From the rage docs: 18 | * Implementations MUST NOT return more than one stanza per "actual recipient". 19 | * https://github.com/str4d/rage/blob/main/age/src/lib.rs#L225-L239 20 | */ 21 | public interface Recipient { 22 | public fun wrap(fileKey: ByteArray): List 23 | } 24 | -------------------------------------------------------------------------------- /src/kotlin/kage/crypto/scrypt/ScryptIdentity.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 The kage Authors. All rights reserved. Use of this source code is governed by 3 | * either an Apache 2.0 or MIT license at your discretion, that can be found in the LICENSE-APACHE 4 | * or LICENSE-MIT files respectively. 5 | */ 6 | package kage.crypto.scrypt 7 | 8 | import kage.Age 9 | import kage.Identity 10 | import kage.crypto.stream.ChaCha20Poly1305 11 | import kage.crypto.stream.ChaCha20Poly1305.KEY_LENGTH 12 | import kage.errors.IncorrectIdentityException 13 | import kage.errors.ScryptIdentityException 14 | import kage.format.AgeStanza 15 | import kage.multiUnwrap 16 | import kage.utils.decodeBase64 17 | import org.bouncycastle.crypto.generators.SCrypt 18 | 19 | public class ScryptIdentity( 20 | private val password: ByteArray, 21 | private val maxWorkFactor: Int = DEFAULT_WORK_FACTOR, 22 | ) : Identity { 23 | 24 | init { 25 | require(maxWorkFactor in 2..30) { "workFactor must be > 1 and <= 30" } 26 | } 27 | 28 | override fun unwrap(stanzas: List): ByteArray { 29 | return multiUnwrap(::unwrapSingle, stanzas) 30 | } 31 | 32 | private fun unwrapSingle(stanza: AgeStanza): ByteArray { 33 | if (stanza.type != ScryptRecipient.SCRYPT_STANZA_TYPE) throw IncorrectIdentityException() 34 | 35 | if (stanza.args.size != 2) throw ScryptIdentityException("invalid scrypt recipient block") 36 | 37 | val salt = 38 | try { 39 | stanza.args.first().decodeBase64() 40 | } catch (err: IllegalAccessException) { 41 | throw ScryptIdentityException("failed to parse scrypt salt: ${err.message}") 42 | } 43 | 44 | if (salt.size != ScryptRecipient.SCRYPT_SALT_SIZE) 45 | throw ScryptIdentityException("invalid scrypt recipient block") 46 | 47 | val digitsRe = "^[1-9][0-9]*$".toRegex() 48 | if (!stanza.args[1].matches(digitsRe)) 49 | throw ScryptIdentityException("scrypt work factor encoding invalid: ${stanza.args[1]}") 50 | val workFactor = stanza.args[1].toInt() 51 | 52 | if (workFactor > maxWorkFactor) 53 | throw ScryptIdentityException("scrypt factor too large: $workFactor") 54 | 55 | if (workFactor <= 0) // Unreachable due to regex 56 | throw ScryptIdentityException("invalid scrypt workfactor: $workFactor") 57 | 58 | val fullSalt = ScryptRecipient.SCRYPT_SALT_LABEL.toByteArray().plus(salt) 59 | 60 | try { 61 | val wrappingKey = SCrypt.generate(password, fullSalt, 1 shl workFactor, 8, 1, KEY_LENGTH) 62 | return ChaCha20Poly1305.aeadDecrypt(wrappingKey, stanza.body, Age.FILE_KEY_SIZE) 63 | } catch (err: Exception) { 64 | throw ScryptIdentityException(null, err) 65 | } 66 | } 67 | 68 | internal companion object { 69 | const val DEFAULT_WORK_FACTOR = 22 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/kotlin/kage/crypto/scrypt/ScryptRecipient.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 The kage Authors. All rights reserved. Use of this source code is governed by 3 | * either an Apache 2.0 or MIT license at your discretion, that can be found in the LICENSE-APACHE 4 | * or LICENSE-MIT files respectively. 5 | */ 6 | package kage.crypto.scrypt 7 | 8 | import java.security.SecureRandom 9 | import kage.Recipient 10 | import kage.crypto.stream.ChaCha20Poly1305 11 | import kage.crypto.stream.ChaCha20Poly1305.KEY_LENGTH 12 | import kage.format.AgeStanza 13 | import kage.utils.encodeBase64 14 | import org.bouncycastle.crypto.generators.SCrypt 15 | 16 | public class ScryptRecipient( 17 | private val password: ByteArray, 18 | private val workFactor: Int = DEFAULT_WORK_FACTOR, 19 | ) : Recipient { 20 | 21 | override fun wrap(fileKey: ByteArray): List { 22 | val salt = ByteArray(SCRYPT_SALT_SIZE) 23 | SecureRandom().nextBytes(salt) 24 | 25 | val logN = this.workFactor 26 | 27 | val fullSalt = SCRYPT_SALT_LABEL.toByteArray().plus(salt) 28 | 29 | val scryptKey = SCrypt.generate(password, fullSalt, 1 shl logN, 8, 1, KEY_LENGTH) 30 | 31 | val wrappedKey = ChaCha20Poly1305.aeadEncrypt(scryptKey, fileKey) 32 | 33 | val stanza = 34 | AgeStanza(SCRYPT_STANZA_TYPE, listOf(salt.encodeBase64(), logN.toString()), wrappedKey) 35 | 36 | return listOf(stanza) 37 | } 38 | 39 | internal companion object { 40 | const val SCRYPT_SALT_SIZE = 16 41 | const val SCRYPT_STANZA_TYPE = "scrypt" 42 | const val SCRYPT_SALT_LABEL = "age-encryption.org/v1/scrypt" 43 | const val DEFAULT_WORK_FACTOR = 18 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/kotlin/kage/crypto/stream/ArmorOutputStream.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 The kage Authors. All rights reserved. Use of this source code is governed by 3 | * either an Apache 2.0 or MIT license at your discretion, that can be found in the LICENSE-APACHE 4 | * or LICENSE-MIT files respectively. 5 | */ 6 | package kage.crypto.stream 7 | 8 | import java.io.OutputStream 9 | import kage.utils.encodeBase64 10 | 11 | internal class ArmorOutputStream(private val dst: OutputStream) : OutputStream() { 12 | 13 | private val buf = ByteArray(ArmorInputStream.BYTES_PER_LINE) 14 | private var bufSize = 0 15 | 16 | private var started = false 17 | 18 | override fun write(i: Int) { 19 | if (!started) { 20 | dst.write((ArmorInputStream.HEADER + "\n").toByteArray()) 21 | started = true 22 | } 23 | 24 | val b = i.toByte() 25 | 26 | if (bufSize == ArmorInputStream.BYTES_PER_LINE) { 27 | writeLine() 28 | bufSize = 0 29 | } 30 | 31 | buf[bufSize] = b 32 | bufSize++ 33 | } 34 | 35 | private fun writeLine() { 36 | if (bufSize > 0) { 37 | val b64Bytes = (buf.sliceArray(0 until bufSize)).encodeBase64(true).toByteArray() 38 | dst.write(b64Bytes) 39 | } 40 | 41 | dst.write('\n'.code) 42 | } 43 | 44 | override fun close() { 45 | writeLine() 46 | dst.write((ArmorInputStream.FOOTER).toByteArray()) 47 | dst.close() 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/kotlin/kage/crypto/stream/ChaCha20Poly1305.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 The kage Authors. All rights reserved. Use of this source code is governed by 3 | * either an Apache 2.0 or MIT license at your discretion, that can be found in the LICENSE-APACHE 4 | * or LICENSE-MIT files respectively. 5 | */ 6 | package kage.crypto.stream 7 | 8 | import kage.errors.IncorrectCipherTextSizeException 9 | import kotlin.math.max 10 | import org.bouncycastle.crypto.modes.ChaCha20Poly1305 11 | import org.bouncycastle.crypto.params.AEADParameters 12 | import org.bouncycastle.crypto.params.KeyParameter 13 | 14 | internal object ChaCha20Poly1305 { 15 | const val MAC_SIZE: Int = 16 // bytes 16 | const val KEY_LENGTH: Int = 32 // bytes 17 | const val NONCE_LENGTH: Int = 12 // bytes 18 | 19 | fun encrypt( 20 | key: ByteArray, 21 | nonce: ByteArray, 22 | input: ByteArray, 23 | inOff: Int, 24 | inLen: Int, 25 | ): ByteArray { 26 | val cipher = ChaCha20Poly1305() 27 | val secKey = KeyParameter(key) 28 | val params = AEADParameters(secKey, MAC_SIZE * 8, nonce) 29 | 30 | cipher.init(true, params) 31 | 32 | val output = ByteArray(cipher.getOutputSize(inLen)) 33 | 34 | val c = cipher.processBytes(input, inOff, inLen, output, 0) 35 | cipher.doFinal(output, c) 36 | 37 | return output 38 | } 39 | 40 | fun aeadEncrypt(key: ByteArray, input: ByteArray): ByteArray { 41 | // From the spec: https://github.com/C2SP/C2SP/blob/main/age.md 42 | // "ChaCha20-Poly1305 nonce is fixed as 12 0x00 bytes [...]" 43 | val nonce = ByteArray(NONCE_LENGTH) 44 | 45 | return encrypt(key, nonce, input, 0, input.size) 46 | } 47 | 48 | fun decrypt( 49 | key: ByteArray, 50 | nonce: ByteArray, 51 | input: ByteArray, 52 | inOff: Int, 53 | inLen: Int, 54 | out: ByteArray, 55 | outOff: Int, 56 | ): Int { 57 | val cipher = ChaCha20Poly1305() 58 | val secKey = KeyParameter(key) 59 | val params = AEADParameters(secKey, MAC_SIZE * 8, nonce) 60 | 61 | cipher.init(false, params) 62 | 63 | var outputSize = 0 64 | 65 | outputSize += cipher.processBytes(input, inOff, inLen, out, outOff) 66 | outputSize += cipher.doFinal(out, outputSize) 67 | 68 | return outputSize 69 | } 70 | 71 | /** 72 | * From age.go: 73 | * 74 | * The message size is limited to mitigate multi-key attacks, where a ciphertext can be crafted 75 | * that decrypts successfully under multiple keys. Short ciphertexts can only target two keys, 76 | * which has limited impact. 77 | */ 78 | fun aeadDecrypt(key: ByteArray, input: ByteArray, expectedPlaintextSize: Int): ByteArray { 79 | val out = ByteArray(max(0, input.size - MAC_SIZE)) 80 | 81 | val nonce = ByteArray(NONCE_LENGTH) 82 | 83 | if (input.size != expectedPlaintextSize + MAC_SIZE) throw IncorrectCipherTextSizeException() 84 | 85 | decrypt(key, nonce, input, 0, input.size, out, 0) 86 | 87 | return out 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/kotlin/kage/crypto/stream/EncryptOutputStream.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 The kage Authors. All rights reserved. Use of this source code is governed by 3 | * either an Apache 2.0 or MIT license at your discretion, that can be found in the LICENSE-APACHE 4 | * or LICENSE-MIT files respectively. 5 | */ 6 | package kage.crypto.stream 7 | 8 | import java.io.OutputStream 9 | import kage.crypto.stream.ChaCha20Poly1305.NONCE_LENGTH 10 | import kage.crypto.stream.Stream.incNonce 11 | import kage.crypto.stream.Stream.nonceIsZero 12 | import kage.crypto.stream.Stream.setLastChunkFlag 13 | import kage.errors.StreamException 14 | 15 | /** 16 | * Encrypts data written to this OutputStream and writes the resulting ciphertext to the underlying 17 | * stream. 18 | * 19 | * This class is **not** thread safe. 20 | */ 21 | internal class EncryptOutputStream(private val key: ByteArray, private val dst: OutputStream) : 22 | OutputStream() { 23 | 24 | private val nonce = ByteArray(NONCE_LENGTH) 25 | 26 | private val buf = ByteArray(CHUNK_SIZE) 27 | private var bufSize = 0 28 | 29 | override fun write(i: Int) { 30 | val b = i.toByte() 31 | 32 | if (bufSize == CHUNK_SIZE) { 33 | flushChunk() 34 | bufSize = 0 35 | } 36 | 37 | buf[bufSize] = b 38 | bufSize++ 39 | } 40 | 41 | override fun close() { 42 | flushChunk(last = true) 43 | dst.close() 44 | } 45 | 46 | private fun flushChunk(last: Boolean = false) { 47 | if (!last && bufSize != CHUNK_SIZE) { 48 | throw StreamException("internal error: flush called with partial chunk") 49 | } 50 | 51 | if (bufSize == 0 && !nonceIsZero(this.nonce)) throw StreamException("chunk cannot be empty") 52 | 53 | if (last) setLastChunkFlag(nonce) 54 | 55 | val chunk = ChaCha20Poly1305.encrypt(key, nonce, buf, 0, bufSize) 56 | 57 | dst.write(chunk) 58 | 59 | incNonce(nonce) 60 | } 61 | 62 | internal companion object { 63 | const val CHUNK_SIZE = 64 * 1024 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/kotlin/kage/crypto/stream/Stream.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 The kage Authors. All rights reserved. Use of this source code is governed by 3 | * either an Apache 2.0 or MIT license at your discretion, that can be found in the LICENSE-APACHE 4 | * or LICENSE-MIT files respectively. 5 | */ 6 | package kage.crypto.stream 7 | 8 | import kage.crypto.stream.ChaCha20Poly1305.NONCE_LENGTH 9 | import kage.errors.StreamException 10 | 11 | internal object Stream { 12 | fun incNonce(nonce: ByteArray) { 13 | for (i in nonce.size - 2 downTo 0) { 14 | nonce[i]++ 15 | 16 | if (nonce[i] != 0.toByte()) break 17 | else if (i == 0) throw StreamException("stream: chunk counter wrapped around") 18 | } 19 | } 20 | 21 | fun nonceIsZero(nonce: ByteArray): Boolean = nonce.contentEquals(ByteArray(NONCE_LENGTH)) 22 | 23 | fun setLastChunkFlag(nonce: ByteArray) { 24 | nonce[nonce.size - 1] = 0x01 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/kotlin/kage/crypto/x25519/X25519.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 The kage Authors. All rights reserved. Use of this source code is governed by 3 | * either an Apache 2.0 or MIT license at your discretion, that can be found in the LICENSE-APACHE 4 | * or LICENSE-MIT files respectively. 5 | */ 6 | package kage.crypto.x25519 7 | 8 | import kage.errors.X25519LowOrderPointException 9 | import org.bouncycastle.math.ec.rfc7748.X25519 10 | 11 | public object X25519 { 12 | 13 | public fun scalarMult(input: ByteArray, r: ByteArray): ByteArray { 14 | val out = ByteArray(input.size) 15 | 16 | if (!X25519.calculateAgreement(input, 0, r, 0, out, 0)) 17 | throw X25519LowOrderPointException("Low order point") 18 | 19 | X25519.scalarMult(input, 0, r, 0, out, 0) 20 | return out 21 | } 22 | 23 | public fun scalarMultBase(input: ByteArray): ByteArray { 24 | val out = ByteArray(input.size) 25 | 26 | X25519.scalarMultBase(input, 0, out, 0) 27 | 28 | return out 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/kotlin/kage/crypto/x25519/X25519Identity.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 The kage Authors. All rights reserved. Use of this source code is governed by 3 | * either an Apache 2.0 or MIT license at your discretion, that can be found in the LICENSE-APACHE 4 | * or LICENSE-MIT files respectively. 5 | */ 6 | package kage.crypto.x25519 7 | 8 | import at.favre.lib.hkdf.HKDF 9 | import com.github.michaelbull.result.getOrThrow 10 | import com.github.michaelbull.result.mapError 11 | import kage.Age 12 | import kage.Identity 13 | import kage.crypto.stream.ChaCha20Poly1305 14 | import kage.crypto.x25519.X25519Recipient.Companion.MAC_KEY_LENGTH 15 | import kage.crypto.x25519.X25519Recipient.Companion.X25519_INFO 16 | import kage.errors.IncorrectIdentityException 17 | import kage.errors.X25519IdentityException 18 | import kage.format.AgeKeyFile 19 | import kage.format.AgeStanza 20 | import kage.format.Bech32 21 | import kage.multiUnwrap 22 | import kage.utils.decodeBase64 23 | import org.bouncycastle.math.ec.rfc7748.X25519.POINT_SIZE 24 | 25 | public class X25519Identity(private val secretKey: ByteArray, private val publicKey: ByteArray) : 26 | Identity { 27 | 28 | private fun unwrapSingle(stanza: AgeStanza): ByteArray { 29 | if (stanza.type != X25519Recipient.X25519_STANZA_TYPE) throw IncorrectIdentityException() 30 | 31 | try { 32 | if (stanza.args.size != 1) throw X25519IdentityException("invalid x25519 recipient block") 33 | 34 | val stanzaPublicKey = stanza.args[0].decodeBase64() 35 | 36 | if (stanzaPublicKey.size != POINT_SIZE) 37 | throw X25519IdentityException("invalid x25519 recipient block") 38 | 39 | val sharedSecret = X25519.scalarMult(secretKey, stanzaPublicKey) 40 | 41 | val salt = stanzaPublicKey.plus(this.publicKey) 42 | 43 | val hkdf = HKDF.fromHmacSha256() 44 | 45 | val wrapingKey = 46 | hkdf.extractAndExpand(salt, sharedSecret, X25519_INFO.toByteArray(), MAC_KEY_LENGTH) 47 | 48 | return ChaCha20Poly1305.aeadDecrypt(wrapingKey, stanza.body, Age.FILE_KEY_SIZE) 49 | } catch (err: Exception) { 50 | if (err is X25519IdentityException) { 51 | throw err 52 | } else { 53 | throw X25519IdentityException("Error occurred while unwrapping stanza", err) 54 | } 55 | } 56 | } 57 | 58 | override fun unwrap(stanzas: List): ByteArray { 59 | return multiUnwrap(::unwrapSingle, stanzas) 60 | } 61 | 62 | public fun encodeToString(): String = 63 | Bech32.encode(AgeKeyFile.AGE_SECRET_KEY_PREFIX, secretKey).getOrThrow() 64 | 65 | public companion object { 66 | 67 | public fun decode(string: String): X25519Identity { 68 | val (hrp, key) = 69 | Bech32.decode(string) 70 | .mapError { X25519IdentityException("Invalid public key", it) } 71 | .getOrThrow() 72 | 73 | if (key.size != POINT_SIZE) 74 | throw X25519IdentityException("Invalid X25519 private key size: (${key.size})") 75 | 76 | if (hrp != AgeKeyFile.AGE_SECRET_KEY_PREFIX) 77 | throw X25519IdentityException("Invalid human readable part for age secret key ($hrp)") 78 | 79 | val publicKey = X25519.scalarMultBase(key) 80 | 81 | return X25519Identity(key, publicKey) 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/kotlin/kage/crypto/x25519/X25519Recipient.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 The kage Authors. All rights reserved. Use of this source code is governed by 3 | * either an Apache 2.0 or MIT license at your discretion, that can be found in the LICENSE-APACHE 4 | * or LICENSE-MIT files respectively. 5 | */ 6 | package kage.crypto.x25519 7 | 8 | import at.favre.lib.hkdf.HKDF 9 | import com.github.michaelbull.result.getOrThrow 10 | import com.github.michaelbull.result.mapError 11 | import java.security.SecureRandom 12 | import kage.Recipient 13 | import kage.crypto.stream.ChaCha20Poly1305 14 | import kage.errors.InvalidRecipientException 15 | import kage.format.AgeKeyFile.Companion.AGE_PUBLIC_KEY_PREFIX 16 | import kage.format.AgeStanza 17 | import kage.format.Bech32 18 | import kage.utils.encodeBase64 19 | 20 | public class X25519Recipient(private val publicKey: ByteArray) : Recipient { 21 | 22 | override fun wrap(fileKey: ByteArray): List { 23 | val ephemeralSecret = ByteArray(EPHEMERAL_SECRET_LEN) 24 | SecureRandom().nextBytes(ephemeralSecret) 25 | 26 | val ephemeralShare = X25519.scalarMultBase(ephemeralSecret) 27 | 28 | val salt = ephemeralShare.plus(publicKey) 29 | 30 | val sharedSecret = X25519.scalarMult(ephemeralSecret, publicKey) 31 | 32 | val hkdf = HKDF.fromHmacSha256() 33 | 34 | val wrappingKey = 35 | hkdf.extractAndExpand(salt, sharedSecret, X25519_INFO.toByteArray(), MAC_KEY_LENGTH) 36 | 37 | val wrappedKey = ChaCha20Poly1305.aeadEncrypt(wrappingKey, fileKey) 38 | 39 | val stanza = AgeStanza(X25519_STANZA_TYPE, listOf(ephemeralShare.encodeBase64()), wrappedKey) 40 | 41 | return listOf(stanza) 42 | } 43 | 44 | public fun encodeToString(): String = Bech32.encode(AGE_PUBLIC_KEY_PREFIX, publicKey).getOrThrow() 45 | 46 | internal companion object { 47 | const val X25519_STANZA_TYPE = "X25519" 48 | const val X25519_INFO = "age-encryption.org/v1/X25519" 49 | const val KEY_LENGTH = 32 // bytes 50 | const val MAC_KEY_LENGTH = 32 // bytes 51 | const val EPHEMERAL_SECRET_LEN = 32 // bytes 52 | 53 | fun decode(string: String): X25519Recipient { 54 | val (hrp, key) = 55 | Bech32.decode(string) 56 | .mapError { InvalidRecipientException("Invalid public key", it) } 57 | .getOrThrow() 58 | 59 | if (key.size != KEY_LENGTH) 60 | throw InvalidRecipientException("Invalid key size for age public key (${key.size})") 61 | 62 | if (hrp != AGE_PUBLIC_KEY_PREFIX) 63 | throw InvalidRecipientException("Invalid human readable part for age public key ($hrp)") 64 | 65 | return X25519Recipient(key) 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/kotlin/kage/errors/Bech32Exception.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 The kage Authors. All rights reserved. Use of this source code is governed by 3 | * either an Apache 2.0 or MIT license at your discretion, that can be found in the LICENSE-APACHE 4 | * or LICENSE-MIT files respectively. 5 | */ 6 | package kage.errors 7 | 8 | /** Thrown when encoding or decoding Bech32 */ 9 | public class Bech32Exception(message: String? = null, cause: Throwable? = null) : 10 | Exception(message, cause) 11 | -------------------------------------------------------------------------------- /src/kotlin/kage/errors/ParseException.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021-2022 The kage Authors. All rights reserved. Use of this source code is governed by 3 | * either an Apache 2.0 or MIT license at your discretion, that can be found in the LICENSE-APACHE 4 | * or LICENSE-MIT files respectively. 5 | */ 6 | package kage.errors 7 | 8 | /** Wrapper type for errors triggered while parsing an [kage.format.AgeStanza] */ 9 | public sealed class ParseException(message: String? = null, cause: Throwable? = null) : 10 | Exception(message, cause) 11 | 12 | /** Raised when a non-ASCII string is encountered when parsing an [kage.format.AgeHeader]. */ 13 | public class InvalidArbitraryStringException(message: String? = null, cause: Throwable? = null) : 14 | ParseException(message, cause) 15 | 16 | /** Raised when the parsed version is not an expected one. */ 17 | public class InvalidVersionException(message: String? = null, cause: Throwable? = null) : 18 | ParseException(message, cause) 19 | 20 | /** Raised when a failure occurs while parsing [kage.format.AgeStanza] for [kage.Recipient]s. */ 21 | public class InvalidRecipientException(message: String? = null, cause: Throwable? = null) : 22 | ParseException(message, cause) 23 | 24 | /** Raised when the footer for a [kage.format.AgeHeader] is incorrect. */ 25 | public class InvalidFooterException(message: String? = null, cause: Throwable? = null) : 26 | ParseException(message, cause) 27 | 28 | /** Raised when the [kage.format.AgeHeader.mac] is empty when writing a [kage.format.AgeHeader]. */ 29 | public class InvalidHMACException(message: String? = null, cause: Throwable? = null) : 30 | ParseException(message, cause) 31 | 32 | /** 33 | * Raised when the [kage.format.AgeKeyFile.privateKey] is empty when parsing a 34 | * [kage.format.AgeKeyFile]. 35 | */ 36 | public class InvalidAgeKeyException(message: String? = null, cause: Throwable? = null) : 37 | ParseException(message, cause) 38 | -------------------------------------------------------------------------------- /src/kotlin/kage/format/AgeFile.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021-2022 The kage Authors. All rights reserved. Use of this source code is governed by 3 | * either an Apache 2.0 or MIT license at your discretion, that can be found in the LICENSE-APACHE 4 | * or LICENSE-MIT files respectively. 5 | */ 6 | package kage.format 7 | 8 | import java.io.ByteArrayOutputStream 9 | import java.io.InputStream 10 | 11 | public class AgeFile(public val header: AgeHeader, public val body: ByteArray) { 12 | 13 | override fun equals(other: Any?): Boolean { 14 | if (other == null) return false 15 | if (other !is AgeFile) return false 16 | 17 | if (this === other) return true 18 | 19 | if (header != other.header) return false 20 | if (!body.contentEquals(other.body)) return false 21 | 22 | return true 23 | } 24 | 25 | override fun hashCode(): Int { 26 | var result = header.hashCode() 27 | result = 31 * result + body.contentHashCode() 28 | return result 29 | } 30 | 31 | internal companion object { 32 | internal const val VERSION_LINE = "age-encryption.org/v1" 33 | internal const val RECIPIENT_PREFIX = "->" 34 | internal const val FOOTER_PREFIX = "---" 35 | internal const val COLUMNS_PER_LINE = 64 36 | internal const val BYTES_PER_LINE = COLUMNS_PER_LINE / 4 * 3 37 | 38 | internal fun parse(input: InputStream): AgeFile { 39 | val body = ByteArrayOutputStream() 40 | 41 | val header = 42 | body.use { b -> 43 | input.buffered().use { i -> 44 | val hdr = AgeHeader.parse(i) 45 | i.copyTo(b) 46 | hdr 47 | } 48 | } 49 | 50 | return AgeFile(header, body.toByteArray()) 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/kotlin/kage/format/ParseUtils.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021-2022 The kage Authors. All rights reserved. Use of this source code is governed by 3 | * either an Apache 2.0 or MIT license at your discretion, that can be found in the LICENSE-APACHE 4 | * or LICENSE-MIT files respectively. 5 | */ 6 | package kage.format 7 | 8 | import kage.errors.InvalidArbitraryStringException 9 | 10 | internal object ParseUtils { 11 | /* 12 | * Splits a line over ' ' and returns a pair with the line prefix and the arguments 13 | */ 14 | @JvmStatic 15 | internal fun splitArgs(line: String): Pair> { 16 | // Split recipient line over " " 17 | val parts = line.split(" ") 18 | // Drop '->' (RECIPIENT_PREFIX) from recipient line and return it along with the remaining 19 | // arguments 20 | return Pair(parts.first(), parts.drop(1)) 21 | } 22 | 23 | /* 24 | * Age Spec: 25 | * ... an arbitrary string is a sequence of ASCII characters with values 33 to 126. 26 | */ 27 | @JvmStatic 28 | internal fun isValidArbitraryString(string: String): Boolean { 29 | if (string.isEmpty()) 30 | throw InvalidArbitraryStringException("Arbitrary string should not be empty") 31 | string.forEach { char -> if (char.code < 33 || char.code > 126) return false } 32 | return true 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/kotlin/kage/utils/Extensions.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 The kage Authors. All rights reserved. Use of this source code is governed by 3 | * either an Apache 2.0 or MIT license at your discretion, that can be found in the LICENSE-APACHE 4 | * or LICENSE-MIT files respectively. 5 | */ 6 | package kage.utils 7 | 8 | import java.io.BufferedInputStream 9 | import java.io.ByteArrayOutputStream 10 | import java.io.Writer 11 | import java.util.Base64 12 | import kage.errors.InvalidBase64StringException 13 | 14 | internal fun ByteArray.encodeBase64(isArmor: Boolean = false): String { 15 | return Base64.getEncoder().run { if (isArmor) this else withoutPadding() }.encodeToString(this) 16 | } 17 | 18 | internal fun String.decodeBase64(isArmor: Boolean = false): ByteArray { 19 | val (isCanonical, decoded) = this.isCanonicalBase64(isArmor) 20 | if (!isCanonical) throw InvalidBase64StringException() 21 | return decoded 22 | } 23 | 24 | internal fun String.isCanonicalBase64(isArmor: Boolean = false): Pair { 25 | val decodedByteArray = Base64.getDecoder().decode(this) 26 | // Armor can contain padding 27 | val encodedString = 28 | Base64.getEncoder() 29 | .run { if (isArmor) this else withoutPadding() } 30 | .encodeToString(decodedByteArray) 31 | return Pair(this == encodedString, decodedByteArray) 32 | } 33 | 34 | // Writer.newLine() uses System.lineSeparator(), we want to only use \n 35 | internal fun Writer.writeNewLine() { 36 | write("\n") 37 | } 38 | 39 | internal fun Writer.writeSpace() { 40 | write(" ") 41 | } 42 | 43 | internal fun BufferedInputStream.readLine(): String? { 44 | val baos = ByteArrayOutputStream() 45 | 46 | while (true) { 47 | val r = this.read() 48 | 49 | if (r.toChar() == '\n') return baos.toByteArray().decodeToString() 50 | 51 | if (r != -1) baos.write(r) 52 | else if (baos.size() > 0) return baos.toByteArray().decodeToString() else return null 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/test/kotlin/kage/UpstreamTestSuite.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 The kage Authors. All rights reserved. Use of this source code is governed by 3 | * either an Apache 2.0 or MIT license at your discretion, that can be found in the LICENSE-APACHE 4 | * or LICENSE-MIT files respectively. 5 | */ 6 | package kage 7 | 8 | import com.github.michaelbull.result.getError 9 | import com.github.michaelbull.result.runCatching 10 | import com.google.common.truth.Truth.assertThat 11 | import java.io.ByteArrayOutputStream 12 | import java.nio.file.Files 13 | import java.nio.file.Paths 14 | import java.security.MessageDigest 15 | import kage.kage.test.utils.TestSuite 16 | import kage.kage.utils.mapToUpstreamExpect 17 | import kage.test.utils.Expect.Success 18 | import kage.test.utils.PayloadHash 19 | import kotlin.io.path.name 20 | import org.junit.jupiter.api.DynamicTest 21 | import org.junit.jupiter.api.TestFactory 22 | import org.junit.jupiter.api.fail 23 | 24 | class UpstreamTestSuite { 25 | private val testFixtureRoot = Paths.get("src", "test", "resources", "CCTV", "age", "testdata") 26 | 27 | @TestFactory 28 | fun generateTests(): List { 29 | return Files.newDirectoryStream(testFixtureRoot).map { path -> 30 | val contents = path.toFile().readBytes() 31 | DynamicTest.dynamicTest(path.name) { 32 | val suite = TestSuite.parse(contents) 33 | val expect = suite.expect 34 | 35 | val baos = ByteArrayOutputStream() 36 | val result = runCatching { 37 | Age.decryptStream(suite.identities, suite.testContent.inputStream(), baos) 38 | } 39 | 40 | val error = result.getError() 41 | if (error == null) { 42 | if (expect != Success) fail("expected $expect, got success") 43 | suite.payloadHash?.let { expectedHash -> 44 | val md = MessageDigest.getInstance("SHA-256") 45 | val payloadHash = PayloadHash(md.digest(baos.toByteArray())) 46 | assertThat(payloadHash.bytes).isEqualTo(expectedHash.bytes) 47 | } 48 | } else { 49 | error.printStackTrace() 50 | val actual = mapToUpstreamExpect(error) 51 | assertThat(actual).isEqualTo(expect) 52 | } 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/test/kotlin/kage/crypto/scrypt/ScryptRecipientTest.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 The kage Authors. All rights reserved. Use of this source code is governed by 3 | * either an Apache 2.0 or MIT license at your discretion, that can be found in the LICENSE-APACHE 4 | * or LICENSE-MIT files respectively. 5 | */ 6 | package kage.crypto.scrypt 7 | 8 | import com.google.common.truth.Truth.assertThat 9 | import java.security.SecureRandom 10 | import kage.Age 11 | import org.junit.jupiter.api.Test 12 | 13 | class ScryptRecipientTest { 14 | @Test 15 | fun testWrapUnwrap() { 16 | val recipient = ScryptRecipient("mypass".toByteArray(), workFactor = 1) 17 | 18 | val fileKey = ByteArray(Age.FILE_KEY_SIZE) 19 | SecureRandom().nextBytes(fileKey) 20 | 21 | val stanza = recipient.wrap(fileKey).first() 22 | 23 | val identity = ScryptIdentity("mypass".toByteArray()) 24 | 25 | val unwrappedKey = identity.unwrap(listOf(stanza)) 26 | 27 | assertThat(fileKey).asList().containsExactlyElementsIn(unwrappedKey.asList()) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/test/kotlin/kage/crypto/x25519/X25519RecipientTest.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 The kage Authors. All rights reserved. Use of this source code is governed by 3 | * either an Apache 2.0 or MIT license at your discretion, that can be found in the LICENSE-APACHE 4 | * or LICENSE-MIT files respectively. 5 | */ 6 | package kage.kage.crypto.x25519 7 | 8 | import com.google.common.truth.Truth.assertThat 9 | import java.security.SecureRandom 10 | import java.util.Random 11 | import kage.Age 12 | import kage.crypto.x25519.X25519 13 | import kage.crypto.x25519.X25519Identity 14 | import kage.crypto.x25519.X25519Recipient 15 | import kage.errors.X25519LowOrderPointException 16 | import kage.utils.decodeBase64 17 | import org.junit.jupiter.api.Test 18 | import org.junit.jupiter.api.assertThrows 19 | 20 | class X25519RecipientTest { 21 | @Test 22 | fun testWrapUnwrap() { 23 | val privateKey = ByteArray(32) 24 | SecureRandom().nextBytes(privateKey) 25 | val publicKey = X25519.scalarMultBase(privateKey) 26 | 27 | val recipient = X25519Recipient(publicKey) 28 | 29 | val fileKey = ByteArray(Age.FILE_KEY_SIZE) 30 | Random().nextBytes(fileKey) 31 | 32 | val stanza = recipient.wrap(fileKey).first() 33 | 34 | val sharedSecret = stanza.args.first().decodeBase64() 35 | 36 | assertThat(sharedSecret).hasLength(X25519Recipient.EPHEMERAL_SECRET_LEN) 37 | assertThat(stanza.type).isEqualTo(X25519Recipient.X25519_STANZA_TYPE) 38 | 39 | val identity = X25519Identity(privateKey, publicKey) 40 | 41 | val unwrapped = identity.unwrap(listOf(stanza)) 42 | 43 | assertThat(fileKey).asList().containsExactlyElementsIn(unwrapped.asList()) 44 | } 45 | 46 | @Test 47 | fun lowOrderX25519() { 48 | val privateKey = ByteArray(32) 49 | SecureRandom().nextBytes(privateKey) 50 | val sharedSecret = "X5yVvKNQjCSx0LFVnIPvWwREXMRYHI6G2CJO3dCfEdc".decodeBase64() 51 | assertThrows { X25519.scalarMult(privateKey, sharedSecret) } 52 | } 53 | 54 | @Test 55 | fun identityX25519() { 56 | val privateKey = ByteArray(32) 57 | SecureRandom().nextBytes(privateKey) 58 | val sharedSecret = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA".decodeBase64() 59 | assertThrows { X25519.scalarMult(privateKey, sharedSecret) } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/test/kotlin/kage/format/AgeFileTest.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 The kage Authors. All rights reserved. Use of this source code is governed by 3 | * either an Apache 2.0 or MIT license at your discretion, that can be found in the LICENSE-APACHE 4 | * or LICENSE-MIT files respectively. 5 | */ 6 | package kage.kage.format 7 | 8 | import com.google.common.truth.Truth.assertThat 9 | import java.io.ByteArrayInputStream 10 | import kage.crypto.scrypt.ScryptRecipient 11 | import kage.format.AgeFile 12 | import org.bouncycastle.util.encoders.Base64 13 | import org.junit.jupiter.api.Test 14 | 15 | class AgeFileTest { 16 | 17 | @Test 18 | fun testParseAgeGoFile() { 19 | val testFile = 20 | Base64.decode( 21 | "YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNjcnlwdCArNlZtcm5hOWRXdjZTNEFBOFJ2MEZRIDE4CjJISTM0aTYxRHNkVHNMdVVvdXoyWk1jVTM1WUk0R2F1TlJEMDBnL0M2Nk0KLS0tIC9GSXA0eElxS1BqUG80aEJkK2lPNDRibyt1R093Q2IvWVZrTTRxcFJhZk0KUuBcu00b/uzC+wlTRwEHxBI4tR1LlkR5YQ7rvOZXJepwOZ7j9pJNIW3jgbIotYnurGE/6A==" 22 | ) 23 | 24 | val inputStream = ByteArrayInputStream(testFile) 25 | 26 | val ageFile = AgeFile.parse(inputStream) 27 | 28 | val recipient = ageFile.header.recipients.first() 29 | 30 | assertThat(recipient.type).isEqualTo(ScryptRecipient.SCRYPT_STANZA_TYPE) 31 | assertThat(recipient.args).contains(ScryptRecipient.DEFAULT_WORK_FACTOR.toString()) 32 | assertThat(ageFile.body) 33 | .asList() 34 | .containsExactlyElementsIn(testFile.takeLast(ageFile.body.size)) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/test/kotlin/kage/test/utils/Bytes.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 The kage Authors. All rights reserved. Use of this source code is governed by 3 | * either an Apache 2.0 or MIT license at your discretion, that can be found in the LICENSE-APACHE 4 | * or LICENSE-MIT files respectively. 5 | */ 6 | package kage.kage.test.utils 7 | 8 | import com.google.common.truth.Truth.assertThat 9 | import org.junit.jupiter.api.Test 10 | 11 | /** 12 | * Splits the [ByteArray] around the first instance of [byte], returning the bytes before and after 13 | * [byte]. If [byte] cannot be found in the [ByteArray], returns `Pair(this, null, false)`. 14 | */ 15 | fun ByteArray.split(byte: Byte): Triple { 16 | val splitByte = firstOrNull { b -> b == byte } ?: return Triple(this, null, false) 17 | val splitIndex = indexOf(splitByte) 18 | return Triple(copyOfRange(0, splitIndex), copyOfRange(splitIndex + 1, size), true) 19 | } 20 | 21 | /** Shorthand for [ByteArray.split] that takes in a [Char] to simplify usages. */ 22 | fun ByteArray.split(char: Char) = split(char.code.toByte()) 23 | 24 | class BytesTest { 25 | @Test 26 | fun splitNewline() { 27 | val testString = 28 | """ 29 | Line 1 30 | Line 2 31 | """ 32 | .trimIndent() 33 | val (line, rest, found) = check(testString, '\n') 34 | assertThat(found).isTrue() 35 | assertThat(line.decodeToString()).isEqualTo("Line 1") 36 | assertThat(rest?.decodeToString()).isEqualTo("Line 2") 37 | } 38 | 39 | @Test 40 | fun splitNotFound() { 41 | val testString = "Line 1" 42 | val (line, rest, found) = check(testString, '|') 43 | assertThat(found).isFalse() 44 | assertThat(line.decodeToString()).isEqualTo(testString) 45 | assertThat(rest).isNull() 46 | } 47 | 48 | private fun check(testString: String, split: Char) = testString.encodeToByteArray().split(split) 49 | } 50 | -------------------------------------------------------------------------------- /src/test/kotlin/kage/test/utils/TestSuite.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 The kage Authors. All rights reserved. Use of this source code is governed by 3 | * either an Apache 2.0 or MIT license at your discretion, that can be found in the LICENSE-APACHE 4 | * or LICENSE-MIT files respectively. 5 | */ 6 | package kage.kage.test.utils 7 | 8 | import kage.Identity 9 | import kage.crypto.scrypt.ScryptIdentity 10 | import kage.crypto.x25519.X25519Identity 11 | import kage.test.utils.Expect 12 | import kage.test.utils.PayloadHash 13 | 14 | class TestSuite 15 | private constructor( 16 | val expect: Expect, 17 | val payloadHash: PayloadHash?, 18 | val identities: List, 19 | val armored: Boolean, 20 | val testContent: ByteArray, 21 | ) { 22 | companion object { 23 | fun parse(contents: ByteArray): TestSuite { 24 | var expect: Expect? = null 25 | var payloadHash: PayloadHash? = null 26 | val identities = mutableListOf() 27 | var armored = false 28 | 29 | var remaining = contents 30 | do { 31 | val (line, rest, found) = remaining.split('\n') 32 | if (!found) { 33 | error("invalid test file: no payload") 34 | } 35 | if (rest != null) remaining = rest 36 | if (line.isEmpty()) { 37 | break 38 | } 39 | val (key, value) = line.decodeToString().split(": ") 40 | when (key) { 41 | "expect" -> expect = Expect.fromString(value) 42 | "payload" -> payloadHash = PayloadHash.from(value) 43 | "identity" -> identities.add(X25519Identity.decode(value)) 44 | "passphrase" -> identities.add(ScryptIdentity(value.encodeToByteArray())) 45 | "armored" -> armored = true 46 | "file key" -> {} 47 | "comment" -> {} 48 | else -> error("invalid test file: unknown header key: $key") 49 | } 50 | } while (true) 51 | 52 | check(expect != null) { "invalid test file: no 'expect' header found" } 53 | check((expect != Expect.Success || expect != Expect.PayloadFailure) || payloadHash != null) { 54 | // This check verifies that the payload is present except when expectation is either 55 | // Success or PayloadFailure 56 | "invalid test file: no 'payload' header found" 57 | } 58 | 59 | return TestSuite(expect, payloadHash, identities, armored, remaining) 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/test/kotlin/kage/test/utils/types.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 The kage Authors. All rights reserved. Use of this source code is governed by 3 | * either an Apache 2.0 or MIT license at your discretion, that can be found in the LICENSE-APACHE 4 | * or LICENSE-MIT files respectively. 5 | */ 6 | package kage.test.utils 7 | 8 | enum class Expect { 9 | Success, 10 | HMACFailure, 11 | HeaderFailure, 12 | ArmorFailure, 13 | PayloadFailure, 14 | NoMatch; 15 | 16 | companion object { 17 | fun fromString(value: String): Expect { 18 | return when (value) { 19 | "success" -> Success 20 | "HMAC failure" -> HMACFailure 21 | "header failure" -> HeaderFailure 22 | "armor failure" -> ArmorFailure 23 | "payload failure" -> PayloadFailure 24 | "no match" -> NoMatch 25 | else -> throw IllegalArgumentException("unknown expect value: $value") 26 | } 27 | } 28 | } 29 | } 30 | 31 | class PayloadHash(val bytes: ByteArray) { 32 | companion object { 33 | fun from(value: String): PayloadHash { 34 | check(value.length % 2 == 0) { "Must have an even length" } 35 | val hex = value.chunked(2).map { it.toInt(16).toByte() }.toByteArray() 36 | check(hex.size == 32) { "Payload hash should be exactly 32 bytes" } 37 | return PayloadHash(hex) 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/test/kotlin/kage/utils/Extensions.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 The kage Authors. All rights reserved. Use of this source code is governed by 3 | * either an Apache 2.0 or MIT license at your discretion, that can be found in the LICENSE-APACHE 4 | * or LICENSE-MIT files respectively. 5 | */ 6 | package kage.kage.utils 7 | 8 | import kage.errors.ArmorCodingException 9 | import kage.errors.CryptoException 10 | import kage.errors.IncorrectHMACException 11 | import kage.errors.IncorrectIdentityException 12 | import kage.errors.InvalidHMACHeaderException 13 | import kage.errors.InvalidIdentityException 14 | import kage.errors.ParseException 15 | import kage.errors.StreamException 16 | import kage.test.utils.Expect 17 | import kage.test.utils.Expect.ArmorFailure 18 | import kage.test.utils.Expect.HMACFailure 19 | import kage.test.utils.Expect.HeaderFailure 20 | import kage.test.utils.Expect.NoMatch 21 | import kage.test.utils.Expect.PayloadFailure 22 | import org.bouncycastle.crypto.InvalidCipherTextException 23 | 24 | private inline fun hasCause(error: Throwable): Boolean { 25 | var cause = error.cause 26 | while (cause != null) { 27 | if (error.cause is T) return true 28 | cause = cause.cause 29 | } 30 | return false 31 | } 32 | 33 | fun mapToUpstreamExpect(error: Throwable): Expect { 34 | if (error is InvalidIdentityException && hasCause(error)) 35 | return NoMatch 36 | if (error is IncorrectHMACException || hasCause(error)) return HMACFailure 37 | if (error is InvalidHMACHeaderException) return HeaderFailure 38 | if (error is IncorrectIdentityException) return NoMatch 39 | if (error is StreamException) return PayloadFailure 40 | if (error is ParseException) return HeaderFailure 41 | if (error is ArmorCodingException) return ArmorFailure 42 | if (error is CryptoException) return HeaderFailure 43 | 44 | // TODO: Handle cases where we are throwing anything other than CryptoException or ParseException 45 | // throw IllegalStateException("Only exceptions thrown by kage can be mapped to expect value") 46 | return HeaderFailure 47 | } 48 | -------------------------------------------------------------------------------- /src/test/kotlin/kage/utils/ExtensionsTest.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 The kage Authors. All rights reserved. Use of this source code is governed by 3 | * either an Apache 2.0 or MIT license at your discretion, that can be found in the LICENSE-APACHE 4 | * or LICENSE-MIT files respectively. 5 | */ 6 | package kage.utils 7 | 8 | import com.google.common.truth.Truth.assertThat 9 | import kage.errors.InvalidBase64StringException 10 | import org.junit.jupiter.api.Test 11 | import org.junit.jupiter.api.assertDoesNotThrow 12 | import org.junit.jupiter.api.assertThrows 13 | 14 | class ExtensionsTest { 15 | 16 | @Test 17 | fun testIsCanonicalBase6() { 18 | val canonicalString = "rF0/NwblUHHTpgQgRpe5CQ" 19 | val nonCanonicalString = "rF0/NwblUHHTpgQgRpe5CR" 20 | assertThat(canonicalString.isCanonicalBase64().first).isTrue() 21 | assertThat(canonicalString.isCanonicalBase64().second) 22 | .isEqualTo(byteArrayOf(-84, 93, 63, 55, 6, -27, 80, 113, -45, -90, 4, 32, 70, -105, -71, 9)) 23 | assertThat(nonCanonicalString.isCanonicalBase64().first).isFalse() 24 | assertThat(nonCanonicalString.isCanonicalBase64().second) 25 | .isEqualTo(byteArrayOf(-84, 93, 63, 55, 6, -27, 80, 113, -45, -90, 4, 32, 70, -105, -71, 9)) 26 | } 27 | 28 | @Test 29 | fun testDecodeBase64() { 30 | val canonicalString = "rF0/NwblUHHTpgQgRpe5CQ" 31 | val nonCanonicalString = "rF0/NwblUHHTpgQgRpe5CR" 32 | 33 | assertDoesNotThrow { canonicalString.decodeBase64() } 34 | assertThrows { nonCanonicalString.decodeBase64() } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/README.md: -------------------------------------------------------------------------------- 1 | # The Community Cryptography Test Vectors 2 | 3 | CCTV is an experiment, part of the [Community Cryptography Specification Project](https://c2sp.org), in test vector collection and reuse. 4 | 5 | All cryptography-related test vectors are welcome, and projects are encouraged to reuse them and contribute back any new vectors they generate. 6 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/go.mod: -------------------------------------------------------------------------------- 1 | module c2sp.org/CCTV/age 2 | 3 | go 1.19 4 | 5 | require golang.org/x/crypto v0.1.0 6 | 7 | require golang.org/x/sys v0.1.0 // indirect 8 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/go.sum: -------------------------------------------------------------------------------- 1 | golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= 2 | golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= 3 | golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= 4 | golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 5 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019 The age Authors 2 | Copyright 2022 The C2SP Authors 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above 11 | copyright notice, this list of conditions and the following disclaimer 12 | in the documentation and/or other materials provided with the 13 | distribution. 14 | * Neither the name of the age project nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/generate.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The C2SP Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "log" 9 | "os" 10 | "os/exec" 11 | "path/filepath" 12 | "strings" 13 | ) 14 | 15 | //go:generate go run generate.go 16 | 17 | func main() { 18 | log.SetFlags(0) 19 | tests, err := filepath.Glob("../testdata/*") 20 | if err != nil { 21 | log.Fatal(err) 22 | } 23 | for _, test := range tests { 24 | os.Remove(test) 25 | } 26 | generators, err := filepath.Glob("tests/*.go") 27 | if err != nil { 28 | log.Fatal(err) 29 | } 30 | for _, generator := range generators { 31 | vector := strings.TrimSuffix(generator, ".go") 32 | vector = "../testdata/" + strings.TrimPrefix(vector, "tests/") 33 | log.Printf("%s -> %s\n", generator, vector) 34 | out, err := exec.Command("go", "run", generator).Output() 35 | if err != nil { 36 | if err, ok := err.(*exec.ExitError); ok { 37 | log.Fatalf("%s", err.Stderr) 38 | } 39 | log.Fatal(err) 40 | } 41 | os.WriteFile(vector, out, 0664) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/armor.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Recipient) 16 | f.HMAC() 17 | f.Payload("age") 18 | file := f.Bytes() 19 | f.Buf.Reset() 20 | f.BeginArmor("AGE ENCRYPTED FILE") 21 | f.Body(file) 22 | f.Base64Padding() 23 | f.EndArmor("AGE ENCRYPTED FILE") 24 | f.Generate() 25 | } 26 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/armor_crlf.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import ( 11 | "bytes" 12 | 13 | "c2sp.org/CCTV/age/internal/testkit" 14 | ) 15 | 16 | func main() { 17 | f := testkit.NewTestFile() 18 | f.VersionLine("v1") 19 | f.X25519(testkit.TestX25519Recipient) 20 | f.HMAC() 21 | f.Payload("age") 22 | file := f.Bytes() 23 | f.Buf.Reset() 24 | f.BeginArmor("AGE ENCRYPTED FILE") 25 | f.Body(file) 26 | f.Base64Padding() 27 | f.EndArmor("AGE ENCRYPTED FILE") 28 | armored := f.Bytes() 29 | f.Buf.Reset() 30 | f.Buf.Write(bytes.Replace(armored, []byte("\n"), []byte("\r\n"), -1)) 31 | f.Comment("CRLF is allowed as a end of line for armored files") 32 | f.Generate() 33 | } 34 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/armor_empty_line_begin.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Recipient) 16 | f.HMAC() 17 | f.Payload("age") 18 | file := f.Bytes() 19 | f.Buf.Reset() 20 | f.BeginArmor("AGE ENCRYPTED FILE") 21 | f.TextLine("") 22 | f.Body(file) 23 | f.Base64Padding() 24 | f.EndArmor("AGE ENCRYPTED FILE") 25 | f.ExpectArmorFailure() 26 | f.Generate() 27 | } 28 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/armor_empty_line_end.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Recipient) 16 | f.HMAC() 17 | f.Payload("age") 18 | file := f.Bytes() 19 | f.Buf.Reset() 20 | f.BeginArmor("AGE ENCRYPTED FILE") 21 | f.Body(file) 22 | f.Base64Padding() 23 | f.TextLine("") 24 | f.EndArmor("AGE ENCRYPTED FILE") 25 | f.ExpectArmorFailure() 26 | f.Generate() 27 | } 28 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/armor_eol_between_padding.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import ( 11 | "strings" 12 | 13 | "c2sp.org/CCTV/age/internal/testkit" 14 | ) 15 | 16 | // See base64finl in RFC 7468. 17 | // ; ...AB= = is not good, but is valid 18 | 19 | func main() { 20 | f := testkit.NewTestFile() 21 | f.VersionLine("v1") 22 | f.X25519(testkit.TestX25519Recipient) 23 | f.HMAC() 24 | f.Payload("age12") 25 | file := f.Bytes() 26 | f.Buf.Reset() 27 | f.BeginArmor("AGE ENCRYPTED FILE") 28 | f.Body(file) 29 | f.Base64Padding() 30 | line := f.UnreadLine() 31 | if !strings.Contains(line, "==") { 32 | panic("need two padding characters") 33 | } 34 | line = strings.Replace(line, "==", "=\n=", 1) 35 | f.TextLine(line) 36 | f.EndArmor("AGE ENCRYPTED FILE") 37 | f.ExpectArmorFailure() 38 | f.Generate() 39 | } 40 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/armor_full_last_line.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Recipient) 16 | f.HMAC() 17 | f.Payload("age age age age age age age age age age ") 18 | file := f.Bytes() 19 | f.Buf.Reset() 20 | f.BeginArmor("AGE ENCRYPTED FILE") 21 | if len(file)%48 != 0 { 22 | println(len(file) % 48) 23 | panic("last line is not full") 24 | } 25 | f.Body(file) 26 | f.UnreadLine() // Body leaves an empty line, PEM doesn't. 27 | f.EndArmor("AGE ENCRYPTED FILE") 28 | f.Generate() 29 | } 30 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/armor_garbage_encoded.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.FileKey(testkit.LargeTestFileKey) 15 | f.VersionLine("v1") 16 | f.X25519(testkit.TestX25519Identity) 17 | f.HMAC() 18 | f.Nonce(testkit.LargeTestNonce) 19 | f.PayloadChunkFinal(testkit.LargeTestFirstChunk) 20 | f.Buf.Write(f.Rand(20)) 21 | f.ExpectPartialPayload(64 * 1024) 22 | file := f.Bytes() 23 | f.Buf.Reset() 24 | f.BeginArmor("AGE ENCRYPTED FILE") 25 | f.Body(file) 26 | f.Base64Padding() 27 | f.EndArmor("AGE ENCRYPTED FILE") 28 | f.Comment("there is trailing garbage encoded after the final chunk") 29 | f.Generate() 30 | } 31 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/armor_garbage_leading.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Recipient) 16 | f.HMAC() 17 | f.Payload("age") 18 | file := f.Bytes() 19 | f.Buf.Reset() 20 | f.TextLine("garbage") 21 | f.BeginArmor("AGE ENCRYPTED FILE") 22 | f.Body(file) 23 | f.Base64Padding() 24 | f.EndArmor("AGE ENCRYPTED FILE") 25 | f.ExpectArmorFailure() 26 | f.Generate() 27 | } 28 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/armor_garbage_trailing.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Recipient) 16 | f.HMAC() 17 | f.Payload("age") 18 | file := f.Bytes() 19 | f.Buf.Reset() 20 | f.BeginArmor("AGE ENCRYPTED FILE") 21 | f.Body(file) 22 | f.Base64Padding() 23 | f.EndArmor("AGE ENCRYPTED FILE") 24 | f.TextLine("garbage") 25 | f.ExpectArmorFailure() 26 | f.Generate() 27 | } 28 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/armor_header_crlf.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import ( 11 | "bytes" 12 | 13 | "c2sp.org/CCTV/age/internal/testkit" 14 | ) 15 | 16 | func main() { 17 | f := testkit.NewTestFile() 18 | f.VersionLine("v1") 19 | f.X25519(testkit.TestX25519Identity) 20 | hdr := f.Buf.Bytes() 21 | f.Buf.Reset() 22 | f.Buf.Write(bytes.Replace(hdr, []byte("\n"), []byte("\r\n"), -1)) 23 | f.HMAC() 24 | f.Buf.WriteString(f.UnreadLine() + "\r\n") 25 | f.Payload("age") 26 | f.ExpectHeaderFailure() 27 | f.Comment("lines in the header end with CRLF instead of LF") 28 | file := f.Bytes() 29 | f.Buf.Reset() 30 | f.BeginArmor("AGE ENCRYPTED FILE") 31 | f.Body(file) 32 | f.Base64Padding() 33 | f.EndArmor("AGE ENCRYPTED FILE") 34 | f.Generate() 35 | } 36 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/armor_headers.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Recipient) 16 | f.HMAC() 17 | f.Payload("age") 18 | file := f.Bytes() 19 | f.Buf.Reset() 20 | f.BeginArmor("AGE ENCRYPTED FILE") 21 | f.TextLine("Headers: are") 22 | f.TextLine("Not: allowed") 23 | f.TextLine("") 24 | f.Body(file) 25 | f.Base64Padding() 26 | f.EndArmor("AGE ENCRYPTED FILE") 27 | f.ExpectArmorFailure() 28 | f.Generate() 29 | } 30 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/armor_invalid_character_header.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import ( 11 | "strings" 12 | 13 | "c2sp.org/CCTV/age/internal/testkit" 14 | ) 15 | 16 | func main() { 17 | f := testkit.NewTestFile() 18 | f.VersionLine("v1") 19 | f.X25519(testkit.TestX25519Recipient) 20 | f.HMAC() 21 | f.Payload("age") 22 | file := f.Bytes() 23 | f.Buf.Reset() 24 | f.BeginArmor("AGE ENCRYPTED FILE") 25 | f.Body(file) 26 | f.Base64Padding() 27 | begin, rest, _ := strings.Cut(string(f.Bytes()), "\n") 28 | f.Buf.Reset() 29 | f.TextLine(begin) 30 | f.Buf.WriteString(rest[:4] + "*" + rest[5:]) 31 | f.EndArmor("AGE ENCRYPTED FILE") 32 | f.ExpectArmorFailure() 33 | f.Generate() 34 | } 35 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/armor_invalid_character_payload.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Recipient) 16 | f.HMAC() 17 | f.Payload("age") 18 | file := f.Bytes() 19 | f.Buf.Reset() 20 | f.BeginArmor("AGE ENCRYPTED FILE") 21 | f.Body(file) 22 | f.Base64Padding() 23 | line := f.UnreadLine() 24 | f.TextLine("*" + line[1:]) 25 | f.EndArmor("AGE ENCRYPTED FILE") 26 | f.ExpectArmorFailure() 27 | f.Generate() 28 | } 29 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/armor_long_line.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import ( 11 | "encoding/base64" 12 | 13 | "c2sp.org/CCTV/age/internal/testkit" 14 | ) 15 | 16 | func main() { 17 | f := testkit.NewTestFile() 18 | f.VersionLine("v1") 19 | f.X25519(testkit.TestX25519Recipient) 20 | f.HMAC() 21 | f.Payload("age") 22 | file := f.Bytes() 23 | f.Buf.Reset() 24 | f.BeginArmor("AGE ENCRYPTED FILE") 25 | f.TextLine(base64.StdEncoding.EncodeToString(file)) 26 | f.Base64Padding() 27 | f.EndArmor("AGE ENCRYPTED FILE") 28 | f.ExpectArmorFailure() 29 | f.Generate() 30 | } 31 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/armor_lowercase.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Recipient) 16 | f.HMAC() 17 | f.Payload("age") 18 | file := f.Bytes() 19 | f.Buf.Reset() 20 | f.BeginArmor("age ENCRYPTED FILE") 21 | f.Body(file) 22 | f.Base64Padding() 23 | f.EndArmor("age ENCRYPTED FILE") 24 | f.ExpectArmorFailure() 25 | f.Generate() 26 | } 27 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/armor_no_end_line.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Recipient) 16 | f.HMAC() 17 | f.Payload("age") 18 | file := f.Bytes() 19 | f.Buf.Reset() 20 | f.BeginArmor("AGE ENCRYPTED FILE") 21 | f.Body(file) 22 | f.Base64Padding() 23 | f.ExpectArmorFailure() 24 | f.Generate() 25 | } 26 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/armor_no_eol.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Recipient) 16 | f.HMAC() 17 | f.Payload("age") 18 | file := f.Bytes() 19 | f.Buf.Reset() 20 | f.BeginArmor("AGE ENCRYPTED FILE") 21 | f.Body(file) 22 | f.Base64Padding() 23 | f.Buf.WriteString("-----END AGE ENCRYPTED FILE-----") 24 | f.Comment("there is no end of line at the end of the file") 25 | f.Generate() 26 | } 27 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/armor_no_match.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | identity := f.Rand(32) 16 | f.X25519RecordIdentity(identity) 17 | f.X25519NoRecordIdentity(testkit.TestX25519Recipient) 18 | f.HMAC() 19 | f.Payload("age") 20 | f.ExpectNoMatch() 21 | file := f.Bytes() 22 | f.Buf.Reset() 23 | f.BeginArmor("AGE ENCRYPTED FILE") 24 | f.Body(file) 25 | f.Base64Padding() 26 | f.EndArmor("AGE ENCRYPTED FILE") 27 | f.Generate() 28 | } 29 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/armor_no_padding.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Recipient) 16 | f.HMAC() 17 | f.Payload("age") 18 | file := f.Bytes() 19 | f.Buf.Reset() 20 | f.BeginArmor("AGE ENCRYPTED FILE") 21 | if len(file)%3 == 0 { 22 | panic("no need for padding") 23 | } 24 | f.Body(file) 25 | f.EndArmor("AGE ENCRYPTED FILE") 26 | f.ExpectArmorFailure() 27 | f.Comment("missing base64 padding") 28 | f.Generate() 29 | } 30 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/armor_not_canonical.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Recipient) 16 | f.HMAC() 17 | f.Payload("age") 18 | file := f.Bytes() 19 | f.Buf.Reset() 20 | f.BeginArmor("AGE ENCRYPTED FILE") 21 | f.Body(file) 22 | f.TextLine(testkit.NotCanonicalBase64(f.UnreadLine())) 23 | f.Base64Padding() 24 | f.EndArmor("AGE ENCRYPTED FILE") 25 | f.ExpectArmorFailure() 26 | f.Comment("base64 is not canonical") 27 | f.Generate() 28 | } 29 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/armor_pgp_checksum.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import ( 11 | "c2sp.org/CCTV/age/internal/testkit" 12 | "golang.org/x/crypto/openpgp/armor" 13 | ) 14 | 15 | func main() { 16 | f := testkit.NewTestFile() 17 | f.VersionLine("v1") 18 | f.X25519(testkit.TestX25519Recipient) 19 | f.HMAC() 20 | f.Payload("age") 21 | file := f.Bytes() 22 | f.Buf.Reset() 23 | w, _ := armor.Encode(&f.Buf, "AGE ENCRYPTED FILE", nil) 24 | w.Write(file) 25 | w.Close() 26 | f.Buf.WriteString("\n") 27 | f.ExpectArmorFailure() 28 | f.Generate() 29 | } 30 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/armor_short_line.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Recipient) 16 | f.HMAC() 17 | f.Payload("age") 18 | file := f.Bytes() 19 | f.Buf.Reset() 20 | f.BeginArmor("AGE ENCRYPTED FILE") 21 | f.Body(file[:12]) 22 | f.Body(file[12:]) 23 | f.Base64Padding() 24 | f.EndArmor("AGE ENCRYPTED FILE") 25 | f.ExpectArmorFailure() 26 | f.Generate() 27 | } 28 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/armor_whitespace_begin.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Recipient) 16 | f.HMAC() 17 | f.Payload("age") 18 | file := f.Bytes() 19 | f.Buf.Reset() 20 | f.TextLine("----- BEGIN AGE ENCRYPTED FILE -----") 21 | f.Body(file) 22 | f.Base64Padding() 23 | f.EndArmor("AGE ENCRYPTED FILE") 24 | f.ExpectArmorFailure() 25 | f.Generate() 26 | } 27 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/armor_whitespace_end.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Recipient) 16 | f.HMAC() 17 | f.Payload("age") 18 | file := f.Bytes() 19 | f.Buf.Reset() 20 | f.BeginArmor("AGE ENCRYPTED FILE") 21 | f.Body(file) 22 | f.Base64Padding() 23 | f.TextLine("----- END AGE ENCRYPTED FILE -----") 24 | f.ExpectArmorFailure() 25 | f.Generate() 26 | } 27 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/armor_whitespace_eol.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Recipient) 16 | f.HMAC() 17 | f.Payload("age") 18 | file := f.Bytes() 19 | f.Buf.Reset() 20 | f.BeginArmor("AGE ENCRYPTED FILE") 21 | f.Body(file) 22 | f.Base64Padding() 23 | line2, line1 := f.UnreadLine(), f.UnreadLine() 24 | f.TextLine(line1 + " ") 25 | f.TextLine(line2) 26 | f.EndArmor("AGE ENCRYPTED FILE") 27 | f.ExpectArmorFailure() 28 | f.Generate() 29 | } 30 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/armor_whitespace_last_line.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Recipient) 16 | f.HMAC() 17 | f.Payload("age") 18 | file := f.Bytes() 19 | f.Buf.Reset() 20 | f.BeginArmor("AGE ENCRYPTED FILE") 21 | f.Body(file) 22 | f.Base64Padding() 23 | f.TextLine(f.UnreadLine() + " ") 24 | f.EndArmor("AGE ENCRYPTED FILE") 25 | f.ExpectArmorFailure() 26 | f.Generate() 27 | } 28 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/armor_whitespace_line_start.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Recipient) 16 | f.HMAC() 17 | f.Payload("age") 18 | file := f.Bytes() 19 | f.Buf.Reset() 20 | f.BeginArmor("AGE ENCRYPTED FILE") 21 | f.Body(file) 22 | f.Base64Padding() 23 | line2, line1 := f.UnreadLine(), f.UnreadLine() 24 | f.TextLine(" " + line1) 25 | f.TextLine(line2) 26 | f.EndArmor("AGE ENCRYPTED FILE") 27 | f.ExpectArmorFailure() 28 | f.Generate() 29 | } 30 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/armor_whitespace_outside.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Recipient) 16 | f.HMAC() 17 | f.Payload("age") 18 | file := f.Bytes() 19 | f.Buf.Reset() 20 | f.Buf.Write([]byte("\n\r \t\n")) 21 | f.BeginArmor("AGE ENCRYPTED FILE") 22 | f.Body(file) 23 | f.Base64Padding() 24 | f.EndArmor("AGE ENCRYPTED FILE") 25 | f.Buf.Write([]byte("\n\r \t\n")) 26 | f.Comment("whitespace is allowed before and after armored files") 27 | f.Generate() 28 | } 29 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/armor_wrong_type.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Recipient) 16 | f.HMAC() 17 | f.Payload("age") 18 | file := f.Bytes() 19 | f.Buf.Reset() 20 | f.BeginArmor("AGE ENCRYPTED MESSAGE") 21 | f.Body(file) 22 | f.Base64Padding() 23 | f.EndArmor("AGE ENCRYPTED MESSAGE") 24 | f.ExpectArmorFailure() 25 | f.Generate() 26 | } 27 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/header_crlf.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import ( 11 | "bytes" 12 | 13 | "c2sp.org/CCTV/age/internal/testkit" 14 | ) 15 | 16 | func main() { 17 | f := testkit.NewTestFile() 18 | f.VersionLine("v1") 19 | f.X25519(testkit.TestX25519Identity) 20 | hdr := f.Buf.Bytes() 21 | f.Buf.Reset() 22 | f.Buf.Write(bytes.Replace(hdr, []byte("\n"), []byte("\r\n"), -1)) 23 | f.HMAC() 24 | f.Buf.WriteString(f.UnreadLine() + "\r\n") 25 | f.Payload("age") 26 | f.ExpectHeaderFailure() 27 | f.Comment("lines in the header end with CRLF instead of LF") 28 | f.Generate() 29 | } 30 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/hmac_bad.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Identity) 16 | f.FileKey(make([]byte, 16)) 17 | f.HMAC() 18 | f.FileKey(testkit.TestFileKey) 19 | f.Payload("age") 20 | f.ExpectHMACFailure() 21 | f.Generate() 22 | } 23 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/hmac_extra_space.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import ( 11 | "strings" 12 | 13 | "c2sp.org/CCTV/age/internal/testkit" 14 | ) 15 | 16 | func main() { 17 | f := testkit.NewTestFile() 18 | f.VersionLine("v1") 19 | f.X25519(testkit.TestX25519Identity) 20 | f.HMAC() 21 | f.TextLine(strings.Replace(f.UnreadLine(), "--- ", "--- ", -1)) 22 | f.Payload("age") 23 | f.ExpectHeaderFailure() 24 | f.Generate() 25 | } 26 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/hmac_garbage.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Identity) 16 | f.HMAC() 17 | f.TextLine(f.UnreadLine() + "AAA") 18 | f.Payload("age") 19 | f.ExpectHeaderFailure() 20 | f.Generate() 21 | } 22 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/hmac_missing.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Identity) 16 | f.HMACLine(nil) 17 | f.Payload("age") 18 | f.ExpectHeaderFailure() 19 | f.Generate() 20 | } 21 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/hmac_no_space.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import ( 11 | "strings" 12 | 13 | "c2sp.org/CCTV/age/internal/testkit" 14 | ) 15 | 16 | func main() { 17 | f := testkit.NewTestFile() 18 | f.VersionLine("v1") 19 | f.X25519(testkit.TestX25519Identity) 20 | f.HMAC() 21 | f.TextLine(strings.Replace(f.UnreadLine(), "--- ", "---", -1)) 22 | f.Payload("age") 23 | f.ExpectHeaderFailure() 24 | f.Generate() 25 | } 26 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/hmac_not_canonical.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Identity) 16 | f.HMAC() 17 | f.TextLine(testkit.NotCanonicalBase64(f.UnreadLine())) 18 | f.Payload("age") 19 | f.ExpectHeaderFailure() 20 | f.Comment("the base64 encoding of the HMAC is not canonical") 21 | f.Generate() 22 | } 23 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/hmac_trailing_space.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Identity) 16 | f.HMAC() 17 | f.TextLine(f.UnreadLine() + " ") 18 | f.Payload("age") 19 | f.ExpectHeaderFailure() 20 | f.Generate() 21 | } 22 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/hmac_truncated.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Identity) 16 | f.HMAC() 17 | f.TextLine(f.UnreadLine()[:len("--- 1234")]) 18 | f.Payload("age") 19 | f.ExpectHeaderFailure() 20 | f.Generate() 21 | } 22 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/scrypt.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.Scrypt("password", 10) 16 | f.HMAC() 17 | f.Payload("age") 18 | f.Generate() 19 | } 20 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/scrypt_and_x25519.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519RecordIdentity(f.Rand(32)) 16 | f.X25519NoRecordIdentity(testkit.TestX25519Identity) 17 | f.Scrypt("password", 10) 18 | f.HMAC() 19 | f.Payload("age") 20 | f.ExpectHeaderFailure() 21 | f.Comment("scrypt stanzas must be alone in the header") 22 | f.Generate() 23 | } 24 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/scrypt_bad_tag.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import ( 11 | "encoding/base64" 12 | 13 | "c2sp.org/CCTV/age/internal/testkit" 14 | ) 15 | 16 | func main() { 17 | f := testkit.NewTestFile() 18 | f.VersionLine("v1") 19 | f.Scrypt("password", 10) 20 | body, _ := base64.RawStdEncoding.DecodeString(f.UnreadLine()) 21 | body[len(body)-1] ^= 0xff 22 | f.TextLine(base64.RawStdEncoding.EncodeToString(body)) 23 | f.HMAC() 24 | f.Payload("age") 25 | f.ExpectNoMatch() 26 | f.Comment("the ChaCha20Poly1305 authentication tag on the body of the scrypt stanza is wrong") 27 | f.Generate() 28 | } 29 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/scrypt_double.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.Scrypt("password", 10) 16 | f.Scrypt("hunter2", 10) 17 | f.HMAC() 18 | f.Payload("age") 19 | f.ExpectHeaderFailure() 20 | f.Comment("scrypt stanzas must be alone in the header") 21 | f.Generate() 22 | } 23 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/scrypt_extra_argument.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.Scrypt("password", 10) 16 | body, args := f.UnreadLine(), f.UnreadLine() 17 | f.TextLine(args + " 10") 18 | f.TextLine(body) 19 | f.HMAC() 20 | f.Payload("age") 21 | f.ExpectHeaderFailure() 22 | f.Comment("the base64 encoding of the share is not canonical") 23 | f.Generate() 24 | } 25 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/scrypt_long_file_key.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.FileKey([]byte("A LONGER YELLOW SUBMARINE")) 15 | f.VersionLine("v1") 16 | f.Scrypt("password", 10) 17 | f.HMAC() 18 | f.Payload("age") 19 | f.ExpectHeaderFailure() 20 | f.Comment("the file key must be checked to be 16 bytes before decrypting it") 21 | f.Generate() 22 | } 23 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/scrypt_no_match.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.ScryptRecordPassphrase("wrong") 16 | f.ScryptNoRecordPassphrase("password", 10) 17 | f.HMAC() 18 | f.Payload("age") 19 | f.ExpectNoMatch() 20 | f.Generate() 21 | } 22 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/scrypt_not_canonical_body.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.Scrypt("password", 10) 16 | body := f.UnreadLine() 17 | f.TextLine(testkit.NotCanonicalBase64(body)) 18 | f.HMAC() 19 | f.Payload("age") 20 | f.ExpectHeaderFailure() 21 | f.Comment("the base64 encoding of the share is not canonical") 22 | f.Generate() 23 | } 24 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/scrypt_not_canonical_salt.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.Scrypt("password", 10) 16 | body, args := f.UnreadLine(), f.UnreadArgsLine() 17 | f.ArgsLine(args[0], testkit.NotCanonicalBase64(args[1]), args[2]) 18 | f.TextLine(body) 19 | f.HMAC() 20 | f.Payload("age") 21 | f.ExpectHeaderFailure() 22 | f.Generate() 23 | } 24 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/scrypt_salt_long.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.ScryptRecordPassphrase("password") 16 | f.ScryptNoRecordPassphraseWithSalt("password", 10, f.Rand(20)) 17 | f.HMAC() 18 | f.Payload("age") 19 | f.ExpectHeaderFailure() 20 | f.Generate() 21 | } 22 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/scrypt_salt_missing.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.ScryptRecordPassphrase("password") 16 | f.ScryptNoRecordPassphraseWithSalt("password", 10, nil) 17 | body, args := f.UnreadLine(), f.UnreadArgsLine() 18 | f.ArgsLine(args[0], args[2]) 19 | f.TextLine(body) 20 | f.HMAC() 21 | f.Payload("age") 22 | f.ExpectHeaderFailure() 23 | f.Generate() 24 | } 25 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/scrypt_salt_short.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.ScryptRecordPassphrase("password") 16 | f.ScryptNoRecordPassphraseWithSalt("password", 10, f.Rand(12)) 17 | f.HMAC() 18 | f.Payload("age") 19 | f.ExpectHeaderFailure() 20 | f.Generate() 21 | } 22 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/scrypt_uppercase.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.Scrypt("password", 10) 16 | body, args := f.UnreadLine(), f.UnreadArgsLine() 17 | f.ArgsLine("Scrypt", args[1], args[2]) 18 | f.TextLine(body) 19 | f.HMAC() 20 | f.Payload("age") 21 | f.ExpectNoMatch() 22 | f.Generate() 23 | } 24 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/scrypt_work_factor_23.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | // Hardcoded because it would be too slow to regenerate every time. 16 | // f.Scrypt("password", 23) 17 | f.ScryptRecordPassphrase("password") 18 | f.ArgsLine("scrypt", "rF0/NwblUHHTpgQgRpe5CQ", "23") 19 | f.TextLine("qW9eVsT0NVb/Vswtw8kPIxUnaYmm9Px1dYmq2+4+qZA") 20 | f.HMAC() 21 | f.Payload("age") 22 | f.ExpectHeaderFailure() 23 | f.Comment("work factor is very high, would take a long time to compute") 24 | f.Generate() 25 | } 26 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/scrypt_work_factor_hex.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.Scrypt("password", 10) 16 | body, args := f.UnreadLine(), f.UnreadArgsLine() 17 | f.ArgsLine(args[0], args[1], "0xa") 18 | f.TextLine(body) 19 | f.HMAC() 20 | f.Payload("age") 21 | f.ExpectHeaderFailure() 22 | f.Generate() 23 | } 24 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/scrypt_work_factor_leading_garbage.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.Scrypt("password", 10) 16 | body, args := f.UnreadLine(), f.UnreadArgsLine() 17 | f.ArgsLine(args[0], args[1], "aaaa10") 18 | f.TextLine(body) 19 | f.HMAC() 20 | f.Payload("age") 21 | f.ExpectHeaderFailure() 22 | f.Generate() 23 | } 24 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/scrypt_work_factor_leading_plus.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.Scrypt("password", 10) 16 | body, args := f.UnreadLine(), f.UnreadArgsLine() 17 | f.ArgsLine(args[0], args[1], "+10") 18 | f.TextLine(body) 19 | f.HMAC() 20 | f.Payload("age") 21 | f.ExpectHeaderFailure() 22 | f.Generate() 23 | } 24 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/scrypt_work_factor_leading_zero_decimal.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.Scrypt("password", 10) 16 | body, args := f.UnreadLine(), f.UnreadArgsLine() 17 | f.ArgsLine(args[0], args[1], "010") 18 | f.TextLine(body) 19 | f.HMAC() 20 | f.Payload("age") 21 | f.ExpectHeaderFailure() 22 | f.Generate() 23 | } 24 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/scrypt_work_factor_leading_zero_octal.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.Scrypt("password", 10) 16 | body, args := f.UnreadLine(), f.UnreadArgsLine() 17 | f.ArgsLine(args[0], args[1], "012") 18 | f.TextLine(body) 19 | f.HMAC() 20 | f.Payload("age") 21 | f.ExpectHeaderFailure() 22 | f.Generate() 23 | } 24 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/scrypt_work_factor_missing.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.Scrypt("password", 18) // cmd/age default 16 | body, args := f.UnreadLine(), f.UnreadArgsLine() 17 | f.ArgsLine(args[0], args[1]) 18 | f.TextLine(body) 19 | f.HMAC() 20 | f.Payload("age") 21 | f.ExpectHeaderFailure() 22 | f.Generate() 23 | } 24 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/scrypt_work_factor_negative.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.Scrypt("password", 10) 16 | body, args := f.UnreadLine(), f.UnreadArgsLine() 17 | f.ArgsLine(args[0], args[1], "-10") 18 | f.TextLine(body) 19 | f.HMAC() 20 | f.Payload("age") 21 | f.ExpectHeaderFailure() 22 | f.Generate() 23 | } 24 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/scrypt_work_factor_overflow.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import ( 11 | "math" 12 | "strconv" 13 | 14 | "c2sp.org/CCTV/age/internal/testkit" 15 | ) 16 | 17 | func main() { 18 | f := testkit.NewTestFile() 19 | f.VersionLine("v1") 20 | f.Scrypt("password", 10) 21 | body, args := f.UnreadLine(), f.UnreadArgsLine() 22 | f.ArgsLine(args[0], args[1], strconv.FormatUint(math.MaxInt64+1+10, 10)) 23 | f.TextLine(body) 24 | f.HMAC() 25 | f.Payload("age") 26 | f.ExpectHeaderFailure() 27 | f.Generate() 28 | } 29 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/scrypt_work_factor_trailing_garbage.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.Scrypt("password", 10) 16 | body, args := f.UnreadLine(), f.UnreadArgsLine() 17 | f.ArgsLine(args[0], args[1], "aaaa10") 18 | f.TextLine(body) 19 | f.HMAC() 20 | f.Payload("age") 21 | f.ExpectHeaderFailure() 22 | f.Generate() 23 | } 24 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/scrypt_work_factor_wrong.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.Scrypt("password", 18) // cmd/go default 16 | body, args := f.UnreadLine(), f.UnreadArgsLine() 17 | f.ArgsLine(args[0], args[1], "10") 18 | f.TextLine(body) 19 | f.HMAC() 20 | f.Payload("age") 21 | f.ExpectNoMatch() 22 | f.Generate() 23 | } 24 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/scrypt_work_factor_zero.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.Scrypt("password", 10) 16 | body, args := f.UnreadLine(), f.UnreadArgsLine() 17 | f.ArgsLine(args[0], args[1], "0") 18 | f.TextLine(body) 19 | f.HMAC() 20 | f.Payload("age") 21 | f.ExpectHeaderFailure() 22 | f.Generate() 23 | } 24 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/stanza_bad_start.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Recipient) 16 | f.TextLine("-- stanza") 17 | f.Body([]byte("")) 18 | f.HMAC() 19 | f.Payload("age") 20 | f.ExpectHeaderFailure() 21 | f.Generate() 22 | } 23 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/stanza_base64_padding.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import ( 11 | "bytes" 12 | 13 | "c2sp.org/CCTV/age/internal/testkit" 14 | ) 15 | 16 | func main() { 17 | f := testkit.NewTestFile() 18 | f.VersionLine("v1") 19 | f.X25519(testkit.TestX25519Recipient) 20 | f.ArgsLine("stanza") 21 | f.Body(bytes.Repeat([]byte("A"), 50)) 22 | f.TextLine(f.UnreadLine() + "=") 23 | f.HMAC() 24 | f.Payload("age") 25 | f.ExpectHeaderFailure() 26 | f.Generate() 27 | } 28 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/stanza_empty_argument.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Recipient) 16 | f.ArgsLine("stanza", "", "argument") 17 | f.Body([]byte("")) 18 | f.HMAC() 19 | f.Payload("age") 20 | f.ExpectHeaderFailure() 21 | f.Generate() 22 | } 23 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/stanza_empty_body.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Recipient) 16 | f.ArgsLine("empty") 17 | f.Body([]byte("")) 18 | f.HMAC() 19 | f.Payload("age") 20 | f.Generate() 21 | } 22 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/stanza_empty_last_line.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import ( 11 | "bytes" 12 | 13 | "c2sp.org/CCTV/age/internal/testkit" 14 | ) 15 | 16 | func main() { 17 | f := testkit.NewTestFile() 18 | f.VersionLine("v1") 19 | f.X25519(testkit.TestX25519Recipient) 20 | f.ArgsLine("stanza") 21 | f.Body(bytes.Repeat([]byte("A"), 48*2)) 22 | f.HMAC() 23 | f.Payload("age") 24 | f.Generate() 25 | } 26 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/stanza_invalid_character.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Recipient) 16 | f.ArgsLine("stanza", "è") 17 | f.Body([]byte("")) 18 | f.HMAC() 19 | f.Payload("age") 20 | f.ExpectHeaderFailure() 21 | f.Generate() 22 | } 23 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/stanza_long_line.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import ( 11 | "strings" 12 | 13 | "c2sp.org/CCTV/age/internal/testkit" 14 | ) 15 | 16 | func main() { 17 | f := testkit.NewTestFile() 18 | f.VersionLine("v1") 19 | f.X25519(testkit.TestX25519Recipient) 20 | f.ArgsLine("stanza") 21 | f.TextLine(strings.Repeat("A", 68)) 22 | f.TextLine("") 23 | f.HMAC() 24 | f.Payload("age") 25 | f.ExpectHeaderFailure() 26 | f.Comment("a body line is longer than 64 columns") 27 | f.Generate() 28 | } 29 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/stanza_missing_body.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Recipient) 16 | f.ArgsLine("empty") 17 | // Missing body. 18 | f.HMAC() 19 | f.Payload("age") 20 | f.ExpectHeaderFailure() 21 | f.Comment("every stanza must end with a short body line, even if empty") 22 | f.Generate() 23 | } 24 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/stanza_missing_final_line.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import ( 11 | "strings" 12 | 13 | "c2sp.org/CCTV/age/internal/testkit" 14 | ) 15 | 16 | func main() { 17 | f := testkit.NewTestFile() 18 | f.VersionLine("v1") 19 | f.X25519(testkit.TestX25519Recipient) 20 | f.ArgsLine("stanza") 21 | f.TextLine(strings.Repeat("A", 64)) 22 | f.HMAC() 23 | f.Payload("age") 24 | f.ExpectHeaderFailure() 25 | f.Comment("every stanza must end with a short body line") 26 | f.Generate() 27 | } 28 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/stanza_multiple_short_lines.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import ( 11 | "strings" 12 | 13 | "c2sp.org/CCTV/age/internal/testkit" 14 | ) 15 | 16 | func main() { 17 | f := testkit.NewTestFile() 18 | f.VersionLine("v1") 19 | f.X25519(testkit.TestX25519Recipient) 20 | f.ArgsLine("stanza") 21 | f.TextLine(strings.Repeat("A", 32)) 22 | f.TextLine(strings.Repeat("A", 32)) 23 | f.HMAC() 24 | f.Payload("age") 25 | f.ExpectHeaderFailure() 26 | f.Comment("a short body line ends the stanza") 27 | f.Generate() 28 | } 29 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/stanza_no_arguments.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Recipient) 16 | f.ArgsLine() 17 | f.Body([]byte("")) 18 | f.HMAC() 19 | f.Payload("age") 20 | f.ExpectHeaderFailure() 21 | f.Generate() 22 | } 23 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/stanza_not_canonical.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import ( 11 | "bytes" 12 | 13 | "c2sp.org/CCTV/age/internal/testkit" 14 | ) 15 | 16 | func main() { 17 | f := testkit.NewTestFile() 18 | f.VersionLine("v1") 19 | f.X25519(testkit.TestX25519Recipient) 20 | f.ArgsLine("stanza") 21 | f.Body(bytes.Repeat([]byte("A"), 50)) 22 | f.TextLine(testkit.NotCanonicalBase64(f.UnreadLine())) 23 | f.HMAC() 24 | f.Payload("age") 25 | f.ExpectHeaderFailure() 26 | f.Generate() 27 | } 28 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/stanza_spurious_cr.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import ( 11 | "strings" 12 | 13 | "c2sp.org/CCTV/age/internal/testkit" 14 | ) 15 | 16 | func main() { 17 | f := testkit.NewTestFile() 18 | f.VersionLine("v1") 19 | f.X25519(testkit.TestX25519Recipient) 20 | f.ArgsLine("stanza") 21 | f.TextLine(strings.Repeat("A", 32) + "\r" + strings.Repeat("A", 31)) 22 | f.HMAC() 23 | f.Payload("age") 24 | f.ExpectHeaderFailure() 25 | f.Generate() 26 | } 27 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/stanza_valid_characters.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.ArgsLine("!\"#$%&'", "()*+,-./", "01234567", "89:;<=>?", "@ABCDEFG", "HIJKLMNO") 16 | f.Body([]byte("")) 17 | f.ArgsLine("PQRSTUVW", "XYZ[\\]^_", "`abcdefg", "hijklmno", "pqrstuvw", "xyz{|}~") 18 | f.Body([]byte("")) 19 | f.X25519(testkit.TestX25519Recipient) 20 | f.HMAC() 21 | f.Payload("age") 22 | f.Generate() 23 | } 24 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/stream_bad_tag.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Recipient) 16 | f.HMAC() 17 | f.Payload("age") 18 | file := f.Buf.Bytes() 19 | f.Buf.Reset() 20 | file[len(file)-1] ^= 0b0010_0000 21 | f.Buf.Write(file) 22 | f.ExpectPayloadFailure() 23 | f.Generate() 24 | } 25 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/stream_bad_tag_second_chunk.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.FileKey(testkit.LargeTestFileKey) 15 | f.VersionLine("v1") 16 | f.X25519(testkit.TestX25519Identity) 17 | f.HMAC() 18 | f.Nonce(testkit.LargeTestNonce) 19 | f.PayloadChunk(testkit.LargeTestFirstChunk) 20 | f.PayloadChunkFinal([]byte("age")) 21 | file := f.Buf.Bytes() 22 | f.Buf.Reset() 23 | file[len(file)-1] ^= 0b0010_0000 24 | f.Buf.Write(file) 25 | f.ExpectPartialPayload(64 * 1024) 26 | f.Generate() 27 | } 28 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/stream_bad_tag_second_chunk_full.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.FileKey(testkit.LargeTestFileKey) 15 | f.VersionLine("v1") 16 | f.X25519(testkit.TestX25519Identity) 17 | f.HMAC() 18 | f.Nonce(testkit.LargeTestNonce) 19 | f.PayloadChunk(testkit.LargeTestFirstChunk) 20 | f.PayloadChunkFinal(testkit.LargeTestSecondChunk) 21 | file := f.Buf.Bytes() 22 | f.Buf.Reset() 23 | file[len(file)-1] ^= 0b0010_0000 24 | f.Buf.Write(file) 25 | f.ExpectPartialPayload(64 * 1024) 26 | f.Generate() 27 | } 28 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/stream_empty_payload.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Recipient) 16 | f.HMAC() 17 | f.Payload("") 18 | f.Generate() 19 | } 20 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/stream_last_chunk_empty.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.FileKey(testkit.LargeTestFileKey) 15 | f.VersionLine("v1") 16 | f.X25519(testkit.TestX25519Identity) 17 | f.HMAC() 18 | f.Nonce(testkit.LargeTestNonce) 19 | f.PayloadChunk(testkit.LargeTestFirstChunk) 20 | f.PayloadChunkFinal([]byte{}) 21 | f.Comment("final STREAM chunk can't be empty unless whole payload is empty") 22 | f.ExpectPartialPayload(64 * 1024) 23 | f.Generate() 24 | } 25 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/stream_last_chunk_full.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.FileKey(testkit.LargeTestFileKey) 15 | f.VersionLine("v1") 16 | f.X25519(testkit.TestX25519Identity) 17 | f.HMAC() 18 | f.Nonce(testkit.LargeTestNonce) 19 | f.PayloadChunkFinal(testkit.LargeTestFirstChunk) 20 | f.Generate() 21 | } 22 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/stream_last_chunk_full_second.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.FileKey(testkit.LargeTestFileKey) 15 | f.VersionLine("v1") 16 | f.X25519(testkit.TestX25519Identity) 17 | f.HMAC() 18 | f.Nonce(testkit.LargeTestNonce) 19 | f.PayloadChunk(testkit.LargeTestFirstChunk) 20 | f.PayloadChunkFinal(testkit.LargeTestSecondChunk) 21 | f.Generate() 22 | } 23 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/stream_missing_tag.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Recipient) 16 | f.HMAC() 17 | f.Payload("age") 18 | file := f.Buf.Bytes() 19 | f.Buf.Reset() 20 | f.Buf.Write(file[:len(file)-16]) 21 | f.ExpectPayloadFailure() 22 | f.Generate() 23 | } 24 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/stream_no_chunks.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Recipient) 16 | f.HMAC() 17 | f.Nonce(f.Rand(16)) 18 | f.ExpectPayloadFailure() 19 | f.Generate() 20 | } 21 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/stream_no_final.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Recipient) 16 | f.HMAC() 17 | f.Nonce(f.Rand(16)) 18 | f.PayloadChunk([]byte("age")) 19 | f.ExpectPayloadFailure() 20 | f.Generate() 21 | } 22 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/stream_no_final_full.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.FileKey(testkit.LargeTestFileKey) 15 | f.VersionLine("v1") 16 | f.X25519(testkit.TestX25519Identity) 17 | f.HMAC() 18 | f.Nonce(testkit.LargeTestNonce) 19 | f.PayloadChunk(testkit.LargeTestFirstChunk) 20 | f.ExpectPartialPayload(64 * 1024) 21 | f.Generate() 22 | } 23 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/stream_no_final_two_chunks.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.FileKey(testkit.LargeTestFileKey) 15 | f.VersionLine("v1") 16 | f.X25519(testkit.TestX25519Identity) 17 | f.HMAC() 18 | f.Nonce(testkit.LargeTestNonce) 19 | f.PayloadChunk(testkit.LargeTestFirstChunk) 20 | f.PayloadChunk([]byte("age")) 21 | f.ExpectPartialPayload(64 * 1024) 22 | f.Generate() 23 | } 24 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/stream_no_final_two_chunks_full.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.FileKey(testkit.LargeTestFileKey) 15 | f.VersionLine("v1") 16 | f.X25519(testkit.TestX25519Identity) 17 | f.HMAC() 18 | f.Nonce(testkit.LargeTestNonce) 19 | f.PayloadChunk(testkit.LargeTestFirstChunk) 20 | f.PayloadChunk(testkit.LargeTestSecondChunk) 21 | f.ExpectPartialPayload(64 * 1024 * 2) 22 | f.Generate() 23 | } 24 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/stream_no_nonce.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Recipient) 16 | f.HMAC() 17 | // Marked as header failure because we read the nonce while reading the 18 | // header, before handing off to the STREAM implementation. 19 | f.ExpectHeaderFailure() 20 | f.Generate() 21 | } 22 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/stream_short_chunk.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Recipient) 16 | f.HMAC() 17 | f.Nonce(f.Rand(16)) 18 | f.Nonce(f.Rand(12)) // less than the length of a Poly1305 tag 19 | f.ExpectPayloadFailure() 20 | f.Generate() 21 | } 22 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/stream_short_nonce.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Recipient) 16 | f.HMAC() 17 | f.Nonce(f.Rand(12)) 18 | // Marked as header failure because we read the nonce while reading the 19 | // header, before handing off to the STREAM implementation. 20 | f.ExpectHeaderFailure() 21 | f.Generate() 22 | } 23 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/stream_short_second_chunk.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.FileKey(testkit.LargeTestFileKey) 15 | f.VersionLine("v1") 16 | f.X25519(testkit.TestX25519Identity) 17 | f.HMAC() 18 | f.Nonce(testkit.LargeTestNonce) 19 | f.PayloadChunk(testkit.LargeTestFirstChunk) 20 | f.Nonce(f.Rand(12)) // less than the length of a Poly1305 tag 21 | f.ExpectPartialPayload(64 * 1024) 22 | f.Generate() 23 | } 24 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/stream_three_chunks.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.FileKey(testkit.LargeTestFileKey) 15 | f.VersionLine("v1") 16 | f.X25519(testkit.TestX25519Identity) 17 | f.HMAC() 18 | f.Nonce(testkit.LargeTestNonce) 19 | f.PayloadChunk(testkit.LargeTestFirstChunk) 20 | f.PayloadChunk(testkit.LargeTestSecondChunk) 21 | f.PayloadChunkFinal(testkit.LargeTestThirdChunk) 22 | f.Generate() 23 | } 24 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/stream_trailing_garbage_long.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.FileKey(testkit.LargeTestFileKey) 15 | f.VersionLine("v1") 16 | f.X25519(testkit.TestX25519Identity) 17 | f.HMAC() 18 | f.Nonce(testkit.LargeTestNonce) 19 | f.PayloadChunkFinal(testkit.LargeTestFirstChunk) 20 | f.Buf.Write(f.Rand(20)) 21 | f.ExpectPartialPayload(64 * 1024) 22 | f.Generate() 23 | } 24 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/stream_trailing_garbage_short.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.FileKey(testkit.LargeTestFileKey) 15 | f.VersionLine("v1") 16 | f.X25519(testkit.TestX25519Identity) 17 | f.HMAC() 18 | f.Nonce(testkit.LargeTestNonce) 19 | f.PayloadChunkFinal(testkit.LargeTestFirstChunk) 20 | f.Buf.Write(f.Rand(12)) 21 | f.ExpectPartialPayload(64 * 1024) 22 | f.Generate() 23 | } 24 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/stream_two_chunks.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.FileKey(testkit.LargeTestFileKey) 15 | f.VersionLine("v1") 16 | f.X25519(testkit.TestX25519Identity) 17 | f.HMAC() 18 | f.Nonce(testkit.LargeTestNonce) 19 | f.PayloadChunk(testkit.LargeTestFirstChunk) 20 | f.PayloadChunkFinal(testkit.LargeTestSecondChunk) 21 | f.Generate() 22 | } 23 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/stream_two_final_chunks.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.FileKey(testkit.LargeTestFileKey) 15 | f.VersionLine("v1") 16 | f.X25519(testkit.TestX25519Identity) 17 | f.HMAC() 18 | f.Nonce(testkit.LargeTestNonce) 19 | f.PayloadChunkFinal(testkit.LargeTestFirstChunk) 20 | f.PayloadChunkFinal([]byte("age")) 21 | f.ExpectPartialPayload(64 * 1024) 22 | f.Generate() 23 | } 24 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/version_unsupported.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1234") 15 | f.X25519(testkit.TestX25519Recipient) 16 | f.HMAC() 17 | f.Payload("age") 18 | f.ExpectHeaderFailure() 19 | f.Generate() 20 | } 21 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/x25519.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Recipient) 16 | f.HMAC() 17 | f.Payload("age") 18 | f.Generate() 19 | } 20 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/x25519_bad_tag.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import ( 11 | "encoding/base64" 12 | 13 | "c2sp.org/CCTV/age/internal/testkit" 14 | ) 15 | 16 | func main() { 17 | f := testkit.NewTestFile() 18 | f.VersionLine("v1") 19 | f.X25519(testkit.TestX25519Recipient) 20 | body, _ := base64.RawStdEncoding.DecodeString(f.UnreadLine()) 21 | body[len(body)-1] ^= 0xff 22 | f.TextLine(base64.RawStdEncoding.EncodeToString(body)) 23 | f.HMAC() 24 | f.Payload("age") 25 | f.ExpectNoMatch() 26 | f.Comment("the ChaCha20Poly1305 authentication tag on the body of the X25519 stanza is wrong") 27 | f.Generate() 28 | } 29 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/x25519_extra_argument.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Recipient) 16 | body, args := f.UnreadLine(), f.UnreadLine() 17 | f.TextLine(args + " 1234") 18 | f.TextLine(body) 19 | f.HMAC() 20 | f.Payload("age") 21 | f.ExpectHeaderFailure() 22 | f.Comment("the base64 encoding of the share is not canonical") 23 | f.Generate() 24 | } 25 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/x25519_grease.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.ArgsLine("grease") 16 | f.Body(nil) 17 | f.X25519(testkit.TestX25519Recipient) 18 | f.ArgsLine("grease") 19 | f.Body(nil) 20 | f.HMAC() 21 | f.Payload("age") 22 | f.Generate() 23 | } 24 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/x25519_identity.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519RecordIdentity(testkit.TestX25519Identity) 16 | share := make([]byte, 32) 17 | f.X25519Stanza(share, testkit.TestX25519Identity) 18 | f.HMAC() 19 | f.Payload("age") 20 | f.ExpectHeaderFailure() 21 | f.Comment("the X25519 share is a low-order point, so the shared secret is the disallowed all-zero value") 22 | f.Generate() 23 | } 24 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/x25519_long_file_key.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.FileKey([]byte("A LONGER YELLOW SUBMARINE")) 15 | f.VersionLine("v1") 16 | f.X25519(testkit.TestX25519Identity) 17 | f.HMAC() 18 | f.Payload("age") 19 | f.ExpectHeaderFailure() 20 | f.Comment("the file key must be checked to be 16 bytes before decrypting it") 21 | f.Generate() 22 | } 23 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/x25519_long_share.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import ( 11 | "encoding/base64" 12 | 13 | "c2sp.org/CCTV/age/internal/testkit" 14 | "golang.org/x/crypto/curve25519" 15 | ) 16 | 17 | func main() { 18 | f := testkit.NewTestFile() 19 | f.VersionLine("v1") 20 | share, _ := curve25519.X25519(f.Rand(32), curve25519.Basepoint) 21 | f.X25519RecordIdentity(testkit.TestX25519Identity) 22 | f.X25519Stanza(share, testkit.TestX25519Identity) 23 | body, _ := f.UnreadLine(), f.UnreadLine() 24 | f.TextLine("-> X25519 " + base64.RawStdEncoding.EncodeToString(append(share, 0x00))) 25 | f.TextLine(body) 26 | f.HMAC() 27 | f.Payload("age") 28 | f.ExpectHeaderFailure() 29 | f.Comment("a trailing zero is missing from the X25519 share") 30 | f.Generate() 31 | } 32 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/x25519_low_order.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519RecordIdentity(testkit.TestX25519Identity) 16 | // Point of order 8 on Curve25519, chosen to be the least likely to be 17 | // flagged by hardcoded list exclusions. 18 | share := []byte{0x5f, 0x9c, 0x95, 0xbc, 0xa3, 0x50, 0x8c, 0x24, 0xb1, 0xd0, 19 | 0xb1, 0x55, 0x9c, 0x83, 0xef, 0x5b, 0x04, 0x44, 0x5c, 0xc4, 0x58, 0x1c, 20 | 0x8e, 0x86, 0xd8, 0x22, 0x4e, 0xdd, 0xd0, 0x9f, 0x11, 0xd7} 21 | f.X25519Stanza(share, testkit.TestX25519Identity) 22 | f.HMAC() 23 | f.Payload("age") 24 | f.ExpectHeaderFailure() 25 | f.Comment("the X25519 share is a low-order point, so the shared secret" + 26 | "is the disallowed all-zero value") 27 | f.Generate() 28 | } 29 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/x25519_lowercase.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Recipient) 16 | body, args := f.UnreadLine(), f.UnreadArgsLine() 17 | f.ArgsLine("x25519", args[1]) 18 | f.TextLine(body) 19 | f.HMAC() 20 | f.Payload("age") 21 | f.ExpectNoMatch() 22 | f.Comment("the first argument in the X25519 stanza is lowercase") 23 | f.Generate() 24 | } 25 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/x25519_multiple_recipients.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519NoRecordIdentity(f.Rand(32)) 16 | f.X25519(testkit.TestX25519Recipient) 17 | f.HMAC() 18 | f.Payload("age") 19 | f.Generate() 20 | } 21 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/x25519_no_match.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | identity := f.Rand(32) 16 | f.X25519RecordIdentity(identity) 17 | f.X25519NoRecordIdentity(testkit.TestX25519Recipient) 18 | f.HMAC() 19 | f.Payload("age") 20 | f.ExpectNoMatch() 21 | f.Generate() 22 | } 23 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/x25519_not_canonical_body.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Recipient) 16 | body := f.UnreadLine() 17 | f.TextLine(testkit.NotCanonicalBase64(body)) 18 | f.HMAC() 19 | f.Payload("age") 20 | f.ExpectHeaderFailure() 21 | f.Comment("the base64 encoding of the share is not canonical") 22 | f.Generate() 23 | } 24 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/x25519_not_canonical_share.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import "c2sp.org/CCTV/age/internal/testkit" 11 | 12 | func main() { 13 | f := testkit.NewTestFile() 14 | f.VersionLine("v1") 15 | f.X25519(testkit.TestX25519Recipient) 16 | body, args := f.UnreadLine(), f.UnreadLine() 17 | f.TextLine(testkit.NotCanonicalBase64(args)) 18 | f.TextLine(body) 19 | f.HMAC() 20 | f.Payload("age") 21 | f.ExpectHeaderFailure() 22 | f.Comment("the base64 encoding of the share is not canonical") 23 | f.Generate() 24 | } 25 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/internal/tests/x25519_short_share.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The age Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import ( 11 | "encoding/base64" 12 | "encoding/hex" 13 | 14 | "c2sp.org/CCTV/age/internal/testkit" 15 | ) 16 | 17 | func main() { 18 | f := testkit.NewTestFile() 19 | f.VersionLine("v1") 20 | share, _ := hex.DecodeString("97ba38a135fd5f9137fca3836bfec24340ab03d7ca316b26f482636334a52600") 21 | f.X25519RecordIdentity(testkit.TestX25519Identity) 22 | f.X25519Stanza(share, testkit.TestX25519Identity) 23 | body, _ := f.UnreadLine(), f.UnreadLine() 24 | f.TextLine("-> X25519 " + base64.RawStdEncoding.EncodeToString(share[:31])) 25 | f.TextLine(body) 26 | f.HMAC() 27 | f.Payload("age") 28 | f.ExpectHeaderFailure() 29 | f.Comment("a trailing zero is missing from the X25519 share") 30 | f.Generate() 31 | } 32 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/armor: -------------------------------------------------------------------------------- 1 | expect: success 2 | payload: 013f54400c82da08037759ada907a8b864e97de81c088a182062c4b5622fd2ab 3 | file key: 59454c4c4f57205355424d4152494e45 4 | identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 5 | armored: yes 6 | 7 | -----BEGIN AGE ENCRYPTED FILE----- 8 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY 9 | TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW 10 | K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz 11 | ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS 12 | yPC8DpksHoMx+2Y= 13 | -----END AGE ENCRYPTED FILE----- 14 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/armor_crlf: -------------------------------------------------------------------------------- 1 | expect: success 2 | payload: 013f54400c82da08037759ada907a8b864e97de81c088a182062c4b5622fd2ab 3 | file key: 59454c4c4f57205355424d4152494e45 4 | identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 5 | armored: yes 6 | comment: CRLF is allowed as a end of line for armored files 7 | 8 | -----BEGIN AGE ENCRYPTED FILE----- 9 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY 10 | TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW 11 | K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz 12 | ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS 13 | yPC8DpksHoMx+2Y= 14 | -----END AGE ENCRYPTED FILE----- 15 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/armor_empty_line_begin: -------------------------------------------------------------------------------- 1 | expect: armor failure 2 | file key: 59454c4c4f57205355424d4152494e45 3 | identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 4 | armored: yes 5 | 6 | -----BEGIN AGE ENCRYPTED FILE----- 7 | 8 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY 9 | TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW 10 | K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz 11 | ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS 12 | yPC8DpksHoMx+2Y= 13 | -----END AGE ENCRYPTED FILE----- 14 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/armor_empty_line_end: -------------------------------------------------------------------------------- 1 | expect: armor failure 2 | file key: 59454c4c4f57205355424d4152494e45 3 | identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 4 | armored: yes 5 | 6 | -----BEGIN AGE ENCRYPTED FILE----- 7 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY 8 | TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW 9 | K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz 10 | ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS 11 | yPC8DpksHoMx+2Y= 12 | 13 | -----END AGE ENCRYPTED FILE----- 14 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/armor_eol_between_padding: -------------------------------------------------------------------------------- 1 | expect: armor failure 2 | file key: 59454c4c4f57205355424d4152494e45 3 | identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 4 | armored: yes 5 | 6 | -----BEGIN AGE ENCRYPTED FILE----- 7 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY 8 | TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW 9 | K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz 10 | ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW2ewwwqo 11 | mNlxYv6gMOKyDNzgiw= 12 | = 13 | -----END AGE ENCRYPTED FILE----- 14 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/armor_full_last_line: -------------------------------------------------------------------------------- 1 | expect: success 2 | payload: 724a112a2cac139a4fca3ea0f799f2e5ccd1d0db46af654dee40567bff16ee33 3 | file key: 59454c4c4f57205355424d4152494e45 4 | identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 5 | armored: yes 6 | 7 | -----BEGIN AGE ENCRYPTED FILE----- 8 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY 9 | TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW 10 | K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz 11 | ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW3bj4iHS 12 | YS3WWUtZB5wJqKgEe8kpsp0iOnD2CNG4DVKBC0Z7SAcCFb8xdwV9CRavSEE7OU1c 13 | -----END AGE ENCRYPTED FILE----- 14 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/armor_garbage_leading: -------------------------------------------------------------------------------- 1 | expect: armor failure 2 | file key: 59454c4c4f57205355424d4152494e45 3 | identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 4 | armored: yes 5 | 6 | garbage 7 | -----BEGIN AGE ENCRYPTED FILE----- 8 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY 9 | TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW 10 | K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz 11 | ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS 12 | yPC8DpksHoMx+2Y= 13 | -----END AGE ENCRYPTED FILE----- 14 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/armor_garbage_trailing: -------------------------------------------------------------------------------- 1 | expect: armor failure 2 | file key: 59454c4c4f57205355424d4152494e45 3 | identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 4 | armored: yes 5 | 6 | -----BEGIN AGE ENCRYPTED FILE----- 7 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY 8 | TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW 9 | K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz 10 | ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS 11 | yPC8DpksHoMx+2Y= 12 | -----END AGE ENCRYPTED FILE----- 13 | garbage 14 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/armor_header_crlf: -------------------------------------------------------------------------------- 1 | expect: header failure 2 | file key: 59454c4c4f57205355424d4152494e45 3 | identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0 4 | armored: yes 5 | comment: lines in the header end with CRLF instead of LF 6 | 7 | -----BEGIN AGE ENCRYPTED FILE----- 8 | YWdlLWVuY3J5cHRpb24ub3JnL3YxDQotPiBYMjU1MTkgVEVpRjB5cHFyK2JwdmNx 9 | WE55Q1ZKcEw3T3V3UGRWd1BMN0tRRWJGRE9DYw0KaGphYkdYd1NMUTljM1M2THcy 10 | aStTMlR1MmZpd1FISHNsYkJONkI0MUZMRQ0KLS0tIDJLSUdiN3llMzJNV3RVdUVW 11 | V2tPM01QNnFDREx6T3ZUOXdGMDZsZWxCU0kNCu7PYsfOkbQzJ05o1PL5E0y3TFv+ 12 | 976qUsjwvA6ZLB6DMftm 13 | -----END AGE ENCRYPTED FILE----- 14 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/armor_headers: -------------------------------------------------------------------------------- 1 | expect: armor failure 2 | file key: 59454c4c4f57205355424d4152494e45 3 | identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 4 | armored: yes 5 | 6 | -----BEGIN AGE ENCRYPTED FILE----- 7 | Headers: are 8 | Not: allowed 9 | 10 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY 11 | TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW 12 | K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz 13 | ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS 14 | yPC8DpksHoMx+2Y= 15 | -----END AGE ENCRYPTED FILE----- 16 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/armor_invalid_character_header: -------------------------------------------------------------------------------- 1 | expect: armor failure 2 | file key: 59454c4c4f57205355424d4152494e45 3 | identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 4 | armored: yes 5 | 6 | -----BEGIN AGE ENCRYPTED FILE----- 7 | YWdl*WVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY 8 | TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW 9 | K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz 10 | ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS 11 | yPC8DpksHoMx+2Y= 12 | -----END AGE ENCRYPTED FILE----- 13 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/armor_invalid_character_payload: -------------------------------------------------------------------------------- 1 | expect: armor failure 2 | file key: 59454c4c4f57205355424d4152494e45 3 | identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 4 | armored: yes 5 | 6 | -----BEGIN AGE ENCRYPTED FILE----- 7 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY 8 | TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW 9 | K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz 10 | ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS 11 | *PC8DpksHoMx+2Y= 12 | -----END AGE ENCRYPTED FILE----- 13 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/armor_long_line: -------------------------------------------------------------------------------- 1 | expect: armor failure 2 | file key: 59454c4c4f57205355424d4152494e45 3 | identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 4 | armored: yes 5 | 6 | -----BEGIN AGE ENCRYPTED FILE----- 7 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FYTnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lWK0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkzZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpSyPC8DpksHoMx+2Y= 8 | -----END AGE ENCRYPTED FILE----- 9 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/armor_lowercase: -------------------------------------------------------------------------------- 1 | expect: armor failure 2 | file key: 59454c4c4f57205355424d4152494e45 3 | identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 4 | armored: yes 5 | 6 | -----BEGIN age ENCRYPTED FILE----- 7 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY 8 | TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW 9 | K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz 10 | ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS 11 | yPC8DpksHoMx+2Y= 12 | -----END age ENCRYPTED FILE----- 13 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/armor_no_end_line: -------------------------------------------------------------------------------- 1 | expect: armor failure 2 | file key: 59454c4c4f57205355424d4152494e45 3 | identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 4 | armored: yes 5 | 6 | -----BEGIN AGE ENCRYPTED FILE----- 7 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY 8 | TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW 9 | K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz 10 | ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS 11 | yPC8DpksHoMx+2Y= 12 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/armor_no_eol: -------------------------------------------------------------------------------- 1 | expect: success 2 | payload: 013f54400c82da08037759ada907a8b864e97de81c088a182062c4b5622fd2ab 3 | file key: 59454c4c4f57205355424d4152494e45 4 | identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 5 | armored: yes 6 | comment: there is no end of line at the end of the file 7 | 8 | -----BEGIN AGE ENCRYPTED FILE----- 9 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY 10 | TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW 11 | K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz 12 | ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS 13 | yPC8DpksHoMx+2Y= 14 | -----END AGE ENCRYPTED FILE----- -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/armor_no_match: -------------------------------------------------------------------------------- 1 | expect: no match 2 | file key: 59454c4c4f57205355424d4152494e45 3 | identity: AGE-SECRET-KEY-143WN7DCXU4G8R5AXQSSYD9AEPYDNT3HXSLWSPK36CDU6E8M59SSSAGZ3KG 4 | armored: yes 5 | 6 | -----BEGIN AGE ENCRYPTED FILE----- 7 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBhanRxQXZERWtWTnIyQjd6 8 | VU90cTJtQVFYRFNCbE5yVkF1TS9kS2I1c1Q0CkhVS3R6MFIyajVCbDJFUjdIaEFa 9 | clVSaWtDRnBpSWpOYTBLakhjamJBR1UKLS0tIHJycFRsdktFS3JLM0VxaG9PUEpl 10 | UDFLRThPMWQyYXJyUmV6Nzdtd2VrUmMK3d9y0G+8q1ffPQ0xJJatIYzX/W+AeLv4 11 | gS3YeUcVXre9Xog= 12 | -----END AGE ENCRYPTED FILE----- 13 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/armor_no_padding: -------------------------------------------------------------------------------- 1 | expect: armor failure 2 | file key: 59454c4c4f57205355424d4152494e45 3 | identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 4 | armored: yes 5 | comment: missing base64 padding 6 | 7 | -----BEGIN AGE ENCRYPTED FILE----- 8 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY 9 | TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW 10 | K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz 11 | ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS 12 | yPC8DpksHoMx+2Y 13 | -----END AGE ENCRYPTED FILE----- 14 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/armor_not_canonical: -------------------------------------------------------------------------------- 1 | expect: armor failure 2 | file key: 59454c4c4f57205355424d4152494e45 3 | identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 4 | armored: yes 5 | comment: base64 is not canonical 6 | 7 | -----BEGIN AGE ENCRYPTED FILE----- 8 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY 9 | TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW 10 | K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz 11 | ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS 12 | yPC8DpksHoMx+2Z= 13 | -----END AGE ENCRYPTED FILE----- 14 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/armor_pgp_checksum: -------------------------------------------------------------------------------- 1 | expect: armor failure 2 | file key: 59454c4c4f57205355424d4152494e45 3 | identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 4 | armored: yes 5 | 6 | -----BEGIN AGE ENCRYPTED FILE----- 7 | 8 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY 9 | TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW 10 | K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz 11 | ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS 12 | yPC8DpksHoMx+2Y= 13 | =J2ub 14 | -----END AGE ENCRYPTED FILE----- 15 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/armor_short_line: -------------------------------------------------------------------------------- 1 | expect: armor failure 2 | file key: 59454c4c4f57205355424d4152494e45 3 | identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 4 | armored: yes 5 | 6 | -----BEGIN AGE ENCRYPTED FILE----- 7 | YWdlLWVuY3J5cHRp 8 | b24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FYTnlDVkpwTDdPdXdQ 9 | ZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lWK0h1MHIrRThSNzdE 10 | ZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkzZjFzcUhqbHUvejFM 11 | Q1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpSyPC8DpksHoMx+2Y= 12 | -----END AGE ENCRYPTED FILE----- 13 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/armor_whitespace_begin: -------------------------------------------------------------------------------- 1 | expect: armor failure 2 | file key: 59454c4c4f57205355424d4152494e45 3 | identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 4 | armored: yes 5 | 6 | ----- BEGIN AGE ENCRYPTED FILE ----- 7 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY 8 | TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW 9 | K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz 10 | ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS 11 | yPC8DpksHoMx+2Y= 12 | -----END AGE ENCRYPTED FILE----- 13 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/armor_whitespace_end: -------------------------------------------------------------------------------- 1 | expect: armor failure 2 | file key: 59454c4c4f57205355424d4152494e45 3 | identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 4 | armored: yes 5 | 6 | -----BEGIN AGE ENCRYPTED FILE----- 7 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY 8 | TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW 9 | K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz 10 | ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS 11 | yPC8DpksHoMx+2Y= 12 | ----- END AGE ENCRYPTED FILE ----- 13 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/armor_whitespace_eol: -------------------------------------------------------------------------------- 1 | expect: armor failure 2 | file key: 59454c4c4f57205355424d4152494e45 3 | identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 4 | armored: yes 5 | 6 | -----BEGIN AGE ENCRYPTED FILE----- 7 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY 8 | TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW 9 | K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz 10 | ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS 11 | yPC8DpksHoMx+2Y= 12 | -----END AGE ENCRYPTED FILE----- 13 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/armor_whitespace_last_line: -------------------------------------------------------------------------------- 1 | expect: armor failure 2 | file key: 59454c4c4f57205355424d4152494e45 3 | identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 4 | armored: yes 5 | 6 | -----BEGIN AGE ENCRYPTED FILE----- 7 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY 8 | TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW 9 | K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz 10 | ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS 11 | yPC8DpksHoMx+2Y= 12 | -----END AGE ENCRYPTED FILE----- 13 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/armor_whitespace_line_start: -------------------------------------------------------------------------------- 1 | expect: armor failure 2 | file key: 59454c4c4f57205355424d4152494e45 3 | identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 4 | armored: yes 5 | 6 | -----BEGIN AGE ENCRYPTED FILE----- 7 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY 8 | TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW 9 | K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz 10 | ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS 11 | yPC8DpksHoMx+2Y= 12 | -----END AGE ENCRYPTED FILE----- 13 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/armor_whitespace_outside: -------------------------------------------------------------------------------- 1 | expect: success 2 | payload: 013f54400c82da08037759ada907a8b864e97de81c088a182062c4b5622fd2ab 3 | file key: 59454c4c4f57205355424d4152494e45 4 | identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 5 | armored: yes 6 | comment: whitespace is allowed before and after armored files 7 | 8 | 9 | 10 | -----BEGIN AGE ENCRYPTED FILE----- 11 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY 12 | TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW 13 | K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz 14 | ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS 15 | yPC8DpksHoMx+2Y= 16 | -----END AGE ENCRYPTED FILE----- 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/armor_wrong_type: -------------------------------------------------------------------------------- 1 | expect: armor failure 2 | file key: 59454c4c4f57205355424d4152494e45 3 | identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 4 | armored: yes 5 | 6 | -----BEGIN AGE ENCRYPTED MESSAGE----- 7 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBURWlGMHlwcXIrYnB2Y3FY 8 | TnlDVkpwTDdPdXdQZFZ3UEw3S1FFYkZET0NjCkVtRUNBRWNLTituL1ZzOVNiV2lW 9 | K0h1MHIrRThSNzdEZFdZeWQ4M253N1UKLS0tIFZuKzU0anFpaVVDRStXWmNFVlkz 10 | ZjFzcUhqbHUvejFMQ1EvVDdYbTdxSTAK7s9ix86RtDMnTmjU8vkTTLdMW/73vqpS 11 | yPC8DpksHoMx+2Y= 12 | -----END AGE ENCRYPTED MESSAGE----- 13 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/header_crlf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/header_crlf -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/hmac_bad: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/hmac_bad -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/hmac_extra_space: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/hmac_extra_space -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/hmac_garbage: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/hmac_garbage -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/hmac_missing: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/hmac_missing -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/hmac_no_space: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/hmac_no_space -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/hmac_not_canonical: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/hmac_not_canonical -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/hmac_trailing_space: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/hmac_trailing_space -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/hmac_truncated: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/hmac_truncated -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/scrypt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/scrypt -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/scrypt_and_x25519: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/scrypt_and_x25519 -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/scrypt_bad_tag: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/scrypt_bad_tag -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/scrypt_double: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/scrypt_double -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/scrypt_extra_argument: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/scrypt_extra_argument -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/scrypt_long_file_key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/scrypt_long_file_key -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/scrypt_no_match: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/scrypt_no_match -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/scrypt_not_canonical_body: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/scrypt_not_canonical_body -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/scrypt_not_canonical_salt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/scrypt_not_canonical_salt -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/scrypt_salt_long: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/scrypt_salt_long -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/scrypt_salt_missing: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/scrypt_salt_missing -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/scrypt_salt_short: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/scrypt_salt_short -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/scrypt_uppercase: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/scrypt_uppercase -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/scrypt_work_factor_23: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/scrypt_work_factor_23 -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/scrypt_work_factor_hex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/scrypt_work_factor_hex -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/scrypt_work_factor_leading_garbage: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/scrypt_work_factor_leading_garbage -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/scrypt_work_factor_leading_plus: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/scrypt_work_factor_leading_plus -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/scrypt_work_factor_leading_zero_decimal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/scrypt_work_factor_leading_zero_decimal -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/scrypt_work_factor_leading_zero_octal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/scrypt_work_factor_leading_zero_octal -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/scrypt_work_factor_missing: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/scrypt_work_factor_missing -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/scrypt_work_factor_negative: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/scrypt_work_factor_negative -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/scrypt_work_factor_overflow: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/scrypt_work_factor_overflow -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/scrypt_work_factor_trailing_garbage: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/scrypt_work_factor_trailing_garbage -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/scrypt_work_factor_wrong: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/scrypt_work_factor_wrong -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/scrypt_work_factor_zero: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/scrypt_work_factor_zero -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/stanza_bad_start: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/stanza_bad_start -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/stanza_base64_padding: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/stanza_base64_padding -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/stanza_empty_argument: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/stanza_empty_argument -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/stanza_empty_body: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/stanza_empty_body -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/stanza_empty_last_line: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/stanza_empty_last_line -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/stanza_invalid_character: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/stanza_invalid_character -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/stanza_long_line: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/stanza_long_line -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/stanza_missing_body: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/stanza_missing_body -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/stanza_missing_final_line: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/stanza_missing_final_line -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/stanza_multiple_short_lines: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/stanza_multiple_short_lines -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/stanza_no_arguments: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/stanza_no_arguments -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/stanza_not_canonical: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/stanza_not_canonical -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/stanza_spurious_cr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/stanza_spurious_cr -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/stanza_valid_characters: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/stanza_valid_characters -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/stream_bad_tag: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/stream_bad_tag -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/stream_bad_tag_second_chunk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/stream_bad_tag_second_chunk -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/stream_bad_tag_second_chunk_full: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/stream_bad_tag_second_chunk_full -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/stream_empty_payload: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/stream_empty_payload -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/stream_last_chunk_empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/stream_last_chunk_empty -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/stream_last_chunk_full: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/stream_last_chunk_full -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/stream_last_chunk_full_second: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/stream_last_chunk_full_second -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/stream_missing_tag: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/stream_missing_tag -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/stream_no_chunks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/stream_no_chunks -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/stream_no_final: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/stream_no_final -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/stream_no_final_full: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/stream_no_final_full -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/stream_no_final_two_chunks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/stream_no_final_two_chunks -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/stream_no_final_two_chunks_full: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/stream_no_final_two_chunks_full -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/stream_no_nonce: -------------------------------------------------------------------------------- 1 | expect: header failure 2 | file key: 59454c4c4f57205355424d4152494e45 3 | identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 4 | 5 | age-encryption.org/v1 6 | -> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc 7 | EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U 8 | --- Vn+54jqiiUCE+WZcEVY3f1sqHjlu/z1LCQ/T7Xm7qI0 9 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/stream_short_chunk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/stream_short_chunk -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/stream_short_nonce: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/stream_short_nonce -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/stream_short_second_chunk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/stream_short_second_chunk -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/stream_three_chunks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/stream_three_chunks -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/stream_trailing_garbage_long: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/stream_trailing_garbage_long -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/stream_trailing_garbage_short: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/stream_trailing_garbage_short -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/stream_two_chunks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/stream_two_chunks -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/stream_two_final_chunks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/stream_two_final_chunks -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/version_unsupported: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/version_unsupported -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/x25519: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/x25519 -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/x25519_bad_tag: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/x25519_bad_tag -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/x25519_extra_argument: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/x25519_extra_argument -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/x25519_grease: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/x25519_grease -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/x25519_identity: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/x25519_identity -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/x25519_long_file_key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/x25519_long_file_key -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/x25519_long_share: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/x25519_long_share -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/x25519_low_order: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/x25519_low_order -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/x25519_lowercase: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/x25519_lowercase -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/x25519_multiple_recipients: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/x25519_multiple_recipients -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/x25519_no_match: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/x25519_no_match -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/x25519_not_canonical_body: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/x25519_not_canonical_body -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/x25519_not_canonical_share: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/x25519_not_canonical_share -------------------------------------------------------------------------------- /src/test/resources/CCTV/age/testdata/x25519_short_share: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/android-password-store/kage/2fb510efb338381a89fdf115c17ff31afddd4dae/src/test/resources/CCTV/age/testdata/x25519_short_share -------------------------------------------------------------------------------- /src/test/resources/CCTV/ed25519vectors/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019 Google LLC 2 | Copyright 2022 Filippo Valsorda 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above 11 | copyright notice, this list of conditions and the following disclaimer 12 | in the documentation and/or other materials provided with the 13 | distribution. 14 | * Neither the name of Google LLC nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/ed25519vectors/go.mod: -------------------------------------------------------------------------------- 1 | module filippo.io/mostly-harmless/ed25519vectors 2 | 3 | go 1.19 4 | 5 | require ( 6 | filippo.io/edwards25519 v1.0.0-beta.3 7 | github.com/hdevalence/ed25519consensus v0.0.0-20210204194344-59a8610d2b87 8 | ) 9 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/ed25519vectors/go.sum: -------------------------------------------------------------------------------- 1 | filippo.io/edwards25519 v1.0.0-beta.2/go.mod h1:X+pm78QAUPtFLi1z9PYIlS/bdDnvbCOGKtZ+ACWEf7o= 2 | filippo.io/edwards25519 v1.0.0-beta.3 h1:WQxB0FH5NzrhciInJ30bgL3soLng3AbdI651yQuVlCs= 3 | filippo.io/edwards25519 v1.0.0-beta.3/go.mod h1:X+pm78QAUPtFLi1z9PYIlS/bdDnvbCOGKtZ+ACWEf7o= 4 | github.com/hdevalence/ed25519consensus v0.0.0-20210204194344-59a8610d2b87 h1:uUjLpLt6bVvZ72SQc/B4dXcPBw4Vgd7soowdRl52qEM= 5 | github.com/hdevalence/ed25519consensus v0.0.0-20210204194344-59a8610d2b87/go.mod h1:XGsKKeXxeRr95aEOgipvluMPlgjr7dGlk9ZTWOjcUcg= 6 | -------------------------------------------------------------------------------- /src/test/resources/CCTV/ed25519vectors/reencoded25519.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Filippo Valsorda 2 | // 3 | // Use of this source code is governed by a BSD-style 4 | // license that can be found in the LICENSE file or at 5 | // https://developers.google.com/open-source/licenses/bsd 6 | 7 | package main 8 | 9 | import ( 10 | "crypto/sha512" 11 | 12 | "filippo.io/edwards25519" 13 | ) 14 | 15 | // ReencodedVerify is an Ed25519 verifier that accepts non-canonical R and A 16 | // values, but then hashes the canonical versions into k. 17 | // 18 | // This verifier would appear to not have malleability issues if the test 19 | // vectors don't target its behavior. 20 | func ReencodedVerify(publicKey, message, sig []byte) bool { 21 | A, err := new(edwards25519.Point).SetBytes(publicKey) 22 | if err != nil { 23 | return false 24 | } 25 | R, err := new(edwards25519.Point).SetBytes(sig[:32]) 26 | if err != nil { 27 | return false 28 | } 29 | S, err := new(edwards25519.Scalar).SetCanonicalBytes(sig[32:]) 30 | if err != nil { 31 | return false 32 | } 33 | 34 | kh := sha512.New() 35 | kh.Write(R.Bytes()) 36 | kh.Write(A.Bytes()) 37 | kh.Write(message) 38 | hramDigest := make([]byte, 0, sha512.Size) 39 | hramDigest = kh.Sum(hramDigest) 40 | k := new(edwards25519.Scalar).SetUniformBytes(hramDigest) 41 | 42 | // [8][S]B = [8]R + [8][k]A --> [8]([k](-A) + [S]B) = [8]R 43 | minusA := new(edwards25519.Point).Negate(A) 44 | RR := new(edwards25519.Point).VarTimeDoubleScalarBaseMult(k, minusA, S) 45 | 46 | RR.MultByCofactor(RR) 47 | R.MultByCofactor(R) 48 | 49 | return RR.Equal(R) == 1 50 | } 51 | --------------------------------------------------------------------------------